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