Skip to content

Commit 52aaeb8

Browse files
committed
multipart hell
1 parent 6d7e30f commit 52aaeb8

File tree

10 files changed

+130
-11
lines changed

10 files changed

+130
-11
lines changed

core.sh

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,79 @@ EOF
168168
# debug "$line"
169169
done
170170

171-
# Read body
172-
CLEN=${HTTP_HEADERS["Content-Length"]}
173-
[[ "$CLEN" =~ ^[0-9]+$ ]] && \
174-
test $CLEN -gt 0 && read -rN $CLEN REQUEST_BODY;
171+
# Parse multipart Form Data
172+
if [[ ${HTTP_HEADERS["Content-Type"]} == "multipart/form-data; "* ]]; then
173+
BOUNDARY="${HTTP_HEADERS["Content-Type"]}"
174+
BOUNDARY="${BOUNDARY#*=}"
175+
debug "BOUNDARY=$BOUNDARY"
176+
fi
177+
178+
179+
# Read cookies (yum!)
180+
if [[ ! -z "${HTTP_HEADERS["Cookie"]}" ]]; then
181+
while read -r -d ';' line; do
182+
COOKIES["${line%%=*}"]=$(urldecode "${line#*=}")
183+
done << EOF
184+
${HTTP_HEADERS["Cookie"]};
185+
EOF
186+
fi
187+
188+
# Read multipart body
189+
if [[ ! -z "$BOUNDARY" ]]; then
190+
state="start"
191+
reader="reading"
192+
local -A MULTIPART_HEADERS
193+
while read -n2 byte; do
194+
# we have to implement our own readline because of reasons
195+
if [[ "$reader" == "reading" ]]; then
196+
if [[ "$byte" == "0a" ]]; then
197+
reader="flushing-newline"
198+
elif [[ "$byte" == "00" ]]; then
199+
reader="flushing-null"
200+
else
201+
line="${line}${byte}"
202+
fi
203+
fi
204+
if [[ "$reader" == "flushing"* ]]; then
205+
PARSED="$(echo -n $line | xxd -r -p)"
206+
if [[ "$state" == "start" ]] && [[ "$PARSED" == "--$BOUNDARY"* ]]; then
207+
state="headers"
208+
MULTIPART_HEADERS=()
209+
elif [[ "$state" == "headers" ]]; then
210+
PARSED="${PARSED%%$'\r'}"
211+
if [[ -z "$PARSED" ]]; then
212+
UPLOAD_TO=$(mktemp -p uploads)
213+
state="body"
214+
else
215+
MULTIPART_HEADERS["${PARSED%%:*}"]="${PARSED#*: }"
216+
fi
217+
elif [[ "$state" == "body" ]]; then
218+
if [[ "$reader" == "flushing-null" ]]; then
219+
# this is a null char
220+
echo -n "${line}00" | xxd -r -p >> "$UPLOAD_TO"
221+
else
222+
# this is a newline char
223+
if [[ "$PARSED" == "--$BOUNDARY--"* ]]; then
224+
# sadly, we cannot goto
225+
break
226+
elif [[ "$PARSED" == "--$BOUNDARY"* ]]; then
227+
state="headers"
228+
else
229+
echo -n "${line}0a" | xxd -r -p >> "$UPLOAD_TO"
230+
fi
231+
fi
232+
fi
233+
reader="reading"
234+
line=''
235+
fi
236+
# wheeeeeeeeeeeeeeeee
237+
done < <(stdbuf -o0 -i0 hexdump -v -e '/1 "%02x"')
238+
else
239+
# Read body
240+
CLEN=${HTTP_HEADERS["Content-Length"]}
241+
[[ "$CLEN" =~ ^[0-9]+$ ]] && \
242+
test $CLEN -gt 0 && read -rN $CLEN REQUEST_BODY;
243+
fi
175244
# Parse Form Data
176245
if [[ ! -z "$REQUEST_BODY" ]] && \
177246
[[ ${HTTP_HEADERS["Content-Type"]} == "application/x-www-form-urlencoded" ]]; then
@@ -182,13 +251,6 @@ ${REQUEST_BODY}&
182251
EOF
183252
fi
184253

185-
if [[ ! -z "${HTTP_HEADERS["Cookie"]}" ]]; then
186-
while read -r -d ';' line; do
187-
COOKIES["${line%%=*}"]=$(urldecode "${line#*=}")
188-
done << EOF
189-
${HTTP_HEADERS["Cookie"]};
190-
EOF
191-
fi
192254
}
193255

194256
writeHttpResponse() {

examples/file-upload/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
data/
2+
pubsub/

examples/file-upload/Dockerfile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
FROM ubuntu
2+
3+
RUN apt-get update && apt-get install ucspi-tcp
4+
5+
EXPOSE 3000
6+
7+
COPY . /app
8+
9+
CMD [ "/app/start.sh" ]

examples/file-upload/core.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../core.sh
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
htmx_page << EOF
3+
<h1>File Upload Example</h1>
4+
<form id='form' hx-encoding='multipart/form-data' hx-post='/upload'
5+
_='on htmx:xhr:progress(loaded, total) set #progress.value to (loaded/total)*100'>
6+
<input type='file' name='file'>
7+
<button>Upload</button>
8+
<progress id='progress' value='0' max='100'></progress>
9+
</form>
10+
EOF
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
3+
debug "WE GOT IT"

examples/file-upload/start.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../start.sh
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#signature {
2+
position: absolute;
3+
z-index: 100;
4+
right: 20px;
5+
top: 20px;
6+
display: flex;
7+
align-items: center;
8+
justify-content: center;
9+
}
10+
11+
#signature a {
12+
text-decoration: none;
13+
}
14+
15+
#signature a:hover {
16+
text-decoration: underline;
17+
}
18+
19+
#signature svg {
20+
width: 36px;
21+
height: 36px;
22+
margin-top: -4px;
23+
vertical-align: middle;
24+
}
25+
26+
#form {
27+
display: flex;
28+
max-width: 400px;
29+
flex-direction: column;
30+
}
121 KB
Loading

start.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
cd "${0%/*}"
44
mkdir -p pubsub
55
mkdir -p data
6+
mkdir -p uploads
67

78
PORT=${PORT:-3000}
89
tcpserver -c 1000 0 $PORT ./core.sh

0 commit comments

Comments
 (0)