#!/bin/bash # Minimal HTTP server using bash and socat/nc # Send HTTP response send_response() { local status="$1" local content_type="${2:-application/json}" local body="$3" local length=${#body} printf "HTTP/1.1 %s\r\n" "$status" printf "Content-Type: %s\r\n" "$content_type" printf "Content-Length: %d\r\n" "$length" printf "Access-Control-Allow-Origin: *\r\n" printf "Access-Control-Allow-Methods: GET, POST, DELETE, OPTIONS\r\n" printf "Access-Control-Allow-Headers: Content-Type\r\n" printf "Connection: close\r\n" printf "\r\n" printf "%s" "$body" } send_json() { send_response "$1" "application/json" "$2" } send_error() { local status="$1" local message="$2" send_json "$status" "{\"error\":\"$message\"}" } # Handle a single HTTP request handle_request() { local request_line="" local content_length=0 local body="" # Read request line read -r request_line request_line=$(echo "$request_line" | tr -d '\r') # Parse method and path local method=$(echo "$request_line" | cut -d' ' -f1) local path=$(echo "$request_line" | cut -d' ' -f2) # Read headers while read -r header; do header=$(echo "$header" | tr -d '\r') [ -z "$header" ] && break # Extract content-length if echo "$header" | grep -qi "^content-length:"; then content_length=$(echo "$header" | cut -d':' -f2 | tr -d ' ') fi done # Read body if present if [ "$content_length" -gt 0 ] 2>/dev/null; then body=$(head -c "$content_length") fi # Route request route_request "$method" "$path" "$body" } # Route requests to handlers route_request() { local method="$1" local path="$2" local body="$3" # Handle CORS preflight if [ "$method" = "OPTIONS" ]; then send_response "204 No Content" "text/plain" "" return fi # API routes case "$path" in /api/machines/register) if [ "$method" = "POST" ]; then handle_register "$body" else send_error "405 Method Not Allowed" "Use POST" fi ;; /api/machines) if [ "$method" = "GET" ]; then handle_list_machines else send_error "405 Method Not Allowed" "Use GET" fi ;; /api/machines/*) local machine_id="${path#/api/machines/}" if [ "$method" = "DELETE" ]; then handle_delete_machine "$machine_id" elif [ "$method" = "GET" ]; then handle_get_machine "$machine_id" else send_error "405 Method Not Allowed" "Use GET or DELETE" fi ;; /health) send_json "200 OK" '{"status":"healthy"}' ;; *) send_error "404 Not Found" "Endpoint not found" ;; esac } # Start HTTP server http_server() { local port="$1" # Check for socat if command -v socat &>/dev/null; then echo "Using socat..." while true; do socat TCP-LISTEN:"$port",reuseaddr,fork EXEC:"$0 --handle-request" done else echo "Error: socat not found. Install with: apt-get install socat" exit 1 fi } # Handle --handle-request flag (called by socat) if [ "$1" = "--handle-request" ]; then SKILL_DIR="$(dirname "$(dirname "$0")")" source "$SKILL_DIR/lib/machines.sh" DATA_DIR="${FLEET_DATA_DIR:-/data/fleet}" export DATA_DIR handle_request fi