Merge branch 'master' of https://secure.softndesign.org/git/webserver
[webserver.git] / webserver.c
CommitLineData
8512671a
LM
1/* depend: */
2/* cflags: */
4baf6839 3/* linker: color.o debug.o file.o http.o server.o */
8512671a 4
184be781 5#include <assert.h>
4baf6839 6#include <dirent.h>
8512671a
LM
7#include <stdio.h>
8#include <stdlib.h>
89f0e084 9#include <unistd.h>
8512671a
LM
10
11#include "debug.h"
96748ca7 12#include "http.h"
00afbb65 13#include "server.h"
8512671a 14
184be781
LM
15/* types */
16
8512671a
LM
17/* constants */
18
19#define BUFFER_SIZE 4096
4baf6839 20#define ROOT_DIR "webroot"
2bec4aa7
LM
21#define SERVER_NAME "localhost"
22#define CHARSET "iso-8859-1"
8512671a
LM
23
24/* macros */
25
26/* gobal variables */
27
28char *progname = NULL;
21ac031b 29int port = 8080;
4baf6839 30char *root = ROOT_DIR;
2bec4aa7
LM
31char *servername = SERVER_NAME;
32char *charset = CHARSET;
8512671a
LM
33
34/* help function */
35
36int usage (int ret)
37{
38 FILE *fid = ret ? stderr : stdout;
39 fprintf (fid, "usage: %s\n", progname);
295b1e68 40 fprintf (fid, " -c : charset name (%s)\n", charset);
8512671a 41 fprintf (fid, " -h : help message\n");
21ac031b 42 fprintf (fid, " -p : port number (%d)\n", port);
4baf6839 43 fprintf (fid, " -r : web root directory (%s)\n", root);
295b1e68 44 fprintf (fid, " -s : server name (%s)\n", servername);
8512671a
LM
45 fprintf (fid, " -v : verbose level (%d)\n", verbose);
46
47 return ret;
48}
49
50/* main function */
51
52int main (int argc, char *argv[])
53{
54 int i = 0;
8512671a
LM
55
56 /* program name */
57
58 progname = argv[0];
59 while (progname[i] != '\0') {
60 if ((progname[i] == '/') || (progname[i] == '\\')) {
61 progname += i + 1;
62 i = 0;
63 } else {
64 i++;
65 }
66 }
67
68 /* argument processing */
69
4baf6839 70 while (argc-- > 1) {
8512671a
LM
71 char *arg = *(++argv);
72 if (arg[0] != '-') {
163f56b1 73 VERBOSE (ERROR, PERROR ("%s: invalid option -- '%s'\n", progname, arg); usage (1));
8512671a
LM
74 return 1;
75 }
76 char c = arg[1];
77 switch (c) {
295b1e68
LM
78 case 'c':
79 arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
80 if (arg == NULL) {
81 VERBOSE (ERROR, PERROR ("%s: missing charset name\n", progname); usage (1));
82 return 1;
83 }
84 charset = arg;
85 break;
86 case 'p':
8512671a
LM
87 arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
88 if (arg == NULL) {
163f56b1 89 VERBOSE (ERROR, PERROR ("%s: missing port number\n", progname); usage (1));
21ac031b
LM
90 return 1;
91 }
92 port = atoi (arg);
93 if (port <= 0) {
163f56b1 94 VERBOSE (ERROR, PERROR ("%s: incorrect port number (%s)\n", progname, arg); usage (1));
8512671a
LM
95 return 1;
96 }
8512671a 97 break;
295b1e68
LM
98 case 's':
99 arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
100 if (arg == NULL) {
101 VERBOSE (ERROR, PERROR ("%s: missing server name\n", progname); usage (1));
102 return 1;
103 }
104 servername = arg;
105 break;
4baf6839
LM
106 case 'r':
107 arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
108 if (arg == NULL) {
109 VERBOSE (ERROR, PERROR ("%s: missing directory name\n", progname); usage (1));
110 return 1;
111 }
112 root = arg;
113 break;
8512671a
LM
114 case 'v':
115 arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
116 if (arg == NULL) {
163f56b1 117 VERBOSE (ERROR, PERROR ("%s: missing verbose level\n", progname); usage (1));
8512671a
LM
118 return 1;
119 }
120 verbose = atoi (arg);
121 break;
122 case 'h':
123 default:
124 return usage (c != 'h');
125 }
126 }
127
4baf6839
LM
128 /* check root directory */
129 VERBOSE (DEBUG, PRINT ("Check web root\n"));
130 DIR *pdir = opendir (root);
131 if (pdir == NULL) {
132 VERBOSE (ERROR, PERROR ("Can't read directory (%s)\n", root));
133 return 1;
134 }
135 closedir (pdir);
136
2bec4aa7
LM
137 /* configuration */
138 conf_t conf = {root, servername, charset};
139
4baf6839 140 /* init network stack */
163f56b1 141 VERBOSE (DEBUG, PRINT ("Initializing socket\n"));
89f0e084 142 init_network_context ();
7d4815a1 143 if (open_listening_socket (port) == 0) {
163f56b1 144 VERBOSE (ERROR, PERROR ("Can't open listening socket\n"));
184be781
LM
145 return 1;
146 }
163f56b1
LM
147 VERBOSE (INFO, PRINT ("Listening socket on port %d\n", port));
148
04a2223d
LM
149 /* main loop */
150 while (1) {
7d4815a1 151 if (accept_incoming_connection () == 0) {
89f0e084 152 usleep (1e5);
04a2223d
LM
153 continue;
154 }
155
163f56b1 156 VERBOSE (DEBUG, PRINT ("Server connected, waiting for data\n"));
04a2223d 157
135b5dee
LM
158 char *data = NULL;
159 char *output = NULL;
04a2223d 160
7d4815a1 161 int len = receive_data (&data);
89f0e084 162 if (len == 0) {
163f56b1 163 VERBOSE (WARNING, PRINT ("Connection closed by peer (rx)\n"));
89f0e084 164 } else if (len < 0) {
163f56b1 165 VERBOSE (WARNING, PRINT ("Connection in error (rx)\n"));
89f0e084 166 } else {
96748ca7 167 VERBOSE (DEBUG, PRINT ("Received %d bytes\n", len));
04a2223d
LM
168
169 // processing
96748ca7 170 VERBOSE (DEBUG, PRINT ("Processing %s\n", data));
2bec4aa7 171 len = processing (data, len, &conf, &output);
89f0e084 172
6add28e9
LM
173 VERBOSE (DEBUG, PRINT ("Sending data (%d)\n%s\n", len, data));
174 int rc = send_data (output, len);
89f0e084 175 if (rc == 0) {
163f56b1 176 VERBOSE (WARNING, PRINT ("Connection closed by peer (tx)\n"));
89f0e084 177 } else if (rc < 0) {
163f56b1 178 VERBOSE (WARNING, PRINT ("Connection in error (tx)\n"));
04a2223d 179 }
89f0e084 180 }
04a2223d 181
163f56b1 182 VERBOSE (DEBUG, PRINT ("Closing connection\n"));
89f0e084
LM
183 if (data) {
184 free (data);
04a2223d 185 }
6add28e9
LM
186 if (output) {
187 free (output);
188 }
7d4815a1 189 close_connection ();
04a2223d 190 }
89f0e084 191
163f56b1 192 VERBOSE (DEBUG, PRINT ("Closing socket\n"));
89f0e084 193 terminate_network_context ();
184be781 194
06ec8057 195 return 2;
8512671a
LM
196}
197
198// test: webserver.exe -h
199// test: webserver.exe -h | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
21ac031b 200// test: webserver.exe -_ 2> /dev/null | wc -l | xargs test 0 =
8512671a
LM
201// test: webserver.exe -_ 2>&1 | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
202// test: webserver.exe error 2>&1 | grep -q 'invalid option'
21ac031b 203// test: webserver.exe -v 2>&1 | grep -q 'missing verbose level'
295b1e68 204// test: webserver.exe -c 2>&1 | grep -q 'missing charset name'
21ac031b
LM
205// test: webserver.exe -p 2>&1 | grep -q 'missing port number'
206// test: webserver.exe -p -1 2>&1 | grep -q 'incorrect port number'
295b1e68
LM
207// test: webserver.exe -s 2>&1 | grep -q 'missing server name'
208// test: webserver.exe -r 2>&1 | grep -q 'missing directory name'
0e6fd2e9
LM
209// test: webserver.exe >& test.log & pid=$!; sleep 1; kill -ABRT $pid; sleep 1; grep -q 'Listening socket on port 8080' test.log
210// test: webserver.exe -p 8000 >& test.log & pid=$!; sleep 1; kill -TERM $pid; sleep 1; grep -q 'Listening socket on port 8000' test.log
211// test: webserver.exe -p 8001 -c iso-8859-1 -r webroot -s localhost >&/dev/null & pid=$!; sleep 1; curl http://localhost:8001/index.html > test.log; kill -TERM $pid; sleep 1; grep -q '<title>Test</title>' test.log
212// test: webserver.exe -v 3 -p 8002 >&/dev/null & pid=$!; sleep 1; curl -v http://localhost:8002/index.html >& test.log; kill -TERM $pid; sleep 1; grep -q 200 test.log
213// test: webserver.exe -v 3 -p 8003 >&/dev/null & pid=$!; sleep 1; curl -v http://localhost:8003/not_found.html >& test.log; kill -TERM $pid; sleep 1; grep -q 404 test.log
214// test: webserver.exe -v 3 -p 8004 >&/dev/null & pid=$!; sleep 1; curl -v -I http://localhost:8004/index.html >& test.log; kill -TERM $pid; sleep 1; grep -q Content-Length test.log; test $? -eq 1
215// test: webserver.exe -v 3 -p 8005 >&/dev/null & pid=$!; sleep 1; curl -v -d '' http://localhost:8005/index.html >& test.log; kill -TERM $pid; sleep 1; grep -q '<title>Test</title>' test.log
8512671a
LM
216
217/* vim: set ts=4 sw=4 et: */