rev |
line source |
nuclear@0
|
1 #include <stdio.h>
|
nuclear@0
|
2 #include <stdlib.h>
|
nuclear@0
|
3 #include <string.h>
|
nuclear@3
|
4 #include <ctype.h>
|
nuclear@3
|
5 #include <signal.h>
|
nuclear@0
|
6 #include <errno.h>
|
nuclear@0
|
7 #include <unistd.h>
|
nuclear@0
|
8 #include <fcntl.h>
|
nuclear@0
|
9 #include <sys/select.h>
|
nuclear@0
|
10 #include <sys/types.h>
|
nuclear@0
|
11 #include <sys/socket.h>
|
nuclear@0
|
12 #include <arpa/inet.h>
|
nuclear@1
|
13 #include "http.h"
|
nuclear@1
|
14
|
nuclear@1
|
15 /* HTTP version */
|
nuclear@1
|
16 #define HTTP_VER_MAJOR 1
|
nuclear@1
|
17 #define HTTP_VER_MINOR 1
|
nuclear@1
|
18 #define HTTP_VER_STR "1.1"
|
nuclear@1
|
19
|
nuclear@1
|
20 /* maximum request length: 64mb */
|
nuclear@1
|
21 #define MAX_REQ_LENGTH (65536 * 1024)
|
nuclear@0
|
22
|
nuclear@0
|
23 struct client {
|
nuclear@0
|
24 int s;
|
nuclear@0
|
25 char *rcvbuf;
|
nuclear@0
|
26 int bufsz;
|
nuclear@0
|
27 struct client *next;
|
nuclear@0
|
28 };
|
nuclear@0
|
29
|
nuclear@0
|
30 int start_server(void);
|
nuclear@0
|
31 int accept_conn(int lis);
|
nuclear@3
|
32 void close_conn(struct client *c);
|
nuclear@0
|
33 int handle_client(struct client *c);
|
nuclear@3
|
34 void do_get_head(struct client *c);
|
nuclear@1
|
35 void respond_error(struct client *c, int errcode);
|
nuclear@3
|
36 void sighandler(int s);
|
nuclear@1
|
37 int parse_args(int argc, char **argv);
|
nuclear@0
|
38
|
nuclear@0
|
39 static int lis;
|
nuclear@0
|
40 static int port = 8080;
|
nuclear@0
|
41 static struct client *clist;
|
nuclear@0
|
42
|
nuclear@0
|
43 int main(int argc, char **argv)
|
nuclear@0
|
44 {
|
nuclear@1
|
45 if(parse_args(argc, argv) == -1) {
|
nuclear@1
|
46 return 1;
|
nuclear@1
|
47 }
|
nuclear@1
|
48
|
nuclear@3
|
49 signal(SIGINT, sighandler);
|
nuclear@3
|
50 signal(SIGTERM, sighandler);
|
nuclear@3
|
51 signal(SIGQUIT, sighandler);
|
nuclear@3
|
52
|
nuclear@0
|
53 if((lis = start_server()) == -1) {
|
nuclear@0
|
54 return 1;
|
nuclear@0
|
55 }
|
nuclear@0
|
56
|
nuclear@0
|
57 for(;;) {
|
nuclear@0
|
58 struct client *c, dummy;
|
nuclear@0
|
59 int maxfd = lis;
|
nuclear@0
|
60 fd_set rdset;
|
nuclear@0
|
61
|
nuclear@0
|
62 FD_ZERO(&rdset);
|
nuclear@0
|
63 FD_SET(lis, &rdset);
|
nuclear@0
|
64
|
nuclear@0
|
65 c = clist;
|
nuclear@0
|
66 while(c) {
|
nuclear@0
|
67 if(c->s > maxfd) {
|
nuclear@0
|
68 maxfd = c->s;
|
nuclear@0
|
69 }
|
nuclear@0
|
70 FD_SET(c->s, &rdset);
|
nuclear@0
|
71 c = c->next;
|
nuclear@0
|
72 }
|
nuclear@0
|
73
|
nuclear@0
|
74 while(select(maxfd + 1, &rdset, 0, 0, 0) == -1 && errno == EINTR);
|
nuclear@0
|
75
|
nuclear@0
|
76 c = clist;
|
nuclear@0
|
77 while(c) {
|
nuclear@0
|
78 if(FD_ISSET(c->s, &rdset)) {
|
nuclear@0
|
79 handle_client(c);
|
nuclear@0
|
80 }
|
nuclear@0
|
81 c = c->next;
|
nuclear@0
|
82 }
|
nuclear@0
|
83
|
nuclear@0
|
84 if(FD_ISSET(lis, &rdset)) {
|
nuclear@0
|
85 accept_conn(lis);
|
nuclear@0
|
86 }
|
nuclear@0
|
87
|
nuclear@0
|
88 dummy.next = clist;
|
nuclear@0
|
89 c = &dummy;
|
nuclear@0
|
90
|
nuclear@0
|
91 while(c->next) {
|
nuclear@0
|
92 struct client *n = c->next;
|
nuclear@0
|
93
|
nuclear@0
|
94 if(n->s == -1) {
|
nuclear@0
|
95 /* marked for removal */
|
nuclear@0
|
96 c->next = n->next;
|
nuclear@0
|
97 free(n);
|
nuclear@0
|
98 } else {
|
nuclear@0
|
99 c = c->next;
|
nuclear@0
|
100 }
|
nuclear@0
|
101 }
|
nuclear@0
|
102 }
|
nuclear@3
|
103
|
nuclear@3
|
104 return 0; /* unreachable */
|
nuclear@0
|
105 }
|
nuclear@0
|
106
|
nuclear@0
|
107 int start_server(void)
|
nuclear@0
|
108 {
|
nuclear@0
|
109 int s;
|
nuclear@0
|
110 struct sockaddr_in sa;
|
nuclear@0
|
111
|
nuclear@0
|
112 if((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
|
nuclear@0
|
113 perror("failed to create listening socket");
|
nuclear@0
|
114 return -1;
|
nuclear@0
|
115 }
|
nuclear@0
|
116 fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
|
nuclear@0
|
117
|
nuclear@0
|
118 memset(&sa, 0, sizeof sa);
|
nuclear@0
|
119 sa.sin_family = AF_INET;
|
nuclear@0
|
120 sa.sin_addr.s_addr = INADDR_ANY;
|
nuclear@0
|
121 sa.sin_port = htons(port);
|
nuclear@0
|
122
|
nuclear@0
|
123 if(bind(s, (struct sockaddr*)&sa, sizeof sa) == -1) {
|
nuclear@0
|
124 fprintf(stderr, "failed to bind socket to port %d: %s\n", port, strerror(errno));
|
nuclear@0
|
125 return -1;
|
nuclear@0
|
126 }
|
nuclear@0
|
127 listen(s, 16);
|
nuclear@0
|
128
|
nuclear@0
|
129 return s;
|
nuclear@0
|
130 }
|
nuclear@0
|
131
|
nuclear@0
|
132 int accept_conn(int lis)
|
nuclear@0
|
133 {
|
nuclear@0
|
134 int s;
|
nuclear@0
|
135 struct client *c;
|
nuclear@0
|
136 struct sockaddr_in addr;
|
nuclear@0
|
137 socklen_t addr_sz = sizeof addr;
|
nuclear@0
|
138
|
nuclear@0
|
139 if((s = accept(lis, (struct sockaddr*)&addr, &addr_sz)) == -1) {
|
nuclear@0
|
140 perror("failed to accept incoming connection");
|
nuclear@0
|
141 return -1;
|
nuclear@0
|
142 }
|
nuclear@0
|
143 fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
|
nuclear@0
|
144
|
nuclear@0
|
145 if(!(c = malloc(sizeof *c))) {
|
nuclear@0
|
146 perror("failed to allocate memory while accepting connection");
|
nuclear@0
|
147 return -1;
|
nuclear@0
|
148 }
|
nuclear@0
|
149 c->s = s;
|
nuclear@0
|
150 c->rcvbuf = 0;
|
nuclear@0
|
151 c->bufsz = 0;
|
nuclear@0
|
152 c->next = clist;
|
nuclear@0
|
153 clist = c;
|
nuclear@0
|
154 return 0;
|
nuclear@0
|
155 }
|
nuclear@0
|
156
|
nuclear@1
|
157 void close_conn(struct client *c)
|
nuclear@1
|
158 {
|
nuclear@1
|
159 close(c->s);
|
nuclear@1
|
160 c->s = -1; /* mark it for removal */
|
nuclear@1
|
161 free(c->rcvbuf);
|
nuclear@1
|
162 }
|
nuclear@1
|
163
|
nuclear@0
|
164 int handle_client(struct client *c)
|
nuclear@0
|
165 {
|
nuclear@3
|
166 struct http_req_header hdr;
|
nuclear@0
|
167 static char buf[2048];
|
nuclear@3
|
168 int rdsz, status;
|
nuclear@0
|
169
|
nuclear@0
|
170 while((rdsz = recv(c->s, buf, sizeof buf, 0)) > 0) {
|
nuclear@0
|
171 if(c->rcvbuf) {
|
nuclear@3
|
172 char *newbuf;
|
nuclear@0
|
173 int newsz = c->bufsz + rdsz;
|
nuclear@1
|
174 if(newsz > MAX_REQ_LENGTH) {
|
nuclear@1
|
175 respond_error(c, 413);
|
nuclear@1
|
176 return -1;
|
nuclear@1
|
177 }
|
nuclear@1
|
178
|
nuclear@3
|
179 if(!(newbuf = realloc(buf, newsz + 1))) {
|
nuclear@0
|
180 fprintf(stderr, "failed to allocate %d byte buffer\n", newsz);
|
nuclear@1
|
181 respond_error(c, 503);
|
nuclear@1
|
182 return -1;
|
nuclear@0
|
183 }
|
nuclear@0
|
184
|
nuclear@0
|
185 memcpy(newbuf + c->bufsz, buf, rdsz);
|
nuclear@1
|
186 newbuf[newsz] = 0;
|
nuclear@0
|
187
|
nuclear@0
|
188 c->rcvbuf = newbuf;
|
nuclear@0
|
189 c->bufsz = newsz;
|
nuclear@0
|
190 }
|
nuclear@0
|
191 }
|
nuclear@0
|
192
|
nuclear@3
|
193 if((status = http_parse_header(&hdr, c->rcvbuf, c->bufsz)) != HTTP_HDR_OK) {
|
nuclear@3
|
194 http_print_header(&hdr);
|
nuclear@3
|
195 switch(status) {
|
nuclear@3
|
196 case HTTP_HDR_INVALID:
|
nuclear@3
|
197 respond_error(c, 400);
|
nuclear@3
|
198 return -1;
|
nuclear@3
|
199
|
nuclear@3
|
200 case HTTP_HDR_NOMEM:
|
nuclear@3
|
201 respond_error(c, 503);
|
nuclear@3
|
202 return -1;
|
nuclear@3
|
203
|
nuclear@3
|
204 case HTTP_HDR_PARTIAL:
|
nuclear@3
|
205 return 0; /* partial header, continue reading */
|
nuclear@3
|
206 }
|
nuclear@3
|
207 }
|
nuclear@3
|
208 http_print_header(&hdr);
|
nuclear@3
|
209
|
nuclear@3
|
210 /* we only support GET and HEAD at this point, so freak out on anything else */
|
nuclear@3
|
211 switch(hdr.method) {
|
nuclear@3
|
212 case HTTP_GET:
|
nuclear@3
|
213 case HTTP_HEAD:
|
nuclear@3
|
214 do_get_head(c);
|
nuclear@3
|
215 break;
|
nuclear@3
|
216
|
nuclear@3
|
217 default:
|
nuclear@3
|
218 respond_error(c, 501);
|
nuclear@3
|
219 return -1;
|
nuclear@1
|
220 }
|
nuclear@1
|
221
|
nuclear@3
|
222 close_conn(c);
|
nuclear@3
|
223 return 0;
|
nuclear@3
|
224 }
|
nuclear@1
|
225
|
nuclear@3
|
226 void do_get_head(struct client *c)
|
nuclear@3
|
227 {
|
nuclear@1
|
228 }
|
nuclear@0
|
229
|
nuclear@1
|
230 void respond_error(struct client *c, int errcode)
|
nuclear@1
|
231 {
|
nuclear@1
|
232 char buf[512];
|
nuclear@1
|
233
|
nuclear@1
|
234 sprintf(buf, HTTP_VER_STR " %d %s\r\n\r\n", errcode, http_strmsg(errcode));
|
nuclear@1
|
235
|
nuclear@1
|
236 send(c->s, buf, strlen(buf), 0);
|
nuclear@1
|
237 close_conn(c);
|
nuclear@1
|
238 }
|
nuclear@1
|
239
|
nuclear@3
|
240 void sighandler(int s)
|
nuclear@3
|
241 {
|
nuclear@3
|
242 if(s == SIGINT || s == SIGTERM || s == SIGQUIT) {
|
nuclear@3
|
243 close(lis);
|
nuclear@3
|
244 while(clist) {
|
nuclear@3
|
245 struct client *c = clist;
|
nuclear@3
|
246 clist = clist->next;
|
nuclear@3
|
247 close_conn(c);
|
nuclear@3
|
248 free(c);
|
nuclear@3
|
249 }
|
nuclear@3
|
250 clist = 0;
|
nuclear@3
|
251
|
nuclear@3
|
252 printf("bye!\n");
|
nuclear@3
|
253 exit(0);
|
nuclear@3
|
254 }
|
nuclear@3
|
255 }
|
nuclear@3
|
256
|
nuclear@1
|
257
|
nuclear@1
|
258 static void print_help(const char *argv0)
|
nuclear@1
|
259 {
|
nuclear@1
|
260 printf("Usage: %s [options]\n", argv0);
|
nuclear@1
|
261 printf("Options:\n");
|
nuclear@1
|
262 printf(" -p <port> set the TCP/IP port number to use\n");
|
nuclear@1
|
263 printf(" -h print usage help and exit\n");
|
nuclear@1
|
264 }
|
nuclear@1
|
265
|
nuclear@1
|
266 int parse_args(int argc, char **argv)
|
nuclear@1
|
267 {
|
nuclear@1
|
268 int i;
|
nuclear@1
|
269
|
nuclear@1
|
270 for(i=1; i<argc; i++) {
|
nuclear@1
|
271 if(argv[i][0] == '-' && argv[i][2] == 0) {
|
nuclear@1
|
272 switch(argv[i][1]) {
|
nuclear@1
|
273 case 'p':
|
nuclear@1
|
274 if((port = atoi(argv[++i])) == 0) {
|
nuclear@1
|
275 fprintf(stderr, "-p must be followed by a valid port number\n");
|
nuclear@1
|
276 return -1;
|
nuclear@1
|
277 }
|
nuclear@1
|
278 break;
|
nuclear@1
|
279
|
nuclear@1
|
280 case 'h':
|
nuclear@1
|
281 print_help(argv[0]);
|
nuclear@1
|
282 exit(0);
|
nuclear@1
|
283
|
nuclear@1
|
284 default:
|
nuclear@1
|
285 fprintf(stderr, "unrecognized option: %s\n", argv[i]);
|
nuclear@1
|
286 return -1;
|
nuclear@1
|
287 }
|
nuclear@1
|
288 } else {
|
nuclear@1
|
289 fprintf(stderr, "unexpected argument: %s\n", argv[i]);
|
nuclear@1
|
290 return -1;
|
nuclear@1
|
291 }
|
nuclear@0
|
292 }
|
nuclear@0
|
293 return 0;
|
nuclear@0
|
294 }
|