adapt to linux
[webserver.git] / webserver.c
1 /* depend: */
2 /* cflags: */
3 /* linker: color.o debug.o */
4
5 #include <assert.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #ifdef _WIN32 /* Windows */
9 #include <winsock2.h>
10 #include <ws2tcpip.h>
11 #else /* Posix */
12 #include <errno.h>
13 #include <unistd.h>
14 #include <fcntl.h>
15 #include <netinet/ip.h>
16 #include <netinet/tcp.h>
17 #include <sys/types.h>
18 #include <sys/socket.h>
19 #endif
20
21 #include "debug.h"
22
23 /* types */
24
25 #ifdef _WIN32 /* Windows */
26 typedef SOCKET socket_t;
27 #else /* Posix */
28 typedef int socket_t;
29 #endif
30
31 /* constants */
32
33 #define BUFFER_SIZE 4096
34
35 /* macros */
36
37 /* gobal variables */
38
39 char *progname = NULL;
40 int port = 8080;
41
42 /* help function */
43
44 int usage (int ret)
45 {
46 FILE *fid = ret ? stderr : stdout;
47 fprintf (fid, "usage: %s\n", progname);
48 fprintf (fid, " -h : help message\n");
49 fprintf (fid, " -p : port number (%d)\n", port);
50 fprintf (fid, " -v : verbose level (%d)\n", verbose);
51
52 return ret;
53 }
54
55 /* open listening socket */
56
57 socket_t open_listening_socket (int port)
58 {
59 #ifdef _WIN32 /* Windows */
60 WSADATA WSAData;
61 WSAStartup (MAKEWORD(2,0), &WSAData);
62 assert (INVALID_SOCKET == (socket_t)-1);
63 #endif
64
65 VERBOSE (DEBUG, fprintf (stdout, "Opening socket\n"));
66 #ifdef _WIN32 /* Windows */
67 socket_t sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
68 if (sock == INVALID_SOCKET)
69 return -1;
70 #else /* Posix */
71 socket_t sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
72 if (sock < 0)
73 return -1;
74 #endif
75
76 struct sockaddr_in addr = {0};
77 #ifdef _WIN32 /* Windows */
78 addr.sin_family = AF_INET;
79 #else /* Posix */
80 addr.sin_family = PF_INET;
81 #endif
82 addr.sin_port = htons (port);
83 addr.sin_addr.s_addr = htonl (INADDR_ANY);
84
85 VERBOSE (DEBUG, fprintf (stdout, "Binding socket\n"));
86 int rc = bind (sock, (struct sockaddr *)&addr, sizeof (addr));
87 #ifdef _WIN32 /* Windows */
88 if (rc == SOCKET_ERROR) {
89 VERBOSE (ERROR, fprintf (stderr, "error: %d\n", WSAGetLastError ()));
90 if (closesocket (sock) == SOCKET_ERROR) {
91 VERBOSE (ERROR, fprintf (stderr, "error: %d\n", WSAGetLastError ()));
92 }
93 #else /* Posix */
94 if (rc < 0) {
95 VERBOSE (ERROR, fprintf (stderr, "error: %d\n", errno));
96 close (sock);
97 #endif
98 return -1;
99 }
100
101 VERBOSE (DEBUG, fprintf (stdout, "Configuring socket\n"));
102 #ifdef _WIN32 /* Windows */
103 #else /* Posix */
104 fcntl (sock, F_SETFL, O_NONBLOCK);
105 #endif
106 int val = 1;
107 rc = setsockopt (sock, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof (val));
108 if (rc < 0) {
109 VERBOSE (ERROR, fprintf (stderr, "%s\n", "setsockopt/TCP_NODELAY"));
110 #ifdef _WIN32 /* Windows */
111 closesocket (sock);
112 #else /* Posix */
113 close (sock);
114 #endif
115 return -1;
116 }
117
118 return sock;
119 }
120
121 /* close listening socket */
122 void close_listening_socket (socket_t sock)
123 {
124 #ifdef _WIN32 /* Windows */
125 closesocket (sock);
126 WSACleanup ();
127 #else /* Posix */
128 close (sock);
129 #endif
130 }
131
132 /* main function */
133
134 int main (int argc, char *argv[])
135 {
136 int i = 0;
137 int ret = 0;
138
139 /* program name */
140
141 progname = argv[0];
142 while (progname[i] != '\0') {
143 if ((progname[i] == '/') || (progname[i] == '\\')) {
144 progname += i + 1;
145 i = 0;
146 } else {
147 i++;
148 }
149 }
150
151 /* argument processing */
152
153 while (argc-- > 1) {
154 char *arg = *(++argv);
155 if (arg[0] != '-') {
156 VERBOSE (ERROR, fprintf (stderr, "%s: invalid option -- '%s'\n", progname, arg); usage (1));
157 return 1;
158 }
159 char c = arg[1];
160 switch (c) {
161 case 'p':
162 arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
163 if (arg == NULL) {
164 VERBOSE (ERROR, fprintf (stderr, "%s: missing port number\n", progname); usage (1));
165 return 1;
166 }
167 port = atoi (arg);
168 if (port <= 0) {
169 VERBOSE (ERROR, fprintf (stderr, "%s: incorrect port number (%s)\n", progname, arg); usage (1));
170 return 1;
171 }
172 break;
173 case 'v':
174 arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
175 if (arg == NULL) {
176 VERBOSE (ERROR, fprintf (stderr, "%s: missing verbose level\n", progname); usage (1));
177 return 1;
178 }
179 verbose = atoi (arg);
180 break;
181 case 'h':
182 default:
183 return usage (c != 'h');
184 }
185 }
186
187 VERBOSE (DEBUG, fprintf (stdout, "Initializing socket\n"));
188 socket_t sock = open_listening_socket (port);
189 if (sock == (socket_t)-1) {
190 VERBOSE (ERROR, fprintf (stderr, "Can't open listening socket\n"));
191 return 1;
192 }
193
194 VERBOSE (INFO, fprintf (stdout, "Listening socket on port %d\n", port));
195 sleep (2);
196
197 VERBOSE (DEBUG, fprintf (stdout, "Closing socket\n"));
198 close_listening_socket (sock);
199
200 return ret;
201 }
202
203 // test: webserver.exe -h
204 // test: webserver.exe -h | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
205 // test: webserver.exe -_ 2> /dev/null | wc -l | xargs test 0 =
206 // test: webserver.exe -_ 2>&1 | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
207 // test: webserver.exe error 2>&1 | grep -q 'invalid option'
208 // test: webserver.exe -v 2>&1 | grep -q 'missing verbose level'
209 // test: webserver.exe -p 2>&1 | grep -q 'missing port number'
210 // test: webserver.exe -p -1 2>&1 | grep -q 'incorrect port number'
211 // test: webserver.exe | grep -q 'Listening socket on port 8080'
212 // test: webserver.exe -p 8000 | grep -q 'Listening socket on port 8000'
213
214 /* vim: set ts=4 sw=4 et: */