#!/bin/bash # slow-server - compatibility launcher for flaky-server's HTTP delay tests # # Usage: # slow-server [port] # slow-server -h # # Options: # -h, --help Show help message # # Dependencies: # flaky-server (sibling or PATH), or curl to fetch it; socat, fractional sleep _slow_server() ( local SCRIPT_NAME; SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")" case "${BASH_SOURCE[0]}" in /dev/*|/proc/*) SCRIPT_NAME="" ;; esac case "$SCRIPT_NAME" in ""|bash|sh|zsh|dash) SCRIPT_NAME="slow-server" ;; esac local MAX_PORT=65535 local MAX_DECIMAL_DIGITS=10 _error() { printf '[ERR][%s] %s\n' "$SCRIPT_NAME" "$*" >&2; } _show_help() { local s="" local r="" if { [ -t 1 ] || [ -n "${CLICOLOR_FORCE:-}" ]; } && [ -z "${NO_COLOR:-}" ]; then s=$'\033[4m' r=$'\033[24m' fi cat < or any otherwise unrecognized path ending in a number return 200 after that many milliseconds, echoing the value and a newline. Non-numeric unknown paths return 404. Uses a sibling flaky-server first, then an executable on PATH, otherwise fetches https://toolio.sh/flaky-server with curl. Help is available offline. Install flaky-server beside this file or on PATH for offline server startup. ARGUMENTS ${s}port${r} Decimal TCP port, 1-65535 (default: 8080) OPTIONS -h, --help Show this help message EXAMPLES $SCRIPT_NAME 9000 curl http://localhost:9000/2500 curl -fsS https://toolio.sh/$SCRIPT_NAME | bash -s -- 9000 flaky-server 9000 curl -i http://localhost:9000/drop/2000 curl -fsS https://toolio.sh/flaky-server | bash -s -- -h COMPATIBILITY Numeric delay values are decimal milliseconds, 0-2147483647. Leading zeroes are decimal; query strings and trailing slashes do not change a legacy delay. Explicit flaky-server behavior paths take precedence over the legacy numeric-segment rule. Run flaky-server -h for the unified tool's complete interface. DEPENDENCIES socat, sleep (fractional seconds), via flaky-server curl (only when flaky-server is unavailable locally) EXIT STATUS 0 Help or clean listener exit 1 Download, listener or runtime failure 2 Invalid arguments 3 Missing dependency CAVEATS Plain HTTP on all IPv4 interfaces. Use the tunnel for HTTPS and verify its buffering and error behavior. This tests HTTP transport, not valid SOAP replies. Use flaky-server for new tests. POST accepts Content-Length bodies up to 1 MiB and Expect: 100-continue; chunked requests return 501. HEAD and status 204/205/304 have no body. Reads have a 10-second timeout; request/header lines are limited to 8192 bytes and headers to 100 lines. Invalid framing closes the connection with a diagnostic. Startup and request diagnostics go to stderr. Stop with Ctrl-C; active connections can finish after the listener stops. EOF } _expand_short_opts() { # $1 = string of short-opt letters that take a value (e.g. "nXHd"); "" for flag-only scripts # $2..$N = "$@" # Populates _EXPANDED; caller does: set -- "${_EXPANDED[@]}"; unset _EXPANDED local value_opts="$1"; shift _EXPANDED=() local passthru="" local arg local rest local c for arg in "$@"; do if [ -n "$passthru" ]; then _EXPANDED+=("$arg"); continue; fi case "$arg" in --) passthru=1; _EXPANDED+=("$arg") ;; --*|-|"") _EXPANDED+=("$arg") ;; -[a-zA-Z]?*) rest="${arg#-}" while [ -n "$rest" ]; do c="${rest%"${rest#?}"}"; rest="${rest#?}" _EXPANDED+=("-$c") case "$value_opts" in *"$c"*) [ -n "$rest" ] && _EXPANDED+=("$rest") rest="" ;; esac done ;; *) _EXPANDED+=("$arg") ;; esac done } local args=() _expand_short_opts "" "$@" set -- "${_EXPANDED[@]}"; unset _EXPANDED while [ $# -gt 0 ]; do case "$1" in -h|--help) _show_help; return 0 ;; --) shift; while [ $# -gt 0 ]; do args+=("$1"); shift; done ;; -*) _error "Unknown argument '$1'. Run \`$SCRIPT_NAME -h\` for usage"; return 2 ;; *) args+=("$1"); shift ;; esac done if [ "${#args[@]}" -gt 1 ]; then _error "Expected at most one port. Run \`$SCRIPT_NAME -h\` for usage" return 2 fi if [ "${#args[@]}" -eq 1 ]; then local port="${args[0]}" port="${port#"${port%%[!0]*}"}" port="${port:-0}" if ! [[ "${args[0]}" =~ ^[0-9]+$ ]] || [ "${#port}" -gt "$MAX_DECIMAL_DIGITS" ] || [ "$((10#$port))" -lt 1 ] || [ "$((10#$port))" -gt "$MAX_PORT" ]; then _error "Invalid port '${args[0]}' (valid: 1-$MAX_PORT). Run \`$SCRIPT_NAME -h\` for usage" return 2 fi fi local implementation="" local source_path="${BASH_SOURCE[0]}" case "$source_path" in /dev/*|/proc/*|""|bash|sh|zsh|dash) ;; *) implementation="$(dirname "$source_path")/flaky-server" [ -f "$implementation" ] || implementation="" ;; esac if [ -z "$implementation" ]; then implementation="$(type -P flaky-server)"; fi if [ -n "$implementation" ]; then /bin/bash "$implementation" -- "${args[@]}" return $? fi if ! command -v curl >/dev/null 2>&1; then _error "curl is required" return 3 fi local payload if ! payload="$(curl -fsS https://toolio.sh/flaky-server)" || [ -z "$payload" ]; then _error "Failed to download flaky-server" return 1 fi # Run only a complete successful download; preserve arguments without eval /bin/bash -c "$payload" flaky-server -- "${args[@]}" ) _slow_server "$@" __slow_server_rc=$? unset -f _slow_server if [ -n "${BASH_SOURCE[0]}" ] && [ "${BASH_SOURCE[0]}" != "$0" ]; then eval "unset __slow_server_rc; return $__slow_server_rc" fi eval "unset __slow_server_rc; exit $__slow_server_rc"