]> code.delx.au - monosys/blob - bashttpd
gnome-shell-raise-window
[monosys] / bashttpd
1 #!/bin/bash
2
3 ##########################################################################
4 # Bash HTTP Server #
5 # #
6 # This is just a bit of fun. Please don't trust it for anything serious! #
7 ##########################################################################
8
9 # static configuration
10 PORT="${PORT:-8080}"
11 SERVER_ROOT="${SERVER_ROOT:-$PWD}"
12
13 # per-request globals
14 STATE_FUNC=""
15 REQUEST_LINE=""
16 REQUEST_FILE=""
17
18
19 function main {
20 set -e
21
22 cd "$SERVER_ROOT"
23 print_log "Server running on port $PORT in $SERVER_ROOT"
24
25 while true; do
26 handle_connection
27 done
28 }
29
30 function print_log {
31 echo "$(date -Iseconds) $@"
32 }
33
34 function handle_connection {
35 start_netcat
36
37 STATE_FUNC="handle_read_request_status_line"
38 while [ -n "$STATE_FUNC" ]; do
39 $STATE_FUNC || break
40 done <&"${COPROC[0]}"
41
42 close_request
43 }
44
45 function start_netcat {
46 coproc nc -l "$PORT"
47 }
48
49 function close_request {
50 STATE_FUNC=""
51 kill "$COPROC_PID" &> /dev/null || true
52 wait "$COPROC_PID" &> /dev/null || true
53 }
54
55 function handle_read_request_status_line {
56 read_request_line
57 print_log "$REQUEST_LINE"
58
59 read method path ignored < <(echo "$REQUEST_LINE")
60 REQUEST_FILE=".$path"
61
62 check_valid_method || return 0
63 check_path_is_under_server_root || return 0
64 find_index_in_request_file
65 check_request_file_exists || return 0
66
67 STATE_FUNC="handle_read_request_to_end"
68 }
69
70 function check_valid_method {
71 if [ "$method" != "GET" ]; then
72 STATE_FUNC="write_error_response_405"
73 return 1
74 fi
75 }
76
77 function check_path_is_under_server_root {
78 abspath="$(cd "$(dirname "$REQUEST_FILE")" && pwd)"
79 if ! echo "$abspath" | grep -q "^$PWD"; then
80 STATE_FUNC="write_error_response_400"
81 return 1
82 fi
83 }
84
85 function find_index_in_request_file {
86 if [ ! -d "$REQUEST_FILE" ]; then
87 return 0
88 fi
89
90 for filename in index.html index.txt; do
91 if [ -f "${REQUEST_FILE}/${filename}" ]; then
92 REQUEST_FILE="${REQUEST_FILE}/${filename}"
93 return 0
94 fi
95 done
96 }
97
98 function check_request_file_exists {
99 if [ ! -f "$REQUEST_FILE" ]; then
100 STATE_FUNC="write_error_response_404"
101 return 1
102 fi
103 }
104
105 function handle_read_request_to_end {
106 read_request_line
107
108 if [ -z "$REQUEST_LINE" ]; then
109 STATE_FUNC="write_response"
110 fi
111 }
112
113 function read_request_line {
114 read REQUEST_LINE
115 REQUEST_LINE="$(echo "$REQUEST_LINE" | tr -d ' ')"
116 }
117
118 function write_response {
119 STATE_FUNC=""
120 cat <<EOT >&"${COPROC[1]}"
121 HTTP/1.0 200 OK
122 Server: bashttpd
123 Content-Type: $(file -bi "$REQUEST_FILE")
124 Connection: close
125
126 $(cat "$REQUEST_FILE")
127 EOT
128 }
129
130 function write_error_response {
131 STATE_FUNC=""
132 local code="$1"
133 local message="$2"
134 cat <<EOT >&"${COPROC[1]}"
135 HTTP/1.0 $code $message
136
137 <!DOCTYPE html>
138 <html>
139 <head>
140 <title>$code $message</title>
141 </head>
142 <body>
143 <h1>$code $message</h1>
144 </body>
145 </html>
146 EOT
147 }
148
149 function write_error_response_400 {
150 write_error_response 400 "Bad Request"
151 }
152
153 function write_error_response_404 {
154 write_error_response 404 "File Not Found"
155 }
156
157 function write_error_response_405 {
158 write_error_response 405 "Unsupported method"
159 }
160
161 function check_dependencies {
162 check_bash
163 check_netcat
164 }
165
166 function check_bash {
167 if [ "${BASH_VERSINFO[0]}" -lt 4 ]; then
168 echo "ERROR! Requires Bash 4+ for coproc support"
169 exit 1
170 fi
171 }
172
173 function check_netcat {
174 if ! nc -h 2>&1 | head -n1 | grep -q '^OpenBSD netcat'; then
175 echo "ERROR! Requires OpenBSD netcat to be installed"
176 exit 1
177 fi
178 }
179
180 check_dependencies
181 main