-
Notifications
You must be signed in to change notification settings - Fork 12
/
server.bf
406 lines (330 loc) · 10.1 KB
/
server.bf
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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
Preamble: some punctuation is valid Brainfuck so there is very little punctuation in these comments
This is a server that returns your useragent back to you if you sent a GET request to / and returns a 404 otherwise
Step 1: send "HTTP/1(dot)1 "
============================
You could be clever with this but I prefer to be boring
H ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.
T ++++++++++++.
T .
P ----.
/ [-]+++++++++++++++++++++++++++++++++++++++++++++++.
1 ++.
dot ---.
1 +++.
space -----------------.
Step 2: is this a GET request?
==============================
Turns out that if you sum the ASCII values of each character in GET or PUT or DELETE (etc) you get different numbers (GET = 224; POST = 326; etc)
GET is the only thing that sums to 224 and with a trailing space it sums to 256
This is fabulous for us
Here's what we'll do:
* start a counter at 256 (in register 0)
* for each character:
* subtract the ASCII value from this counter
* if it's a space then we stop looping
* if the counter is 0 then we know it's a GET request
* if we know it's NOT a GET request then we send a 404 message
Let's make register 0 become 256:
>+++++++++++++++++[-<+++++++++++++>]<+++
Head to r1 and make sure it's nonzero so that we run the following loop at least once
>+
[
In the context of this loop:
* r0 = the counter (mentioned above)
* r1 = the ASCII character for space (32)
* r2 = the character we read
First step is to zero out r1
Iterations of this loop could make r1 positive OR negative so we add a bunch of numbers to it to make sure it's positive before we zero it out
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
[-]
Next we make r1 equal to 32 (the ASCII code for space)
++++++++++++++++++++++++++++++++
Read the character into r2
>,
Subtract the ASCII value from the counter (r0) AND subtract its value from r1
[-<-<->>]
Head back to r1
This loop will stop if the character was a space (AKA r1 == 0)
<
]
Head back to r0
<
At this point:
* if r0 == 0 then this is a GET request
* else this is any other HTTP method (POST or PUT or DELETE or whatever) and r0 is negative
The state of the registers:
* r0 is mentioned above
* everything else is 0
* pointer @ r0
Step 3: is this a request to /?
===============================
At this point we've read "(METHOD) "
The next part is the path which we hope to be "/" and nothing else
The desired result is this:
* if r1 == 0 then this is a request to /
* else this is NOT a request to / and we should 404
Let's head to r1
>
Every path starts with a / so we can read that in and ignore it
But the next character is important so we'll take a look at that
,,
Subtract 32 from this which will give us the desired result from above
--------------------------------
Let's return to r0
<
At this point:
* if r0 == 0 then this is a GET request
* if r1 == 0 then this is a request to /
* else this is a request to any other path and r1 is positive
The state of the registers:
* r0 is mentioned above
* r1 is mentioned above
* pointer @ r0
Step 4: send an error if we should
==================================
This is the dream code we'd love to write:
if path != '/':
send_404
elif method != 'GET':
send_405
else:
send_200
send_user_agent
This kinda translates to:
if r1 != 0:
send_404
r0 = 0
r2 = negative 1
if r0 != 0:
send_405
r2 = negative 1
set r2 to r2 plus 1
if r2 != 0:
send_200
send_user_agent
Here's how we'll do this:
If r1 != 0:
>[
Move to r2
>
r2 is already 0 so we can just start to print stuff
4 ++++++++++++++++++++++++++++++++++++++++++++++++++++.
0 ----.
4 ++++.
--------------------.
N ++++++++++++++++++++++++++++++++++++++++++++++.
o +++++++++++++++++++++++++++++++++.
t +++++.
------------------------------------------------------------------------------------.
F ++++++++++++++++++++++++++++++++++++++.
o +++++++++++++++++++++++++++++++++++++++++.
u ++++++.
n -------.
d ----------.
\r\n [-]+++++++++++++.---.
Reset r2
[-]
Increment r2
+
Zero out r1
<[-]
Zero out r0 to prevent 405 from being sent
<[+]
]
<
If r0 != 0:
[
As we cleared r1 above we can be reasonably sure that r2 is still zero
So we use that for printing
>>
"405 Method Not Allowed"
4 ++++++++++++++++++++++++++++++++++++++++++++++++++++.
0 ----.
5 +++++.
---------------------.
M +++++++++++++++++++++++++++++++++++++++++++++.
e ++++++++++++++++++++++++.
t +++++++++++++++.
h ------------.
o +++++++.
d -----------.
--------------------------------------------------------------------.
N ++++++++++++++++++++++++++++++++++++++++++++++.
o +++++++++++++++++++++++++++++++++.
t +++++.
------------------------------------------------------------------------------------.
A +++++++++++++++++++++++++++++++++.
l +++++++++++++++++++++++++++++++++++++++++++.
l .
o +++.
w ++++++++.
e ------------------.
d -.
\r ---------------------------------------------------------------------------------------.
\n ---.
RFC 2616 mandates an Allow header on a 405 response
A +++++++++++++++++++++++++++++++++++++++++++++++++++++++.
l +++++++++++++++++++++++++++++++++++++++++++.
l .
o +++.
w ++++++++.
: -------------------------------------------------------------.
--------------------------.
G +++++++++++++++++++++++++++++++++++++++.
E --.
T +++++++++++++++.
\r\n [-]+++++++++++++.---.
Zero out r2
[-]
Set r2 to 1
+
Zero out r0
<<[+]
]
At this point:
* if r2 != 0 then we set r1 to negative 1
* if r2 == 0 then we leave r1 be
>>[
Set r2 to 0
[-]
Set r1 to negative 1—we'll come back to this
<->
]
Increment r1 as a marker for "is this a GET request to /"
It'll be 0 if it's NOT a GET request
It'll be 1 if it IS a GET request
<+
Head back to r0
<
At this point:
* if r1 == 1 then this is a GET request to /
* else this is NOT a GET request to /
The state of the registers:
* r1 is mentioned above
* everything else is 0
* pointer @ r0
Step 5: grab and print the useragent
====================================
This is the big kahuna
As mentioned above: if r1 == 1 then this is a GET request to /
So let's go through this loop if we should
>[
Step 5/1: finish the first line of the HTTP request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
First let's read until we hit the \r character (into r1)
[,-------------]
Skip the \n that follows it and then zero out r1
,[-]
Back to r0
<
The state of the registers:
* everything is 0
* pointer @ r0
Step 5/2: send "200 OK" and the content type
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Send "200 OK" before we start parsing and printing stuff
2 ++++++++++++++++++++++++++++++++++++++++++++++++++.
0 --.
0 .
----------------.
O +++++++++++++++++++++++++++++++++++++++++++++++.
K ----.
"\r\nContent(dash)Type: text/plain\r\n\r\n"
\r\n [-]+++++++++++++.---.
C +++++++++++++++++++++++++++++++++++++++++++++++++++++++++.
o ++++++++++++++++++++++++++++++++++++++++++++.
n -.
t ++++++.
e ---------------.
n +++++++++.
t ++++++.
- ----------------------------------------------------------------------.
T +++++++++++++++++++++++++++++++++++++++.
y +++++++++++++++++++++++++++++++++++++.
p ---------.
e -----------.
: -------------------------------------------.
--------------------------.
t ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.
e ---------------.
x +++++++++++++++++++.
t ----.
/ ---------------------------------------------------------------------.
p +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.
l ----.
a -----------.
i ++++++++.
n +++++.
\r\n [-]+++++++++++++.---.
\r\n +++.---.
The state of the registers:
* r0 == 10
* pointer @ r0
Step 5/3: find the useragent header
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
HTTP request headers are like HTTP verbs in that you can sum their ASCII codes and get unique values
You can look at the "List of HTTP header fields" page on Wikipedia to verify this
"User(dash)Agent: " sums to 1045 so let's set r0 to 1045
(We use r1 to help here but zero it out when we're done)
>++++++++++++++++++++++++++++++++[-<++++++++++++++++++++++++++++++++>]<+++++++++++>
Let's read lines until we see "User(dash)Agent:"
We will keep looping while r1 == 1
+[
At the start of this loop:
* r0 = 1045
* r1 = 1
Let's copy 1045 from r0 into r2 and r3
* r2 will start at 1045 and we'll decrement it
* r3 will stay at 1045 so we can reset r0 at the end
<[->>+>+<<<]
Read each character up until the space
all the while subtracting it from r2
+[
Read the character into r0
,
Subtract r2 from it and move it to r4
[->>->>+<<<<]
Move r4 back to r0
>>>>[-<<<<+>>>>]
Subtract 32 from r0 so we can figure out if this is a space
<<<<--------------------------------
]
Move r3 back into r0
>>>[-<<<+>>>]
Now we've read the header key (and the ": ")!
If our counter (in r2) is 0:
* we've found our useragent header
* r1 should be set to 0 so we leave this loop
If our counter (in r2) is NOT 0:
* we haven't found our useragent header
* we should read until the end of the line
* r1 should be set to 1 so we stay in this loop
Always decrement r1 (setting it to 0)
We'll increment it back to 1 if r2 wasn't 0
<<-
If we haven't found the header yet we need to
>[
Increment r1
<+>
Read until the end of the line
[,----------]
]
Head back to r1
<
]
Return to r0
<
The state of the registers:
* r0 == 1045
* pointer @ r0
Step 5/4: print the useragent
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This part is pretty easy
We'll just print every character until we hit \n
[,.----------]
]
That was horrible