├── .gitignore
├── LICENSE
├── README.md
├── docker
├── .dockerignore
├── Dockerfile
├── Dockerfile.minimal
├── Makefile
├── httpd
├── index.htm
├── index.html
└── pi_armed_with_docker.jpg
├── src
├── .gitignore
├── FasmArm.inc
├── fasmarm
└── httpd.fasm
└── start-webservers.sh
/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypriot/rpi-nano-httpd/ab5ab3eb3d536047ec597f08b2543bed08bc3b21/.gitignore
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Hypriot
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # rpi-nano-httpd
2 |
3 | A nano sized web server packed into a Docker Nano Container
4 |
5 | ## Step 1: Compile the assembly source code
6 |
7 | Compiling the ASM source to a statically linked ARM binary for Raspberry Pi.
8 |
9 | The source can be found in `src/` folder. You need a x86/amd64 Linux machine to compile the source assembly code with FASM (can be downloaded from http://arm.flatassembler.net). You only need the statically compiled `fasmarm` binary. I've done it within a Ubuntu 14.04 (64bit) machine running as a Docker Container.
10 |
11 | Compile the thing just with `./fasmarm httpd.fasm httpd`
12 | ```
13 | cd ./src/
14 | docker run --rm -ti -v $(pwd):/src ubuntu:14.04 bash -c 'cd /src && ./fasmarm httpd.fasm httpd'
15 | flat assembler for ARM version 1.71.39 (16384 kilobytes memory)
16 | 3 passes, 4328 bytes.
17 | ```
18 | Now we've got a super small httpd binary with 4kByte only
19 | ```
20 | ls -al httpd
21 | -rwxr-xr-x 1 dieter staff 4328 Jun 28 16:27 httpd
22 | ```
23 | And hell yeah, it's statically linked
24 | ```
25 | file httpd
26 | httpd: ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, stripped
27 | ```
28 |
29 | Let's copying it to `./docker/` folder
30 | ```
31 | cp httpd ../docker/
32 | ```
33 |
34 | ## Step 2: Create the Docker Nano Image
35 | ```
36 | cd ./docker/
37 | make
38 | ```
39 |
40 | Now we do have a ready-to-run Docker Image with a single statically linked ARM binary for use on a Raspberry Pi.
41 | ```
42 | docker images
43 | REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
44 | hypriot/rpi-nano-httpd 0.1.0 0fd6d79d7479 15 minutes ago 87.7 kB
45 | hypriot/rpi-nano-httpd latest 0fd6d79d7479 15 minutes ago 87.7 kB
46 | ```
47 | Please note, the size of the Docker Image includes the payload too! Without the payload the image size would be 4kByte only! Ok, that's not `nano` anymore, that's more `pico` sized.
48 |
49 | ## Step 3: Now let's run a lot of these containers on our Raspberry Pi
50 | To start 10 web servers use the following command
51 | ```
52 | ./start-webservers.sh 10
53 | ```
54 | To ramp up to 100 web servers use the following command
55 | ```
56 | ./start-webservers.sh 100 10
57 | ```
58 | That's it, have fun.
59 |
60 |
61 | # Acknoledgements
62 |
63 | ## httpd, original source code
64 | https://www.raspberrypi.org/forums/viewtopic.php?p=320919
65 |
66 | I was so happy to find this small piece of source code after hours. It's a minimal web server or httpd written in assembly language to run on an ARM cpu. I did only a minor change and use `index.html` as the default resource to look for.
67 | As the source code is written for FASM you also need to download this tool too.
68 |
69 | ## FASMARM: Freeware ARM cross assembler for FASM
70 | http://arm.flatassembler.net
71 |
72 | Just download the package for a Linux distro and extract the tool `fasmarm` which is a statically linked binary and doesn't need any additional dependencies installed.
73 |
--------------------------------------------------------------------------------
/docker/.dockerignore:
--------------------------------------------------------------------------------
1 | Dockerfile*
2 | Makefile
3 |
--------------------------------------------------------------------------------
/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM scratch
2 | ADD . /
3 | EXPOSE 80
4 | CMD ["/httpd", "80"]
5 |
--------------------------------------------------------------------------------
/docker/Dockerfile.minimal:
--------------------------------------------------------------------------------
1 | FROM scratch
2 | ADD . /
3 |
--------------------------------------------------------------------------------
/docker/Makefile:
--------------------------------------------------------------------------------
1 | DOCKER_IMAGE_VERSION=0.1.0
2 | DOCKER_IMAGE_NAME=hypriot/rpi-nano-httpd
3 | DOCKER_IMAGE_TAGNAME=$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_VERSION)
4 |
5 | default: build
6 |
7 | build:
8 | docker build -t $(DOCKER_IMAGE_TAGNAME) .
9 | docker tag -f $(DOCKER_IMAGE_TAGNAME) $(DOCKER_IMAGE_NAME):latest
10 |
11 | minimal:
12 | docker build -t $(DOCKER_IMAGE_TAGNAME) -f Dockerfile.minimal .
13 | docker tag -f $(DOCKER_IMAGE_TAGNAME) $(DOCKER_IMAGE_NAME):minimal
14 |
15 | push:
16 | docker push $(DOCKER_IMAGE_NAME)
17 |
18 | run:
19 | docker run -d -p 80:80 -d hypriot/rpi-nano-httpd
20 |
--------------------------------------------------------------------------------
/docker/httpd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypriot/rpi-nano-httpd/ab5ab3eb3d536047ec597f08b2543bed08bc3b21/docker/httpd
--------------------------------------------------------------------------------
/docker/index.htm:
--------------------------------------------------------------------------------
1 |
2 |
Pi armed with Docker by Hypriot
3 |
4 |
5 |

6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docker/index.html:
--------------------------------------------------------------------------------
1 |
2 | Pi armed with Docker by Hypriot
3 |
4 |
5 |

6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docker/pi_armed_with_docker.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypriot/rpi-nano-httpd/ab5ab3eb3d536047ec597f08b2543bed08bc3b21/docker/pi_armed_with_docker.jpg
--------------------------------------------------------------------------------
/src/.gitignore:
--------------------------------------------------------------------------------
1 | httpd
2 |
--------------------------------------------------------------------------------
/src/FasmArm.inc:
--------------------------------------------------------------------------------
1 | macro imm16 reg,immediate {
2 | mov reg,(immediate) and $FF
3 | orr reg,(immediate) and $FF00
4 | }
5 |
6 | macro imm16eq reg,immediate {
7 | moveq reg,(immediate) and $FF
8 | orreq reg,(immediate) and $FF00
9 | }
10 |
11 | macro imm16ne reg,immediate {
12 | movne reg,(immediate) and $FF
13 | orrne reg,(immediate) and $FF00
14 | }
15 |
16 | macro imm16lt reg,immediate {
17 | movlt reg,(immediate) and $FF
18 | orrlt reg,(immediate) and $FF00
19 | }
20 |
21 | macro imm16gt reg,immediate {
22 | movgt reg,(immediate) and $FF
23 | orrgt reg,(immediate) and $FF00
24 | }
25 |
26 | macro imm32 reg,immediate {
27 | mov reg,(immediate) and $FF
28 | orr reg,(immediate) and $FF00
29 | orr reg,(immediate) and $FF0000
30 | orr reg,(immediate) and $FF000000
31 | }
32 |
33 | macro imm32eq reg,immediate {
34 | moveq reg,(immediate) and $FF
35 | orreq reg,(immediate) and $FF00
36 | orreq reg,(immediate) and $FF0000
37 | orreq reg,(immediate) and $FF000000
38 | }
39 |
40 | macro imm32ne reg,immediate {
41 | movne reg,(immediate) and $FF
42 | orrne reg,(immediate) and $FF00
43 | orrne reg,(immediate) and $FF0000
44 | orrne reg,(immediate) and $FF000000
45 | }
46 |
47 | macro imm32lt reg,immediate {
48 | movlt reg,(immediate) and $FF
49 | orrlt reg,(immediate) and $FF00
50 | orrlt reg,(immediate) and $FF0000
51 | orrlt reg,(immediate) and $FF000000
52 | }
53 |
54 | macro imm32gt reg,immediate {
55 | movgt reg,(immediate) and $FF
56 | orrgt reg,(immediate) and $FF00
57 | orrgt reg,(immediate) and $FF0000
58 | orrgt reg,(immediate) and $FF000000
59 | }
60 |
--------------------------------------------------------------------------------
/src/fasmarm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypriot/rpi-nano-httpd/ab5ab3eb3d536047ec597f08b2543bed08bc3b21/src/fasmarm
--------------------------------------------------------------------------------
/src/httpd.fasm:
--------------------------------------------------------------------------------
1 | ;======================================================;
2 | ; Tiny Server. ;
3 | ;------------------------------------------------------;
4 | ; By Craig Bamford (Dex) (11-03-2013) ;
5 | ;------------------------------------------------------;
6 | format ELF executable ;
7 | entry start ;
8 | ;======================================================;
9 | ; Is logging enabled ? ;
10 | ;------------------------------------------------------;
11 | LOGGING = 1 ;
12 | LOGGING2 = 0 ;
13 | ;======================================================;
14 | ; Def ;
15 | ;------------------------------------------------------;
16 | SOCK_STREAM = 1 ;
17 | AF_INET = 2 ;
18 | SYS_socketcall = 102 ;
19 | SYS_SOCKET = 1 ;
20 | SYS_CONNECT = 3 ;
21 | SYS_SEND = 9 ;
22 | SYS_RECV = 10 ;
23 | IPPROTO_TCP = 6 ; Transmission Control Protocol
24 | ;======================================================;
25 | ; Print macro ;
26 | ;------------------------------------------------------;
27 | macro print _msg,_msglen ;
28 | { common ;
29 | push (r0) ;
30 | push (r1) ;
31 | push (r2) ;
32 | push (r7) ;
33 | mov r7, 0x4 ;
34 | mov r0, 0x1 ;
35 | imm32 r1, _msg ;
36 | imm32 r2, _msglen ;
37 | swi 0x0 ;
38 | ;
39 | pop (r7) ;
40 | pop (r2) ;
41 | pop (r1) ;
42 | pop (r0) ;
43 | } ;
44 | ;======================================================;
45 | ; old tcpsend macro ;
46 | ;------------------------------------------------------;
47 | macro tcpsend _msg,_msglen ;
48 | { common ;
49 | push (r0) ;
50 | push (r1) ;
51 | push (r2) ;
52 | push (r3) ;
53 | push (r7) ;
54 | imm32 r0, clientfd ;
55 | ldr r0, [r0] ;
56 | imm32 r1, _msg ;
57 | imm32 r2, _msglen ;
58 | mov r3, 0 ;
59 | imm32 r7, 0x121 ;
60 | swi 0x0 ;
61 | pop (r7) ;
62 | pop (r3) ;
63 | pop (r2) ;
64 | pop (r1) ;
65 | pop (r0) ;
66 | } ;
67 | ;======================================================;
68 | ; old tcpsend macro ;
69 | ;------------------------------------------------------;
70 | ; r0 = size to send ;
71 | ;------------------------------------------------------;
72 | macro tcpsend_reg _msg ;
73 | { common ;
74 | push (r0) ;
75 | push (r1) ;
76 | push (r2) ;
77 | push (r3) ;
78 | push (r7) ;
79 | mov r2, r0 ;
80 | imm32 r0, clientfd ;
81 | ldr r0, [r0] ;
82 | imm32 r1, _msg ;
83 | mov r3, 0 ;
84 | imm32 r7, 0x121 ;
85 | swi 0x0 ;
86 | pop (r7) ;
87 | pop (r3) ;
88 | pop (r2) ;
89 | pop (r1) ;
90 | pop (r0) ;
91 | } ;
92 | ;======================================================;
93 | ; Print macro ;
94 | ;------------------------------------------------------;
95 | ; r0 = string size ;
96 | ;------------------------------------------------------;
97 | macro print_reg _msg,_msglen ;
98 | { common ;
99 | push (r0) ;
100 | push (r1) ;
101 | push (r2) ;
102 | push (r7) ;
103 | mov r2, r0 ;
104 | mov r7, 0x4 ;
105 | mov r0, 0x1 ;
106 | imm32 r1, _msg ;
107 | swi 0x0 ;
108 | ;
109 | pop (r7) ;
110 | pop (r2) ;
111 | pop (r1) ;
112 | pop (r0) ;
113 | } ;
114 | include 'FasmArm.inc' ;
115 | segment readable writeable executable ;
116 | ;======================================================;
117 | ; Main start ;
118 | ;------------------------------------------------------;
119 | start: ;
120 | print msg,msg.msg_size ; print start message
121 | ;======================================================;
122 | ; Check commamd line for port number ;
123 | ;------------------------------------------------------;
124 | ldmfd sp!, {r1} ;
125 | imm32 r0, argc ;
126 | str r1, [r0] ;
127 | mov r0, 80 ; default port number
128 | cmp r1, 1 ;
129 | ;======================================================;
130 | ; If no number, use default (port 80) ;
131 | ;------------------------------------------------------;
132 | beq UseDeFault ;
133 | ;======================================================;
134 | ; Convet port number ;
135 | ;------------------------------------------------------;
136 | ldmfd sp!,{r1} ;
137 | ldmfd sp!,{r3} ;
138 | mov r4, 10 ;
139 | bl StrToInt ;
140 | mov r0, r1 ;
141 | imm32 r1, portnumber ;
142 | str r0, [r1] ;
143 | align 4 ;
144 | UseDeFault: ;
145 | ;======================================================;
146 | ; Store Port ;
147 | ;------------------------------------------------------;
148 | rev16 r0, r0 ; byte order
149 | imm32 r1, sockaddr_in.sin_port ;
150 | strh r0, [r1] ;
151 | ;======================================================;
152 | ; SOCKET(2) ;
153 | ;------------------------------------------------------;
154 | ; Creates an endpoint for communication and returns ;
155 | ; a descriptor. ;
156 | ;------------------------------------------------------; ;
157 | imm32 r7, 0x119 ; socket sys number
158 | mov r0, AF_INET ;
159 | mov r1, SOCK_STREAM ;
160 | mov r2, 0 ;
161 | swi 0x0 ;
162 | teq r0, 0 ;
163 | bpl bind ; branch if not neg
164 | ;
165 | mvn r0, r0 ;
166 | add r0, 1 ; change neg to pos number
167 | imm32 r1, 4096 ; We need to check because some times address is neg
168 | cmp r0, r1 ;
169 | bl Convert2Hex ; Get error code
170 | print print_hex_string,print_hex_string_size ;
171 | print msgE1,msgE1.msgE1_size ; error message 1
172 | b fin ; branch exit
173 | align 4 ;
174 | ;======================================================;
175 | ; BIND(2) ;
176 | ;------------------------------------------------------;
177 | ; When a socket is created with socket(2), ;
178 | ; it exists in a name space (address family) but has ;
179 | ; no address assigned to it. bind() assigns the ;
180 | ; address specified to by addr to the socket referred ;
181 | ; to by the file descriptor sockfd. addrlen specifies ;
182 | ; the size, in bytes, of the address structure pointed ;
183 | ; to by addr. Traditionally, this operation is called ;
184 | ; "assigning a name to a socket". ;
185 | ;------------------------------------------------------;
186 | bind: ; Ok SOCKET(2)
187 | imm32 r1, sockfd ;
188 | str r0, [r1] ;
189 | ;
190 | imm32 r0, sockfd ; bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
191 | ldr r0, [r0] ;
192 | imm32 r1, sockaddr_in ;
193 | imm32 r2, dest_size ;
194 | imm32 r7, 0x11A ; BIND(2)
195 | swi 0x0 ;
196 | ;
197 | teq r0, 0 ; check for errors
198 | bpl listen ; branch if not neg
199 | ;
200 | mvn r0, r0 ;
201 | add r0, 1 ; change neg to pos number
202 | imm32 r1, 4096 ; We need to check because some times address is neg
203 | cmp r0, r1 ;
204 | bl Convert2Hex ; Get error code
205 | print print_hex_string,print_hex_string_size ;
206 | print msgE2,msgE2.msgE2_size ; error message 1
207 | b fin ; branch exit
208 | ; Ok BIND(2)
209 | ;======================================================;
210 | ; LISTEN(2) ;
211 | ;------------------------------------------------------;
212 | ; Start to listen for incomming connections, ;
213 | ; allowing for a queue size of ;
214 | ; waiting connections of 20. ;
215 | ;------------------------------------------------------;
216 | align 4 ;
217 | listen: ;
218 | imm32 r0, sockfd ; listen(int sockfd, int backlog);
219 | ldr r0, [r0] ;
220 | mov r1, 0 ; was 20 *****
221 | imm32 r7, 0x11C ; LISTEN(2)
222 | swi 0x0 ;
223 | ;
224 | teq r0, 0 ; check for errors
225 | bpl infinity ; branch if not neg
226 | ;
227 | mvn r0, r0 ;
228 | add r0, 1 ; change neg to pos number
229 | imm32 r1, 4096 ; We need to check because some times address is neg
230 | cmp r0, r1 ;
231 | bl Convert2Hex ; Get error code
232 | print print_hex_string,print_hex_string_size ;
233 | print msgE3,msgE3.msgE3_size ; error message 1
234 | b fin ; branch exit
235 | align 4 ;
236 | infinity: ; Ok LISTEN(2)
237 | ;======================================================;
238 | ; ACCEPT(2) ;
239 | ;------------------------------------------------------;
240 | ; The accept() system call is used with ;
241 | ; connection-based socket types (SOCK_STREAM, ;
242 | ; SOCK_SEQPACKET). It extracts the first connection ;
243 | ; request on the queue of pending connections for the ;
244 | ; listening socket, sockfd, creates a new connected ;
245 | ; socket, and returns a new file descriptor referring ;
246 | ; to that socket. The newly created socket is not in ;
247 | ; the listening state. The original socket sockfd is ;
248 | ; unaffected by this call. ;
249 | ;------------------------------------------------------;
250 | accept_loop: ;
251 | imm32 r0, sockfd ;
252 | ldr r0, [r0] ;
253 | imm32 r1, client_addr_ip ; client_addr
254 | imm32 r2, sixteen ; addrlen
255 | imm32 r7, 0x11D ; ACCEPT(2)
256 | swi 0x0 ;
257 | ;
258 | teq r0, 0 ; check for errors
259 | bgt accept_ok ;
260 | if LOGGING2 ;
261 | mvn r0, r0 ;
262 | add r0, 1 ; change neg to pos number
263 | bl Convert2Hex ; Get error code
264 | print print_hex_string,print_hex_string_size ;
265 | print msgE4,msgE4.msgE4_size ; error message 1
266 | end if ;
267 | b accept_loop ; branch if neg or zero
268 | align 4 ;
269 | ;======================================================;
270 | ; RECV(2) ;
271 | ;------------------------------------------------------;
272 | ; The recv(2) and recvmsg() calls are used to receive ;
273 | ; messages from a socket, and may be used to receive ;
274 | ; data on a socket whether or not it is ;
275 | ; connection-oriented. ;
276 | ; ;
277 | ; If src_addr is not NULL, and the underlying protocol ;
278 | ; provides the source address, this source address is ;
279 | ; filled in. When src_addr is NULL, nothing is filled ;
280 | ; in, in this case, addrlen is not used, and should ;
281 | ; also be NULL. The argument addrlen is a value-result ;
282 | ; argument, which the caller should initialize before ;
283 | ; the call to the size of the buffer associated with ;
284 | ; src_addr, and modified on return to indicate the ;
285 | ; actual size of the source address. The returned ;
286 | ; address is truncated if the buffer provided is too ;
287 | ; small, ;
288 | ; in this case, addrlen will return a value ;
289 | ; greater than was supplied to the call. ;
290 | ;------------------------------------------------------;
291 | accept_ok: ;
292 | imm32 r1, clientfd ;
293 | str r0, [r1] ;
294 | recv_loop: ;
295 | imm32 r0, clientfd ;
296 | ldr r0, [r0] ; client FD
297 | imm32 r1, buffer ; buffer
298 | imm32 r2, bufferlen ; buffer size
299 | imm32 r3, 0 ; No options
300 | imm32 r7, 0x123 ; RECV(2)
301 | swi 0x0 ;
302 | ;
303 | teq r0, 0 ; check for errors
304 | blt recv_loop ;
305 | imm32 r1, recv_size ; get size of message
306 | str r0, [r1] ; store it
307 | ;======================================================;
308 | ; if logging2 then print buffer ;
309 | ;------------------------------------------------------;
310 | if LOGGING2 ;
311 | print buffer, bufferlen ;
312 | ;======================================================;
313 | ; Next line ;
314 | ;------------------------------------------------------;
315 | print NextLine, NextLine.NextLine_size ;
316 | ;
317 | end if ;
318 | ;======================================================;
319 | ; test for file ;
320 | ;------------------------------------------------------;
321 | imm32 r0, buffer ;
322 | ldr r2, [r0], 4 ;
323 | imm32 r3, 'GET ' ;
324 | cmp r2, r3 ;
325 | bne close ;
326 | eor r1, r1 ;
327 | ldrb r2, [r0], 1 ;
328 | cmp r2, '/' ;
329 | bne close ;
330 | filewantedloop: ;
331 | ldrb r2, [r0], 1 ;
332 | cmp r2, ' ' ;
333 | beq ItsSpace ;
334 | ;
335 | imm32 r3, bufferwantedfile ;
336 | strb r2, [r3,r1] ;
337 | add r1, 1 ;
338 | b filewantedloop ;
339 | ItsSpace: ;
340 | imm32 r3, bufferwantedfile ;
341 | cmp r1,0 ;
342 | bne Notjustspace ;
343 | imm32 r6,"inde" ;
344 | str r6,[r3],4 ;
345 | imm32 r6,"x.ht" ;
346 | str r6,[r3],4 ;
347 | imm32 r6,"m" ;
348 | strb r6,[r3] ;
349 | imm32 r3, bufferwantedfile ;
350 | mov r1,9 ;
351 | Notjustspace: ;
352 | mov r4, 0 ;
353 | strb r4, [r3,r1] ;
354 | imm32 r4, offset ;
355 | str r1, [r4] ;
356 | ;======================================================;
357 | ; Is logging enabled ? ;
358 | ;------------------------------------------------------;
359 | ; If logging has been enabled, itll use the function ;
360 | ; called ntop (at the bottom of this code) to convert ;
361 | ; the IP address of the person who connected to the ;
362 | ; server into a string form and print it. ;
363 | ;------------------------------------------------------;
364 | if LOGGING ;
365 | imm32 r2, client_addr_ip ;
366 | ldr r0, [r2, 4] ; mov eax, dword [peeraddr+4]
367 | imm32 r3, client_addr_ip_Converted ; mov edi, buffer+bufferlen-16
368 | bl ntop ; call ntop
369 | print_reg client_addr_ip_Converted ;
370 | end if ;
371 | ;======================================================;
372 | ; get file info ;
373 | ;------------------------------------------------------;
374 | fileisnamed: ;
375 | mov r7, 106 ;
376 | imm32 r0, bufferwantedfile ;
377 | imm32 r1, stat ;
378 | swi 0x0 ;
379 | cmp r0, 0 ;
380 | beq openfile ;
381 | ;
382 | ;======================================================;
383 | ; file not found ;
384 | ;------------------------------------------------------;
385 | tcpsend h404,h404.len ;
386 | ;======================================================;
387 | ; if logging print 404 message ;
388 | ;------------------------------------------------------;
389 | if LOGGING ;
390 | print l404,l404.len ;
391 | ;======================================================;
392 | ; Print wanted file name ;
393 | ;------------------------------------------------------;
394 | imm32 r2, offset ;
395 | ldr r2, [r2] ;
396 | mov r7, 0x4 ;
397 | mov r0, 0x1 ;
398 | imm32 r1, bufferwantedfile ;
399 | ;
400 | swi 0x0 ;
401 | ;======================================================;
402 | ; Next line ;
403 | ;------------------------------------------------------;
404 | print NextLine, NextLine.NextLine_size ;
405 | end if ;
406 | b close ;
407 | ;
408 | ;======================================================;
409 | ; Try to open file ;
410 | ;------------------------------------------------------;
411 | openfile: ;
412 | imm32 r4, stat ;
413 | ldr r0, [r4,20] ;
414 | imm32 r5, fdlen ;
415 | str r0, [r5] ;
416 | mov r7, 5 ;
417 | imm32 r0, bufferwantedfile ;
418 | mov r1 ,0 ;
419 | mov r2 ,0 ;
420 | swi 0x0 ;
421 | teq r0, 0 ; check for errors
422 | bgt fd_ok ;
423 | ;======================================================;
424 | ; Error when opeing file ;
425 | ;------------------------------------------------------;
426 | mvn r0, r0 ;
427 | add r0, 1 ; change neg to pos number
428 | bl Convert2Hex ;
429 | print print_hex_string, print_hex_string_size
430 | print fderror,fderror_len ;
431 | b close ;
432 | ;======================================================;
433 | ; file found and ready to send ;
434 | ;------------------------------------------------------;
435 | fd_ok: ;
436 | imm32 r1, fd ;
437 | str r0, [r1] ;
438 | ;======================================================;
439 | ; if logging, print 200 OK message ;
440 | ;------------------------------------------------------;
441 | if LOGGING ;
442 | print l200,l200.len ;
443 | ;======================================================;
444 | ; Print wanted file name ;
445 | ;------------------------------------------------------;
446 | imm32 r2, offset ;
447 | ldr r2, [r2] ;
448 | mov r7, 0x4 ;
449 | mov r0, 0x1 ;
450 | imm32 r1, bufferwantedfile ;
451 | ;
452 | swi 0x0 ;
453 | ;======================================================;
454 | ; Next line ;
455 | ;------------------------------------------------------;
456 | print NextLine, NextLine.NextLine_size ;
457 | end if ;
458 | ;======================================================;
459 | ; set header (eg: file size) ;
460 | ;------------------------------------------------------;
461 | imm32 r0, fdlen ;
462 | ldr r0, [r0] ;
463 | imm32 r10, c_l ;
464 | bl IntToStr ;
465 | mov r1, 13 ;
466 | strb r1, [r10],1 ;
467 | mov r1, 10 ;
468 | strb r1, [r10],1 ;
469 | mov r1, 13 ;
470 | strb r1, [r10],1 ;
471 | mov r1, 10 ;
472 | strb r1, [r10],1 ;
473 | ;
474 | imm32 r1, c_l ;
475 | sub r10, r1 ;
476 | imm32 r0, h200.len ;
477 | add r0, r10 ;
478 | ; print NextLine, NextLine.NextLine_size ;
479 | ; print_reg h200 ;, h200.len ; temp
480 | ; print NextLine, NextLine.NextLine_size ;
481 | ;======================================================;
482 | ; send header ;
483 | ;------------------------------------------------------;
484 | tcpsend_reg h200 ;
485 | ;======================================================;
486 | ; Send file ;
487 | ;------------------------------------------------------;
488 | mov r7, 187 ;
489 | imm32 r0, clientfd ;
490 | ldr r0, [r0] ;
491 | imm32 r1, fd ;
492 | ldr r1, [r1] ;
493 | mov r2, 0 ;
494 | imm32 r3, fdlen ;
495 | ldr r3, [r3] ;
496 | mov r4, 0 ;
497 | swi 0x0 ;
498 | ;
499 | align 4 ;
500 | ;======================================================;
501 | ; Close socket and wait ;
502 | ;------------------------------------------------------;
503 | close: ;
504 | mov r7, 6 ;
505 | imm32 r0, fd ;
506 | ldr r0, [r0] ;
507 | swi 0x0 ;
508 | mov r7, 6 ; CLOSE(2)
509 | imm32 r0, clientfd ;
510 | ldr r0, [r0] ;
511 | swi 0x0 ;
512 | imm32 r0,buffer ;
513 | eor r1,r1 ;
514 | imm32 r2,bufferlen ;
515 | mov r2,r2, lsr 2 ;
516 | align 4 ;
517 | ClearBuffLoop: ;
518 | str r1,[r0],4 ;
519 | subs r2,r2,1 ;
520 | bne ClearBuffLoop ;
521 | b infinity ;
522 | align 4 ;
523 | ;======================================================;
524 | ; Close server and exit ;
525 | ;------------------------------------------------------;
526 | fin: ; exit
527 | print msgclose,msgclose.msgclose_size ;
528 | mov r0, 0x0 ;
529 | mov r7, 0x1 ;
530 | swi 0x0 ;
531 | ;
532 | ;======================================================;
533 | ; Converts to hex ;
534 | ; r0 = number ( prints 8 hex digits) ;
535 | ;------------------------------------------------------;
536 | align 4 ;
537 | Convert2Hex: ;
538 | stmfd sp!, {r0-r12, lr} ;
539 | imm32 r2,hex_digits ;
540 | imm32 r3,print_hex_string ;
541 | mov r4,28 ;
542 | align 4 ;
543 | print_hex_loop: ;
544 | mov r1,r0,lsr r4 ; Get digit n
545 | and r1,r1,0x0f ; mask off lower nibble
546 | ldrb r1,[r2,r1] ; r0 now contains a hex number,
547 | ; look it up in table
548 | strb r1,[r3],1 ;
549 | subs r4,r4,4 ;
550 | bpl print_hex_loop ;
551 | ;
552 | ldmfd sp!, {r0-r12, pc} ; pop & return
553 | align 4 ;
554 | ;======================================================;
555 | ; String to int ;
556 | ; input: r3 = buffer, r4 = base (eg 10) ;
557 | ; output: r1 = number ;
558 | ;------------------------------------------------------;
559 | StrToInt: ;
560 | stmfd sp!, {r2-r3, lr} ; save regs
561 | eor r1, r1 ; zero r1 and r2
562 | eor r2, r2 ;
563 | .loop: ;
564 | ldrb r2, [r3], 1 ; load a byte
565 | cmp r2, 0 ; check for 0
566 | beq .end ; if so exit
567 | mul r1, r1, r4 ; multiply with basic
568 | sub r2, r2, '0' ; sub ASCII value
569 | cmp r2, 9 ; cmpare it to 9
570 | ble .ok ; if its let or = jump to OK
571 | sub r2, 7 ; if not sub 7 (eg: it A-F)
572 | .ok: ;
573 | add r1, r1, r2 ; save it
574 | b .loop ; do another loop .loop
575 | .end: ;
576 | ldmfd sp!, {r2-r3, pc} ; restore regs and return.
577 | align 4 ;
578 | ;======================================================;
579 | ; write number to buffer ;
580 | ;------------------------------------------------------;
581 | ; r10 = buffer address ;
582 | ;------------------------------------------------------;
583 | number2buffer: ;
584 | stmfd sp!, {lr} ;
585 | strb r0, [r10],1 ;
586 | ldmfd sp!, {pc} ;
587 | align 4 ;
588 | ;======================================================;
589 | ; Int To String ;
590 | ;------------------------------------------------------;
591 | ; input: r0 = number ;
592 | ; r10 = buffer ;
593 | ; ;
594 | ; output: r10 = buffer + offset ;
595 | ;------------------------------------------------------; ;
596 | IntToStr: ;
597 | stmfd sp!, {r0-r9, lr} ; save regs
598 | imm32 r8,prt_digits ;
599 | ;
600 | imm32 r6,0 ; current character to print
601 | mov r5,9 ; digits - 1 (max pos value 2147483647) (31 bits)
602 | imm32 r9,0 ;
603 | ;
604 | ;
605 | cmp r0,0 ;
606 | beq zeronum ;
607 | and r7,r0,$80000000 ; test bit 31 of r0
608 | cmp r7,0 ;
609 | beq posnum ; go to posnum if result equals zero (positive)
610 | ;
611 | imm32 r2,$FFFFFFFF ;
612 | eor r0,r0,r2 ; invert all bits of r0 (convert to positive)
613 | mov r2,r0 ; backup r0 value
614 | add r2,r2,1 ; add 1 to (now positive) value
615 | imm32 r0,"-" ;
616 | bl number2buffer ; print negative (-) sign
617 | mov r0,r2 ; restore r0 value
618 | ;
619 | posnum: ;
620 | imm32 r4,1000000000 ;
621 | subs r1,r0,r4 ; subtract r4 (power of 10 result) from starting value, store in r1
622 | b Printloop ;
623 | ;
624 | align 4 ;
625 | mainloop: ;
626 | imm32 r6,0 ; clear r6
627 | mov r2,r5 ; back up value of r5
628 | b pow10 ; lookup power of 10 from r5
629 | p10ret: ;
630 | mov r5,r2 ; restore r5 value
631 | subs r1,r1,r4 ; subtract r4 (power of 10 result) from starting value, store in r1
632 | ;
633 | Printloop: ;
634 | ;print "At Printloop" ;
635 | ;sync ;
636 | bpl notmatched ; go to notmatched if result (r1) is not negative
637 | add r1,r1,r4 ; add power of 10 value to r1
638 | cmp r6,0 ;
639 | blt nodigit ; if r6 is negative, reset to 0
640 | b numfix ;
641 | notmatched: ;
642 | ;-print "at notmatched" ;
643 | add r6,r6,1 ; increment current character by 1
644 | subs r1,r1,r4 ; subtract pow10 value from r1
645 | b Printloop ;
646 | numfix: ;
647 | mov r3,r6 ; update r3 from r6
648 | ldrb r3,[r8,r3] ; r3 now contains a number (ascii)
649 | mov r0,r3 ; move ascii character to r0
650 | cmp r9,0 ; if r9 does not equal zero,
651 | bne allowprt ; go to allowprt (allow printing)
652 | cmp r0,'0' ; if r0 equals ascii zero,
653 | beq noprint ; go to noprint (disable printing leading zeros)
654 | imm32 r9,1 ; set r9 to 1 (allow printing zeros)
655 | ;
656 | allowprt: ;
657 | bl number2buffer ; print character to buffer
658 | noprint: ;
659 | subs r5,r5,1 ; decrement r5 (power of 10 mult) by 1
660 | mov r0,r1 ; replace r0 with r1
661 | bpl mainloop ; go to mainloop if r5 is not negative
662 | ;sync ;
663 | ;-end ;
664 | finish: ;
665 | ;print "Done printing" ;
666 | ;sync ;
667 | ldmfd sp!, {r0-r9, pc} ; restore regs and return.
668 | zeronum: ;
669 | imm32 r0,"0" ;
670 | bl number2buffer ; print negative (-) sign
671 | b finish ;
672 | ;
673 | ;
674 | nodigit: ;
675 | imm32 r6,0 ;
676 | b numfix ;
677 | ;
678 | pow10: ; calculate 10^x (r5 value) ;
679 | ;r4 will contain result, r5 specifies power of 10 to calc
680 | imm32 r4,10 ;
681 | cmp r5,1 ;
682 | bgt p10mul ;
683 | blt p10p1 ;
684 | imm32 r4,10 ;
685 | b p10ret ;
686 | p10p1: ;
687 | imm32 r4,1 ;
688 | b p10ret ;
689 | p10mul: ;
690 | sub r5,r5,1 ;
691 | p10loop: ;
692 | imm32 r7,10 ;
693 | mul r4,r4,r7 ;
694 | sub r5,r5,1 ;
695 | cmp r5,0 ;
696 | bgt p10loop ;
697 | b p10ret ;
698 | align 4 ;
699 | ;======================================================;
700 | ; ntop ;
701 | ; r0 = network-order address, r3 = buffer ;
702 | ; returns: r10 = buffer + offset ;
703 | ;------------------------------------------------------;
704 | ntop: ;
705 | ; eax = network-order address, edi = buffer ;
706 | ; returns: ecx = length ;
707 | stmfd sp!, {r1-r12, lr} ; save regs push ebx edx ebp
708 | mov r10,r3 ;
709 | mov r6, r3 ;
710 | mov r1, r0 ;
711 | imm32 r2, 0x000000FF ;
712 | and r0, r2 ;
713 | mov r4, 2 ;
714 | ;num4 ;
715 | bl IntToStr ;
716 | imm32 r2, '.' ;
717 | strb r2, [r10],1 ;
718 | ;num3 ;
719 | mov r0, r1 ;
720 | imm32 r2, 0x0000FF00 ;
721 | and r0, r2 ;
722 | mov r0, r0, lsr 8 ;
723 | mov r4, 2 ;
724 | bl IntToStr ;
725 | imm32 r2, '.' ;
726 | strb r2, [r10],1 ;
727 | ;num2 ;
728 | mov r0, r1 ;
729 | imm32 r2, 0x00FF0000 ;
730 | and r0, r2 ;
731 | mov r0, r0, lsr 16 ;
732 | mov r4, 2 ;
733 | bl IntToStr ;
734 | imm32 r2, '.' ;
735 | strb r2, [r10],1 ;
736 | ; num1 ;
737 | mov r0, r1 ;
738 | mov r0, r0, lsr 24 ;
739 | mov r4, 2 ;
740 | bl IntToStr ;
741 | ;
742 | sub r10, r6 ;
743 | mov r0, r10 ;
744 | ldmfd sp!, {r1-r12, pc} ; restore regs and return.ret
745 |
746 | ; ---------------------------------------------------- ;
747 | ; OutChar ( print 1 character to stdout ) ;
748 | ; ---------------------------------------------------- ;
749 | ; put char = r0 ;
750 | ; ---------------------------------------------------- ;
751 | OutChar: ;
752 | stmfd sp!, {r0-r2, r7, lr} ; Store registers
753 | mov r1, sp ; r1 address
754 | mov r0, 1 ; r0 stdout
755 | mov r2, r0 ; r2 length
756 | mov r7, 4 ;
757 | swi 0 ;
758 | ldmfd sp!, {r0-r2, r7, pc} ; Restore registers and return
759 | ;======================================================;
760 | ; data ;
761 | ;------------------------------------------------------;
762 | segment readable writeable ;
763 | if LOGGING ;
764 | align 4 ;
765 | l404 db ' - 404 NF - ' ;
766 | .len = $ - l404 ;
767 | align 4 ;
768 | l200 db ' - 200 OK - ' ;
769 | .len = $ - l200 ;
770 | end if ;
771 | align 4 ;
772 | ipnumber dw 0xfefefefe ;
773 | align 4 ;
774 | prt_digits: ;
775 | db "0123456789" ;
776 | align 4 ;
777 | hex_digits: ;
778 | db "0123456789ABCDEF" ;
779 | align 4 ;
780 | print_hex_string: ;
781 | db "12345678", 0xa ; storage for 8 digit hex string,
782 | print_hex_string_size = $-print_hex_string ;
783 | align 4 ;
784 | client_addr_ip: rb 16 ;
785 | sixteen dw 16 ;
786 | align 4 ;
787 | sockaddr_in: ;
788 | .sin_family dh AF_INET ;
789 | .sin_port dh 0 ;
790 | .sin_addr dw 0 ;
791 | .sin_zero rb 8 ;
792 | dest_size = $-sockaddr_in ;
793 | align 4 ;
794 | client_addr dw sockaddr_in ;
795 | addrlen dw dest_size ;
796 | align 4 ;
797 | argc dw 0 ;
798 | portnumber dw 0 ;
799 | align 4 ;
800 | recv_size dw 0 ;
801 | sockfd dw 0 ;
802 | fd dw 0 ;
803 | fdlen dw 0 ;
804 | clientfd dw 0 ;
805 | offset dw 0 ;
806 | align 4 ;
807 | cArray dw 0 ;
808 | dw 0 ;
809 | dw 0 ;
810 | dw 0 ;
811 | dw 0 ;
812 | align 4 ;
813 | ;send 'array'. ;
814 | sArray dw 0 ;
815 | dw 0 ;
816 | dw 0 ;
817 | dw 0 ;
818 | align 4 ;
819 | indexfile db ".index.html",0 ;
820 | align 4 ;
821 | NextLine db " " ,0xa ; Next line
822 | .NextLine_size = $-NextLine ;
823 | align 4 ;
824 | msgreceved db " Server receved data" ,0xa ; Message
825 | .msgreceved_size = $-msgreceved ;
826 | align 4 ;
827 | msg db " Server started..." ,0xa ; Message
828 | .msg_size = $-msg ;
829 | align 4 ;
830 | msgclose db " Server closed", 0xa ; Close
831 | .msgclose_size = $-msgclose ;
832 | align 4 ;
833 | msgsent db " Sent file", 0xa ; Send
834 | .msgsent_size = $-msgsent ;
835 | ;
836 | msgE1 db " socket() failed" ,0xa ; Err1
837 | .msgE1_size = $-msgE1 ;
838 | align 4 ;
839 | msgE2 db " bind() failed", 0xa ; Err2
840 | .msgE2_size = $-msgE2 ;
841 | align 4 ;
842 | msgE3 db " listen() failed", 0xa ; Err3
843 | .msgE3_size = $-msgE3 ;
844 | align 4 ;
845 | msgE4 db " accept() failed", 0xa ; Err4
846 | .msgE4_size = $-msgE4 ;
847 | align 4 ;
848 | msgE5 db " send() failed", 0xa ; Err5
849 | .msgE5_size = $-msgE5 ;
850 | align 4 ;
851 | fderror db " ERROR 1", 0xa ;
852 | fderror_len = $- fderror ;
853 | align 4 ;
854 | fderror2 db " ERROR 2", 0xa ;
855 | fderror_len2 = $- fderror2 ;
856 | align 4 ;
857 | h404 db 'HTTP/1.0 404 Not Found',13,10,13,10,'404 Not FoundFile Not Found
',10,13
858 | .len = $ - h404 ;
859 | align 4 ;
860 | h200 db 'HTTP/1.0 200 OK',13,10 ;
861 | db 'Server: DexServer',13,10 ;
862 | db 'Content-Length: ' ;
863 | .len = $ - h200 ;
864 | align 4 ;
865 | c_l db ' ';
866 | align 4 ;
867 | stat rb 88 ;
868 | align 4 ;
869 | buffer rb 512 ;
870 | bufferlen = $ - buffer ;
871 | align 4 ;
872 | bufferwantedfile rb 512 ;
873 | align 4 ;
874 | client_addr_ip_Converted rb 16 ;
875 | align 4 ;
876 |
--------------------------------------------------------------------------------
/start-webservers.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | DOCKER_IMAGE="hypriot/rpi-nano-httpd"
5 | P1=$1
6 | P2=$2
7 | MAXNR=${P1:="1"}
8 | STARTNR=${P2:="0"}
9 |
10 | COUNTER=$STARTNR
11 | while [ $COUNTER -lt $MAXNR ]; do
12 | let COUNTER=COUNTER+1
13 | let PORT=10000+COUNTER
14 | echo COUNTER=$COUNTER, PORT=$PORT
15 | docker run -d --name=WebServer-$PORT -p $PORT:80 $DOCKER_IMAGE
16 | done
17 |
--------------------------------------------------------------------------------