-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhttp.sh
122 lines (108 loc) · 2.42 KB
/
http.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#!/usr/bin/env bash
set -e
set -u
HTTP_SERVER_BANNER=${HTTP_SERVER_BANNER-Bashttpd 0.1}
http_log() {
local message="$@"
local date=$(date)
echo -e "[$date] $message" 1>&2
}
http_log_request() {
http_log "${Request[Method]} ${Request[Path]} ${Request[Proto]}"
}
http_log_response() {
http_log "${Response[Status]}"
}
http_server_date() {
date '+%a, %d %b %Y %H:%M:%S %Z'
}
http_content_length() {
local body="$1"
echo $(wc -c <<< "$body")
}
http_accept_request() {
local method url proto
read -r method url proto
if [[ ! $method =~ (OPTIONS|HEAD|GET|POST|PUT|PATCH|DELETE) ]]; then
echo 'Bad method'
return 1
elif [[ -z $url ]]; then
echo 'Bad route'
return 2
elif [[ ! $proto =~ (HTTP/1\.0|HTTP/1\.1) ]]; then
echo 'Bad protocol'
return 3
fi
Request[Method]="$method"
Request[Path]="$url"
Request[Proto]=$(echo "$proto" | tr -d '\r\n')
}
http_parse_headers() {
local header value handled
while read -r header value; do
header=$(echo "$header" | tr -d '\r\n')
[[ -z $header ]] && break
[[ ! $header =~ :$ ]] && continue
Request["${header/:/}"]=$(echo "$value" | tr -d '\r\n')
done
}
http_accept() {
http_accept_request
http_parse_headers
}
http_dispatch() {
local routes="$1"
while read -r route handler; do
[[ -z $route ]] && continue
if [[ $route =~ ^${Request[Path]}/?$ ]]; then
$handler
return 0
fi
done <<<"$routes"
http_404 # Default handler
}
http_respond() {
local proto="${Response[Proto]-HTTP/1.1}"
local status="${Response[Status]-200 OK}"
local body="${Response[Body]-}"
Response[Date]="$(http_server_date)"
Response[Server]="$HTTP_SERVER_BANNER"
Response[Content-Length]="$(http_content_length "$body")"
Response[Connection]="close"
#
echo "$proto $status"
for header in "${!Response[@]}"; do
[[ $header =~ ^(Proto|Status|Body)$ ]] && continue
echo "$header: ${Response[$header]}"
done
echo -e "\n$body"
}
http_404() {
Response[Status]='404 NOT FOUND'
}
http_pipeline() {
declare -A Request
declare -A Response
http_accept
http_log_request
http_dispatch "$routes"
http_respond
http_log_response
}
http_serve() {
local address="$1"
local port="$2"
local routes="$3"
local request_counter=0
trap "rm -f .request*" EXIT
while true; do
local out_pipe=".request.$request_counter"
request_counter=$(($request_counter + 1))
#
mkfifo "$out_pipe"
cat "$out_pipe" | nc -l "$address" "$port" > >(
http_pipeline > "$out_pipe"
)
rm -f "$out_pipe"
done
}