quick commit
[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 = {0};
134
135 int len = receive_data (&data);
136 if (len == 0) {
137 VERBOSE (WARNING, PRINT ("Connection closed by peer (rx)\n"));
138 } else if (len < 0) {
139 VERBOSE (WARNING, PRINT ("Connection in error (rx)\n"));
140 } else {
141 VERBOSE (DEBUG, PRINT ("Received %d bytes\n", len));
142
143 // processing
144 VERBOSE (DEBUG, PRINT ("Processing %s\n", data));
145 len = processing ((char *)data, len, root, (char **)&data);
146
147 int rc = send_data (data, len);
148 if (rc == 0) {
149 VERBOSE (WARNING, PRINT ("Connection closed by peer (tx)\n"));
150 } else if (rc < 0) {
151 VERBOSE (WARNING, PRINT ("Connection in error (tx)\n"));
152 }
153 }
154
155 VERBOSE (DEBUG, PRINT ("Closing connection\n"));
156 if (data) {
157 free (data);
158 }
159 close_connection ();
160 }
161
162 VERBOSE (DEBUG, PRINT ("Closing socket\n"));
163 terminate_network_context ();
164
165 return 2;
166 }
167
168 // test: webserver.exe -h
169 // test: webserver.exe -h | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
170 // test: webserver.exe -_ 2> /dev/null | wc -l | xargs test 0 =
171 // test: webserver.exe -_ 2>&1 | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
172 // test: webserver.exe error 2>&1 | grep -q 'invalid option'
173 // test: webserver.exe -v 2>&1 | grep -q 'missing verbose level'
174 // test: webserver.exe -p 2>&1 | grep -q 'missing port number'
175 // test: webserver.exe -p -1 2>&1 | grep -q 'incorrect port number'
176 // test: webserver.exe > test.log & pid=$!; sleep 1; kill -QUIT $pid; grep -q 'Listening socket on port 8080' test.log
177 // test: webserver.exe -p 8000 > test.log & pid=$!; sleep 1; kill -ABRT $pid; grep -q 'Listening socket on port 8000' test.log
178 // 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
179
180 /* vim: set ts=4 sw=4 et: */