quick commit 2
[webserver.git] / webserver.c
1 /* depend: */
2 /* cflags: */
3 /* linker: color.o debug.o file.o http.o server.o */
4
5 #include <assert.h>
6 #include <dirent.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10
11 #include "debug.h"
12 #include "http.h"
13 #include "server.h"
14
15 /* types */
16
17 /* constants */
18
19 #define BUFFER_SIZE 4096
20 #define ROOT_DIR "webroot"
21
22 /* macros */
23
24 /* gobal variables */
25
26 char *progname = NULL;
27 int port = 8080;
28 char *root = ROOT_DIR;
29
30 /* help function */
31
32 int usage (int ret)
33 {
34 FILE *fid = ret ? stderr : stdout;
35 fprintf (fid, "usage: %s\n", progname);
36 fprintf (fid, " -h : help message\n");
37 fprintf (fid, " -p : port number (%d)\n", port);
38 fprintf (fid, " -r : web root directory (%s)\n", root);
39 fprintf (fid, " -v : verbose level (%d)\n", verbose);
40
41 return ret;
42 }
43
44 /* main function */
45
46 int main (int argc, char *argv[])
47 {
48 int i = 0;
49
50 /* program name */
51
52 progname = argv[0];
53 while (progname[i] != '\0') {
54 if ((progname[i] == '/') || (progname[i] == '\\')) {
55 progname += i + 1;
56 i = 0;
57 } else {
58 i++;
59 }
60 }
61
62 /* argument processing */
63
64 while (argc-- > 1) {
65 char *arg = *(++argv);
66 if (arg[0] != '-') {
67 VERBOSE (ERROR, PERROR ("%s: invalid option -- '%s'\n", progname, arg); usage (1));
68 return 1;
69 }
70 char c = arg[1];
71 switch (c) {
72 case 'p':
73 arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
74 if (arg == NULL) {
75 VERBOSE (ERROR, PERROR ("%s: missing port number\n", progname); usage (1));
76 return 1;
77 }
78 port = atoi (arg);
79 if (port <= 0) {
80 VERBOSE (ERROR, PERROR ("%s: incorrect port number (%s)\n", progname, arg); usage (1));
81 return 1;
82 }
83 break;
84 case 'r':
85 arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
86 if (arg == NULL) {
87 VERBOSE (ERROR, PERROR ("%s: missing directory name\n", progname); usage (1));
88 return 1;
89 }
90 root = arg;
91 break;
92 case 'v':
93 arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
94 if (arg == NULL) {
95 VERBOSE (ERROR, PERROR ("%s: missing verbose level\n", progname); usage (1));
96 return 1;
97 }
98 verbose = atoi (arg);
99 break;
100 case 'h':
101 default:
102 return usage (c != 'h');
103 }
104 }
105
106 /* check root directory */
107 VERBOSE (DEBUG, PRINT ("Check web root\n"));
108 DIR *pdir = opendir (root);
109 if (pdir == NULL) {
110 VERBOSE (ERROR, PERROR ("Can't read directory (%s)\n", root));
111 return 1;
112 }
113 closedir (pdir);
114
115 /* init network stack */
116 VERBOSE (DEBUG, PRINT ("Initializing socket\n"));
117 init_network_context ();
118 if (open_listening_socket (port) == 0) {
119 VERBOSE (ERROR, PERROR ("Can't open listening socket\n"));
120 return 1;
121 }
122 VERBOSE (INFO, PRINT ("Listening socket on port %d\n", port));
123
124 /* main loop */
125 while (1) {
126 if (accept_incoming_connection () == 0) {
127 usleep (1e5);
128 continue;
129 }
130
131 VERBOSE (DEBUG, PRINT ("Server connected, waiting for data\n"));
132
133 unsigned char *data = NULL;
134 unsigned char *output = NULL;
135
136 int len = receive_data (&data);
137 if (len == 0) {
138 VERBOSE (WARNING, PRINT ("Connection closed by peer (rx)\n"));
139 } else if (len < 0) {
140 VERBOSE (WARNING, PRINT ("Connection in error (rx)\n"));
141 } else {
142 VERBOSE (DEBUG, PRINT ("Received %d bytes\n", len));
143
144 // processing
145 VERBOSE (DEBUG, PRINT ("Processing %s\n", data));
146 len = processing ((char *)data, len, root, (char **)&output);
147
148 VERBOSE (DEBUG, PRINT ("Sending data (%d)\n%s\n", len, data));
149 int rc = send_data (output, len);
150 if (rc == 0) {
151 VERBOSE (WARNING, PRINT ("Connection closed by peer (tx)\n"));
152 } else if (rc < 0) {
153 VERBOSE (WARNING, PRINT ("Connection in error (tx)\n"));
154 }
155 }
156
157 VERBOSE (DEBUG, PRINT ("Closing connection\n"));
158 if (data) {
159 free (data);
160 }
161 if (output) {
162 free (output);
163 }
164 close_connection ();
165 }
166
167 VERBOSE (DEBUG, PRINT ("Closing socket\n"));
168 terminate_network_context ();
169
170 return 2;
171 }
172
173 // test: webserver.exe -h
174 // test: webserver.exe -h | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
175 // test: webserver.exe -_ 2> /dev/null | wc -l | xargs test 0 =
176 // test: webserver.exe -_ 2>&1 | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
177 // test: webserver.exe error 2>&1 | grep -q 'invalid option'
178 // test: webserver.exe -v 2>&1 | grep -q 'missing verbose level'
179 // test: webserver.exe -p 2>&1 | grep -q 'missing port number'
180 // test: webserver.exe -p -1 2>&1 | grep -q 'incorrect port number'
181 // test: webserver.exe > test.log & pid=$!; sleep 1; kill -QUIT $pid; grep -q 'Listening socket on port 8080' test.log
182 // test: webserver.exe -p 8000 > test.log & pid=$!; sleep 1; kill -ABRT $pid; grep -q 'Listening socket on port 8000' test.log
183 // test: webserver.exe & pid=$!; sleep 1; kill -TERM $pid; ps aux | grep -q [w]ebserver.exe && kill -9 $pid || rc=1; test x$rc = x1
184
185 /* vim: set ts=4 sw=4 et: */