#health-check #fast-cgi #http #fcgi

app fastcgi-healthcheck

Fastcgi healthcheck, power by kvarn-fastcgi-client, axum and tokio

1 unstable release

0.2.0 Apr 9, 2024

#327 in HTTP server

GPL-3.0 license

26KB
102 lines

Introduction

Due to the fact that in the container world is a health check quite essential and I haven’t found any simple fastcgi (fcgi) client with make such check's have I decided to create one in rust.

At the current time is the HTTP Protocol widely supported for health checks but not the fastcgi protocol.

The fastcgi-healthcheck tool exists for only one purpose, it calls a fcgi server with a specific URL and expect a specific answer. Based on that answer will the tool return a 200 or a 404 HTTP code to the requester.

Environment Variables

Variable Description Default value
SERVER The Server and Port combination on which the tool should listen 127.0.0.1:8080
DESTINATION The FCGI destination address, this can be a name or ip 127.0.0.1
DESTINATION_PORT The FCGI destination port 9000
PING_PATH The Ping URL on the FCGI Server /fpm-ping
PING_RESPONSE The Ping response from the FCGI Server pong

I use for the FCGI call this library https://github.com/Icelk/fastcgi-client-rs

Own health endpoints

This tool have it's own health endpoints /health and /healthz.

Disclaimer

This is one of my first rust programm I'm pretty sure that this tool could be simplified 😃
I'm open for suggestions.

example debug run

Scenario: PHP-FPM is not running

health check tool running

# shell 1
RUST_LOG=debug PING_PATH=/lala SERVER=127.0.0.1:8080 DESTINATION=127.0.0.1 DESTINATION_PORT=9000 cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.02s
Running `target/debug/fastcgi-healhcheck`
INFO 2024-04-09 11:03:53 UTC: Listening on 127.0.0.1:8080
DEBUG 2024-04-09 11:03:56 UTC: Request Headers {"host": "127.0.0.1:8080", "user-agent": "curl/7.81.0", "accept": "*/*"}
DEBUG 2024-04-09 11:03:56 UTC: Request Request { method: GET, uri: /fcgi-ping, version: HTTP/1.1, headers: {"host": "127.0.0.1:8080", "user-agent": "curl/7.81.0", "accept": "*/*"}, body: Body(UnsyncBoxBody) }
DEBUG 2024-04-09 11:03:56 UTC: Request URI :/fcgi-ping:
DEBUG 2024-04-09 11:03:56 UTC: destination_addr :127.0.0.1:
DEBUG 2024-04-09 11:03:56 UTC: destination_port :9000:
DEBUG 2024-04-09 11:03:56 UTC: ping_path :/lala:
DEBUG 2024-04-09 11:03:56 UTC: ping_response :pong:
ERROR 2024-04-09 11:03:56 UTC: Stream error :Connection refused (os error 111):
thread 'tokio-runtime-worker' panicked at src/main.rs:63:13:
Connection refused (os error 111)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

curl call

# shell 2
curl -v http://127.0.0.1:8080/fcgi-ping*   Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET /fcgi-ping HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.81.0
> Accept: */*
>
* Empty reply from server
* Closing connection 0
curl: (52) Empty reply from server

Scenario: PHP-FPM is running

health check tool running

# shell 1
RUST_LOG=debug SERVER=127.0.0.1:8080 DESTINATION=127.0.0.1 DESTINATION_PORT=9000 cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.02s
Running `target/debug/fastcgi-healhcheck`
INFO 2024-04-09 11:07:44 UTC: Listening on 127.0.0.1:8080
DEBUG 2024-04-09 11:07:45 UTC: Request Headers {"host": "127.0.0.1:8080", "user-agent": "curl/7.81.0", "accept": "*/*"}
DEBUG 2024-04-09 11:07:45 UTC: Request Request { method: GET, uri: /fcgi-ping, version: HTTP/1.1, headers: {"host": "127.0.0.1:8080", "user-agent": "curl/7.81.0", "accept": "*/*"}, body: Body(UnsyncBoxBody) }
DEBUG 2024-04-09 11:07:45 UTC: Request URI :/fcgi-ping:
DEBUG 2024-04-09 11:07:45 UTC: destination_addr :127.0.0.1:
DEBUG 2024-04-09 11:07:45 UTC: destination_port :9000:
DEBUG 2024-04-09 11:07:45 UTC: ping_path :/fpm-ping:
DEBUG 2024-04-09 11:07:45 UTC: ping_response :pong:
DEBUG 2024-04-09 11:07:45 UTC: dest_connection :PollEvented { io: Some(TcpStream { addr: 127.0.0.1:60274, peer: 127.0.0.1:9000, fd: 8 }) }:
DEBUG 2024-04-09 11:07:45 UTC: Start handle request id=1
DEBUG 2024-04-09 11:07:45 UTC: Send to stream. id=1 begin_request_rec="BeginRequestRec {header: Header { version: 1, type: BeginRequest, request_id: 1, content_length: 8, padding_length: 0, reserved: 0 }, begin_request: BeginRequest { role: Responder, flags: 0, reserved: [0, 0, 0, 0, 0] }}"
DEBUG 2024-04-09 11:07:45 UTC: Params will be sent. id=1 param_pairs=ParamPairs([ParamPair { name_length: Short(15), value_length: Short(17), name_data: "SERVER_SOFTWARE", value_data: "fastcgi-client-rs" }, ParamPair { name_length: Short(14), value_length: Short(3), name_data: "REQUEST_METHOD", value_data: "GET" },ParamPair { name_length: Short(11), value_length: Short(9), name_data: "SCRIPT_NAME", value_data: "/fpm-ping" }, ParamPair { name_length: Short(11), value_length: Short(9), name_data: "REQUEST_URI", value_data: "/fpm-ping" }, ParamPair { name_length: Short(12), value_length: Short(9), name_data: "DOCUMENT_URI", value_data: "/fpm-ping" }, ParamPair { name_length: Short(11), value_length: Short(4), name_data: "SERVER_PORT", value_data: "9000" }, ParamPair { name_length: Short(15), value_length: Short(8), name_data: "SERVER_PROTOCOL", value_data: "HTTP/1.1" }, ParamPair { name_length: Short(17), value_length: Short(11), name_data: "GATEWAY_INTERFACE", value_data: "FastCGI/1.0" }, ParamPair { name_length: Short(15), value_length: Short(9), name_data: "SCRIPT_FILENAME", value_data: "/fpm-ping" }, ParamPair { name_length: Short(11), value_length: Short(9), name_data: "SERVER_ADDR", value_data: "127.0.0.1" }])
DEBUG 2024-04-09 11:07:45 UTC: Send to stream for Params. id=1 header=Header { version: 1, type: Params, request_id: 1, content_length: 240, padding_length: 0, reserved: 0 }
DEBUG 2024-04-09 11:07:45 UTC: Send to stream for Params. id=1 header=Header { version: 1, type: Params, request_id: 1, content_length: 0, padding_length: 0, reserved: 0 }
DEBUG 2024-04-09 11:07:45 UTC: Send to stream for Stdin. id=1 header=Header { version: 1, type: Stdin, request_id: 1, content_length: 0, padding_length: 0, reserved: 0 }
DEBUG 2024-04-09 11:07:45 UTC: Send to stream for Stdin. id=1 header=Header { version: 1, type: Stdin, request_id: 1, content_length: 0, padding_length: 0, reserved: 0 }
DEBUG 2024-04-09 11:07:45 UTC: Receive from stream. id=1 header=Header { version: 1, type: Stdout, request_id: 1, content_length: 186, padding_length: 6, reserved: 0 }
DEBUG 2024-04-09 11:07:45 UTC: Receive from stream. id=1 header=Header { version: 1, type: EndRequest, request_id: 1, content_length: 8, padding_length: 0, reserved: 0 }
DEBUG 2024-04-09 11:07:45 UTC: Receive from stream. id=1 end_request_rec=EndRequestRec { header: Header { version: 1, type: EndRequest, request_id: 1, content_length: 8, padding_length: 0, reserved: 0 }, end_request: EndRequest { app_status: 0, protocol_status: RequestComplete, reserved: [0, 0, 0] } }

curl call

# shell 2
curl -v http://127.0.0.1:8080/fcgi-ping
*   Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET /fcgi-ping HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< content-length: 0
< date: Tue, 09 Apr 2024 12:14:38 GMT
<
* Connection #0 to host 127.0.0.1 left intact

Dependencies

~8–17MB
~228K SLoC