IBM employee Nigel Griffiths published and regularly update an interesting article [1] on developerWorks about how a web server works. He actually wrote tiny web server named nweb in 200 lines C code and explains how each part of the code is used to serve the static content. Below is small quote from the article introduction.
Have you ever wanted to run a tiny, safe web server without worrying about using a fully blown web server that could be complex to install and configure? Do you wonder how to write a program that accepts incoming messages with a network socket? Have you ever just wanted your own Web server to experiment and learn with? Further updates in 2012 to support recent web-server and browser standards and a code refresh.
Well, look no further - nweb is what you need. This is a simple Web server that has only 200 lines of C source code. It runs as a regular user and can't run any server-side scripts or programs, so it can't open up any special privileges or security holes.
This article covers:
- What the nweb server program offers
- Summary of C functions features in the program
- Pseudo code to aid understanding of the flow of the code
- Network socket system calls used and other system calls
- How the client side operates
- C source code
At the bottom of the page You can download the nweb source code (file nweb23.c after extract), the code compiles and runs on the following systems:
- AIX 6.1 TL7 (POWER)
- Ubuntu 12.4 (amd64)
- Fedora 17 Linux (amd64)
- OpenSUSE 12.1 (amd64)
- Debian Squeeze (ARMv6/Raspberry Pi)
... but it will not compile on FreeBSD:
# gcc nweb23.c
nweb23.c: In function 'main':
nweb23.c:165: error: 'SIGCLD' undeclared (first use in this function)
nweb23.c:165: error: (Each undeclared identifier is reported only once
nweb23.c:165: error: for each function it appears in.)
nweb23.c:169: error: too few arguments to function 'setpgrp'
% clang nweb23.c
nweb23.c:165:15: error: use of undeclared identifier 'SIGCLD'
(void)signal(SIGCLD, SIG_IGN); /* ignore child death */
^
nweb23.c:169:16: error: too few arguments to function call, expected 2, have 0
(void)setpgrp(); /* break away from process group */
~~~~~~~ ^
/usr/include/unistd.h:455:1: note: 'setpgrp' declared here
int setpgrp(pid_t _pid, pid_t _pgrp); /* obsoleted by setpgid() */
^
2 errors generated.
To fix the undeclared SIGCLD error we need to add this after the #include (...) and #define (...) lines.
#ifndef SIGCLD
# define SIGCLD SIGCHLD
#endif
So this is what we changed so far comparing to the nweb23.o.c original source code file.
--- nweb23.o.c 2012-08-19 23:15:45.000000000 +0200
+++ nweb23.c 2013-03-12 14:07:16.021757627 +0100
@@ -15,6 +15,9 @@
#define LOG 44
#define FORBIDDEN 403
#define NOTFOUND 404
+#ifndef SIGCLD
+# define SIGCLD SIGCHLD
+#endif
struct {
char *ext;
Lets try the compilation now.
% gcc nweb23.c
nweb23.c: In function 'main':
nweb23.c:172: error: too few arguments to function 'setpgrp'
% clang nweb23.c
nweb23.c:172:16: error: too few arguments to function call, expected 2, have 0
(void)setpgrp(); /* break away from process group */
~~~~~~~ ^
/usr/include/unistd.h:455:1: note: 'setpgrp' declared here
int setpgrp(pid_t _pid, pid_t _pgrp); /* obsoleted by setpgid() */
^
1 error generated.
So we have one error left. The setpgrp(); function in the code is executed without arguments while it needs two setpgrp(pid_t _pid, pid_t _pgrp);, lets fix that. In line 172 we will put (void)setpgrp(getpid(),getpid()); instead of (void)setpgrp(); function.
% gcc nweb23.c
% echo $?
0
% clang nweb23.c
% echo $?
0
Viola! It now compiles, here is the diff(1) for both changes.
--- nweb23.o.c 2012-08-19 23:15:45.000000000 +0200
+++ nweb23.c 2013-03-12 14:20:10.561753772 +0100
@@ -15,6 +15,9 @@
#define LOG 44
#define FORBIDDEN 403
#define NOTFOUND 404
+#ifndef SIGCLD
+# define SIGCLD SIGCHLD
+#endif
struct {
char *ext;
@@ -166,7 +169,7 @@
(void)signal(SIGHUP, SIG_IGN); /* ignore terminal hangups */
for(i=0;i<32;i++)
(void)close(i); /* close open files */
- (void)setpgrp(); /* break away from process group */
+ (void)setpgrp(getpid(),getpid()); /* break away from process group */
logger(LOG,"nweb starting",argv[1],getpid());
/* setup the network socket */
if((listenfd = socket(AF_INET, SOCK_STREAM,0)) <0)
Lets now try how (and if) it works.
% ./a.out
hint: nweb Port-Number Top-Directory version 23
nweb is a small and very safe mini web server
nweb only servers out file/web pages with extensions named below
and only from the named directory or its sub-directories.
There is no fancy features = safe and secure.
Example: nweb 8181 /home/nwebdir &
Only Supports: gif jpg jpeg png ico zip gz tar htm html
Not Supported: URLs including "..", Java, Javascript, CGI
Not Supported: directories / /etc /bin /lib /tmp /usr /dev /sbin
No warranty given or implied
Nigel Griffiths nag@uk.ibm.com
The nweb server seems to work, lets try to serve the directory with extracted files.
% ./a.out 8080 ./
% echo $?
0
% ps ax | grep a.out
40755 7 S 0:00.00 ./a.out 8080 ./
% netstat -a -n -f inet | grep 8080
tcp4 0 0 *.8080 *.* LISTEN
It seems to work, lets try that in the browser.
I did not thought that I would ever quote the default Apache page but, "It works!".
[1] http://www.ibm.com/developerworks/systems/library/es-nweb/index.html