forked from allevaton/starterupper
-
Notifications
You must be signed in to change notification settings - Fork 0
/
web.sh
199 lines (174 loc) · 5.35 KB
/
web.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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#!/bin/bash
source utility.sh
readonly PIPE=.httpipe
# http://mywiki.wooledge.org/NamedPipes
# Also, simultaneous connections
# Is this a request line?
request::line() {
local line="$1"
if [[ -z "$(echo "$line" | grep -E "^GET|^HEAD|^POST|^PUT|^DELETE|^CONNECT|^OPTIONS|^TRACE")" ]]; then
Utility_fail
fi
Utility_success
}
# Get the method (e.g., GET, POST) of the request
request::method() {
local request="$1"
echo "$request" | sed -e "s/\(^[^ ]*\).*/\1/"
}
# Get the target (URL) of the request
request::target() {
local request="$1"
echo "$request" | sed -e 's/^[^ ]* \(.*\) HTTP\/.*/\1/' | head -n 1
}
# Get the file from the request target URL
request::file() {
local request="$1"
local target="$(request::target "$request")"
# Leave the root request alone
if [[ "$target" == "/" ]]; then
printf "/"
# Remove attempts to look outside the current folder, strip off the leading slash and the query
else
printf "$target" | sed -e 's/[.][.]//g' -e 's/^[/]*//g' -e 's/[?].*$//'
fi
}
# Get the query portion of the request target URL, and return the results line by line
request::query() {
request::target "$1" | sed -e 's/.*[?]\(.*\)$/\1/' | tr '&' '\n'
}
# Parse the request payload as form-urlencoded data
request::post_form_data() {
local request="$1"
local payload="$(request::payload "$request")"
echo -e "REQUEST $request" >&2
if [[ "$(request::lookup "$request" "Content-Type")" == "application/x-www-form-urlencoded" ]]; then
echo "$payload" | tr '&' '\n'
fi
}
# Given a query key, return the URL decoded value
Query_lookup() {
local query="$1"; shift
local key="$1"
echo -e "$(printf "$query" | grep "$key" | sed -e "s/^$key=\(.*\)/\1/" -e 'y/+/ /; s/%/\\x/g')"
}
# Return the key corresponding to the field
Parameter_key() {
local parameter="$1"
echo "$parameter" | cut -d '=' -f 1
}
# Return the URL decoded value corresponding to the field
Parameter_value() {
local parameter="$1"
echo -e "$(echo "$parameter" | cut -d '=' -f 2 | sed 'y/+/ /; s/%/\\x/g')"
}
# Given a header key, return the value
request::lookup() {
local request="$1"; shift
local key="$1"
echo -e "$request" | grep "$key" | sed -e "s/^$key: \(.*\)/\1/"
}
# Return the payload of the request, if any (e.g., for POST requests)
request::payload() {
local request="$1"; shift
echo -e "$request" | sed -n -e '/^$/,${p}'
}
# Pipe HTTP request into a string
request::new() {
local line="$1"
# If we got a request, ...
if [[ $(request::line "$line") ]]; then
local request="$line"
# Read all headers
while read -r header; do
request="$request\n$header"
if [[ -z "$header" ]]; then
break
fi
done
# Sometimes, we have a payload in the request, so handle that, too...
local length="$(request::lookup "$request" "Content-Length")"
local payload=""
if [[ -n "$length" ]] && [[ "$length" != "0" ]]; then
read -r -n "$length" payload
request="$request\n$payload"
fi
fi
# Return single line string
echo "$request"
}
# Send out HTTP response and headers
response::send() {
local response="$1"; shift
local length="$1"; shift
local type="$1";
echo -ne "$response\r\nDate: $(date '+%a, %d %b %Y %T %Z')\r\nServer: Starter Upper\r\nContent-Length: $length\r\nContent-Encoding: binary\r\nContent-Type: $type\r\n\r\n"
}
# Send file with HTTP response headers
server::send_file() {
local file="$1"; shift
local response="HTTP/1.1 200 OK"
if [[ -z "$file" ]]; then
return 0
fi
if [[ ! -f "$file" ]]; then
response="HTTP/1.1 404 Not Found"
file="404.html"
fi
local type="$(Utility_MIMEType $file)"
response::send "$response" "$(Utility_fileSize "$file")" "$type"
cat "$file"
echo "SENT $file" >&2
}
# Listen for requests
server::listen() {
local request=""
while read -r line; do
request=$(request::new "$line")
# Send the request through
Pipe_write "$PIPE" "$request\n"
done
}
# Respond to requests, using supplied route function
# The route function is a command that takes a request argument: it should send a response
server::respond() {
local routeFunction="$1"
local request=""
Pipe_await "$PIPE"
while true; do
request="$(Pipe_read "$PIPE")"
# Pass the request to the route function
"$routeFunction" "$request"
done
}
Acquire_netcat() {
local netcat=""
# Look for netcat
for program in "nc" "ncat" "netcat"; do
if [[ -n "$(which $program)" ]]; then
netcat=$program
break
fi
done
# Get netcat, if it's not already installed
if [[ -z "$netcat" ]]; then
curl http://nmap.org/dist/ncat-portable-5.59BETA1.zip 2> /dev/null > ncat.zip
unzip -p ncat.zip ncat-portable-5.59BETA1/ncat.exe > nc.exe
netcat="nc"
rm ncat.zip
fi
printf $netcat
}
server::works() {
local nc=$(Acquire_netcat)
"$nc" -l 8080 &
sleep 10
echo "works" > /dev/tcp/localhost/8080
}
# Start the web server, using the supplied routing function
server::start() {
local routes="$1"
Pipe_new "$PIPE"
local nc=$(Acquire_netcat)
server::respond "$routes" | "$nc" -k -l 8080 | server::listen
}