Created
May 5, 2026 10:34
-
-
Save calston/ea98706e6f210c3e640103ddba9ed4f0 to your computer and use it in GitHub Desktop.
A bash webserver
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Simple bash web server that serves files from ./static | |
| PORT=${1:-8080} | |
| STATIC_DIR="./static" | |
| PIPE="/tmp/bashserver_$$" | |
| NC_PID="" | |
| # Cleanup on exit | |
| cleanup() { | |
| echo -e "\nShutting down server..." | |
| # Kill any running nc process | |
| [[ -n "$NC_PID" ]] && kill "$NC_PID" 2>/dev/null | |
| # Kill all child processes | |
| pkill -P $$ 2>/dev/null | |
| rm -f "$PIPE" | |
| echo "Server stopped." | |
| exit 0 | |
| } | |
| trap cleanup INT TERM EXIT | |
| # Create named pipe | |
| rm -f "$PIPE" | |
| mkfifo "$PIPE" | |
| echo "Starting server on port $PORT..." | |
| echo "Serving files from $STATIC_DIR" | |
| echo "Press Ctrl+C to stop" | |
| get_mime_type() { | |
| case "$1" in | |
| *.html) echo "text/html" ;; | |
| *.css) echo "text/css" ;; | |
| *.js) echo "application/javascript" ;; | |
| *.json) echo "application/json" ;; | |
| *.png) echo "image/png" ;; | |
| *.jpg|*.jpeg) echo "image/jpeg" ;; | |
| *.gif) echo "image/gif" ;; | |
| *.svg) echo "image/svg+xml" ;; | |
| *.txt) echo "text/plain" ;; | |
| *) echo "application/octet-stream" ;; | |
| esac | |
| } | |
| handle_request() { | |
| local request_line path method filepath mime_type content_length | |
| read -r request_line | |
| # Parse the request | |
| method=$(echo "$request_line" | cut -d' ' -f1) | |
| path=$(echo "$request_line" | cut -d' ' -f2) | |
| # Read and discard headers | |
| while read -r header; do | |
| header=$(echo "$header" | tr -d '\r') | |
| [ -z "$header" ] && break | |
| done | |
| # Default to index.html for root | |
| [ "$path" = "/" ] && path="/index.html" | |
| # Build file path | |
| filepath="$STATIC_DIR$path" | |
| # Security: prevent directory traversal | |
| if [[ "$path" == *".."* ]]; then | |
| printf "HTTP/1.1 403 Forbidden\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n403 Forbidden" | |
| return | |
| fi | |
| echo "[$(date)] $method $path" >&2 | |
| if [ -f "$filepath" ]; then | |
| mime_type=$(get_mime_type "$filepath") | |
| content_length=$(wc -c < "$filepath" | tr -d ' ') | |
| printf "HTTP/1.1 200 OK\r\n" | |
| printf "Content-Type: %s\r\n" "$mime_type" | |
| printf "Content-Length: %s\r\n" "$content_length" | |
| printf "Connection: close\r\n" | |
| printf "\r\n" | |
| cat "$filepath" | |
| else | |
| local body="404 Not Found: $path" | |
| printf "HTTP/1.1 404 Not Found\r\n" | |
| printf "Content-Type: text/plain\r\n" | |
| printf "Content-Length: %s\r\n" "${#body}" | |
| printf "Connection: close\r\n" | |
| printf "\r\n" | |
| printf "%s" "$body" | |
| fi | |
| } | |
| # Main server loop using named pipe | |
| while true; do | |
| nc -l "$PORT" < "$PIPE" | handle_request > "$PIPE" & | |
| NC_PID=$! | |
| wait $NC_PID 2>/dev/null | |
| done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment