Annotation of embedaddon/miniupnpc/minihttptestserver.c, revision 1.1.1.1
1.1 misho 1: /* $Id: minihttptestserver.c,v 1.6 2011/05/09 08:53:15 nanard Exp $ */
2: /* Project : miniUPnP
3: * Author : Thomas Bernard
4: * Copyright (c) 2011 Thomas Bernard
5: * This software is subject to the conditions detailed in the
6: * LICENCE file provided in this distribution.
7: * */
8: #include <stdio.h>
9: #include <stdlib.h>
10: #include <string.h>
11: #include <unistd.h>
12: #include <sys/types.h>
13: #include <sys/socket.h>
14: #include <sys/wait.h>
15: #include <arpa/inet.h>
16: #include <netinet/in.h>
17: #include <signal.h>
18: #include <time.h>
19:
20: #define CRAP_LENGTH (2048)
21:
22: volatile int quit = 0;
23: volatile int child_to_wait_for = 0;
24:
25: /**
26: * signal handler for SIGCHLD (child status has changed)
27: */
28: void handle_signal_chld(int sig)
29: {
30: printf("handle_signal_chld(%d)\n", sig);
31: ++child_to_wait_for;
32: }
33:
34: /**
35: * signal handler for SIGINT (CRTL C)
36: */
37: #if 0
38: void handle_signal_int(int sig)
39: {
40: printf("handle_signal_int(%d)\n", sig);
41: quit = 1;
42: }
43: #endif
44:
45: /**
46: * build a text/plain content of the specified length
47: */
48: void build_content(char * p, int n)
49: {
50: char line_buffer[80];
51: int k;
52: int i = 0;
53:
54: while(n > 0) {
55: k = snprintf(line_buffer, sizeof(line_buffer),
56: "%04d_ABCDEFGHIJKL_This_line_is_64_bytes_long_ABCDEFGHIJKL_%04d\r\n",
57: i, i);
58: if(k != 64) {
59: fprintf(stderr, "snprintf() returned %d in build_content()\n", k);
60: }
61: ++i;
62: if(n >= 64) {
63: memcpy(p, line_buffer, 64);
64: p += 64;
65: n -= 64;
66: } else {
67: memcpy(p, line_buffer, n);
68: p += n;
69: n = 0;
70: }
71: }
72: }
73:
74: /**
75: * build crappy content
76: */
77: void build_crap(char * p, int n)
78: {
79: static const char crap[] = "_CRAP_\r\n";
80: int i;
81:
82: while(n > 0) {
83: i = sizeof(crap) - 1;
84: if(i > n)
85: i = n;
86: memcpy(p, crap, i);
87: p += i;
88: n -= i;
89: }
90: }
91:
92: /**
93: * build chunked response.
94: * return a malloc'ed buffer
95: */
96: char * build_chunked_response(int content_length, int * response_len) {
97: char * response_buffer;
98: char * content_buffer;
99: int buffer_length;
100: int i, n;
101:
102: /* allocate to have some margin */
103: buffer_length = 256 + content_length + (content_length >> 4);
104: response_buffer = malloc(buffer_length);
105: *response_len = snprintf(response_buffer, buffer_length,
106: "HTTP/1.1 200 OK\r\n"
107: "Content-Type: text/plain\r\n"
108: "Transfer-Encoding: chunked\r\n"
109: "\r\n");
110:
111: /* build the content */
112: content_buffer = malloc(content_length);
113: build_content(content_buffer, content_length);
114:
115: /* chunk it */
116: i = 0;
117: while(i < content_length) {
118: n = (rand() % 199) + 1;
119: if(i + n > content_length) {
120: n = content_length - i;
121: }
122: /* TODO : check buffer size ! */
123: *response_len += snprintf(response_buffer + *response_len,
124: buffer_length - *response_len,
125: "%x\r\n", n);
126: memcpy(response_buffer + *response_len, content_buffer + i, n);
127: *response_len += n;
128: i += n;
129: response_buffer[(*response_len)++] = '\r';
130: response_buffer[(*response_len)++] = '\n';
131: }
132: memcpy(response_buffer + *response_len, "0\r\n", 3);
133: *response_len += 3;
134: free(content_buffer);
135:
136: printf("resp_length=%d buffer_length=%d content_length=%d\n",
137: *response_len, buffer_length, content_length);
138: return response_buffer;
139: }
140:
141: enum modes { MODE_INVALID, MODE_CHUNKED, MODE_ADDCRAP, MODE_NORMAL };
142: const struct {
143: const enum modes mode;
144: const char * text;
145: } modes_array[] = {
146: {MODE_CHUNKED, "chunked"},
147: {MODE_ADDCRAP, "addcrap"},
148: {MODE_NORMAL, "normal"},
149: {MODE_INVALID, NULL}
150: };
151:
152: /**
153: * write the response with random behaviour !
154: */
155: void send_response(int c, const char * buffer, int len)
156: {
157: int n;
158: while(len > 0) {
159: n = (rand() % 99) + 1;
160: if(n > len)
161: n = len;
162: n = write(c, buffer, n);
163: if(n < 0) {
164: perror("write");
165: return;
166: } else {
167: len -= n;
168: buffer += n;
169: }
170: usleep(10000); /* 10ms */
171: }
172: }
173:
174: /**
175: * handle the HTTP connection
176: */
177: void handle_http_connection(int c)
178: {
179: char request_buffer[2048];
180: int request_len = 0;
181: int headers_found = 0;
182: int n, i;
183: char request_method[16];
184: char request_uri[256];
185: char http_version[16];
186: char * p;
187: char * response_buffer;
188: int response_len;
189: enum modes mode;
190: int content_length = 16*1024;
191:
192: /* read the request */
193: while(request_len < sizeof(request_buffer) && !headers_found) {
194: n = read(c,
195: request_buffer + request_len,
196: sizeof(request_buffer) - request_len);
197: if(n < 0) {
198: perror("read");
199: return;
200: } else if(n==0) {
201: /* remote host closed the connection */
202: break;
203: } else {
204: request_len += n;
205: for(i = 0; i < request_len - 3; i++) {
206: if(0 == memcmp(request_buffer + i, "\r\n\r\n", 4)) {
207: /* found the end of headers */
208: headers_found = 1;
209: break;
210: }
211: }
212: }
213: }
214: if(!headers_found) {
215: /* error */
216: return;
217: }
218: printf("headers :\n%.*s", request_len, request_buffer);
219: /* the request have been received, now parse the request line */
220: p = request_buffer;
221: for(i = 0; i < sizeof(request_method) - 1; i++) {
222: if(*p == ' ' || *p == '\r')
223: break;
224: request_method[i] = *p;
225: ++p;
226: }
227: request_method[i] = '\0';
228: while(*p == ' ')
229: p++;
230: for(i = 0; i < sizeof(request_uri) - 1; i++) {
231: if(*p == ' ' || *p == '\r')
232: break;
233: request_uri[i] = *p;
234: ++p;
235: }
236: request_uri[i] = '\0';
237: while(*p == ' ')
238: p++;
239: for(i = 0; i < sizeof(http_version) - 1; i++) {
240: if(*p == ' ' || *p == '\r')
241: break;
242: http_version[i] = *p;
243: ++p;
244: }
245: http_version[i] = '\0';
246: printf("Method = %s, URI = %s, %s\n",
247: request_method, request_uri, http_version);
248: /* check if the request method is allowed */
249: if(0 != strcmp(request_method, "GET")) {
250: const char response405[] = "HTTP/1.1 405 Method Not Allowed\r\n"
251: "Allow: GET\r\n\r\n";
252: /* 405 Method Not Allowed */
253: /* The response MUST include an Allow header containing a list
254: * of valid methods for the requested resource. */
255: write(c, response405, sizeof(response405) - 1);
256: return;
257: }
258:
259: mode = MODE_INVALID;
260: /* use the request URI to know what to do */
261: for(i = 0; modes_array[i].mode != MODE_INVALID; i++) {
262: if(strstr(request_uri, modes_array[i].text)) {
263: mode = modes_array[i].mode; /* found */
264: break;
265: }
266: }
267:
268: switch(mode) {
269: case MODE_CHUNKED:
270: response_buffer = build_chunked_response(content_length, &response_len);
271: break;
272: case MODE_ADDCRAP:
273: response_len = content_length+256;
274: response_buffer = malloc(response_len);
275: n = snprintf(response_buffer, response_len,
276: "HTTP/1.1 200 OK\r\n"
277: "Server: minihttptestserver\r\n"
278: "Content-Type: text/plain\r\n"
279: "Content-Length: %d\r\n"
280: "\r\n", content_length);
281: response_len = content_length+n+CRAP_LENGTH;
282: response_buffer = realloc(response_buffer, response_len);
283: build_content(response_buffer + n, content_length);
284: build_crap(response_buffer + n + content_length, CRAP_LENGTH);
285: break;
286: default:
287: response_len = content_length+256;
288: response_buffer = malloc(response_len);
289: n = snprintf(response_buffer, response_len,
290: "HTTP/1.1 200 OK\r\n"
291: "Server: minihttptestserver\r\n"
292: "Content-Type: text/plain\r\n"
293: "\r\n");
294: response_len = content_length+n;
295: response_buffer = realloc(response_buffer, response_len);
296: build_content(response_buffer + n, response_len - n);
297: }
298:
299: if(response_buffer) {
300: send_response(c, response_buffer, response_len);
301: free(response_buffer);
302: } else {
303: /* Error 500 */
304: }
305: }
306:
307: /**
308: */
309: int main(int argc, char * * argv) {
310: int ipv6 = 0;
311: int s, c, i;
312: unsigned short port = 0;
313: struct sockaddr_storage server_addr;
314: socklen_t server_addrlen;
315: struct sockaddr_storage client_addr;
316: socklen_t client_addrlen;
317: pid_t pid;
318: int child = 0;
319: int status;
320: const char * expected_file_name = NULL;
321:
322: for(i = 1; i < argc; i++) {
323: if(argv[i][0] == '-') {
324: switch(argv[i][1]) {
325: case '6':
326: ipv6 = 1;
327: break;
328: case 'e':
329: /* write expected file ! */
330: expected_file_name = argv[++i];
331: break;
332: case 'p':
333: /* port */
334: if(++i < argc) {
335: port = (unsigned short)atoi(argv[i]);
336: }
337: break;
338: default:
339: fprintf(stderr, "unknown command line switch '%s'\n", argv[i]);
340: }
341: } else {
342: fprintf(stderr, "unkown command line argument '%s'\n", argv[i]);
343: }
344: }
345:
346: srand(time(NULL));
347: signal(SIGCHLD, handle_signal_chld);
348: #if 0
349: signal(SIGINT, handle_signal_int);
350: #endif
351:
352: s = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0);
353: if(s < 0) {
354: perror("socket");
355: return 1;
356: }
357: memset(&server_addr, 0, sizeof(struct sockaddr_storage));
358: memset(&client_addr, 0, sizeof(struct sockaddr_storage));
359: if(ipv6) {
360: struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&server_addr;
361: addr->sin6_family = AF_INET6;
362: addr->sin6_port = htons(port);
363: addr->sin6_addr = in6addr_any;
364: } else {
365: struct sockaddr_in * addr = (struct sockaddr_in *)&server_addr;
366: addr->sin_family = AF_INET;
367: addr->sin_port = htons(port);
368: addr->sin_addr.s_addr = htonl(INADDR_ANY);
369: }
370: if(bind(s, (struct sockaddr *)&server_addr,
371: ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) < 0) {
372: perror("bind");
373: return 1;
374: }
375: if(listen(s, 5) < 0) {
376: perror("listen");
377: }
378: if(port == 0) {
379: server_addrlen = sizeof(struct sockaddr_storage);
380: if(getsockname(s, (struct sockaddr *)&server_addr, &server_addrlen) < 0) {
381: perror("getsockname");
382: return 1;
383: }
384: if(ipv6) {
385: struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&server_addr;
386: port = ntohs(addr->sin6_port);
387: } else {
388: struct sockaddr_in * addr = (struct sockaddr_in *)&server_addr;
389: port = ntohs(addr->sin_port);
390: }
391: printf("Listening on port %hu\n", port);
392: fflush(stdout);
393: }
394:
395: /* write expected file */
396: if(expected_file_name) {
397: FILE * f;
398: f = fopen(expected_file_name, "wb");
399: if(f) {
400: char * buffer;
401: buffer = malloc(16*1024);
402: build_content(buffer, 16*1024);
403: fwrite(buffer, 1, 16*1024, f);
404: free(buffer);
405: fclose(f);
406: }
407: }
408:
409: /* fork() loop */
410: while(!child && !quit) {
411: while(child_to_wait_for > 0) {
412: pid = wait(&status);
413: if(pid < 0) {
414: perror("wait");
415: } else {
416: printf("child(%d) terminated with status %d\n", pid, status);
417: }
418: --child_to_wait_for;
419: }
420: /* TODO : add a select() call in order to handle the case
421: * when a signal is caught */
422: client_addrlen = sizeof(struct sockaddr_storage);
423: c = accept(s, (struct sockaddr *)&client_addr,
424: &client_addrlen);
425: if(c < 0) {
426: perror("accept");
427: return 1;
428: }
429: printf("accept...\n");
430: pid = fork();
431: if(pid < 0) {
432: perror("fork");
433: return 1;
434: } else if(pid == 0) {
435: /* child */
436: child = 1;
437: close(s);
438: s = -1;
439: handle_http_connection(c);
440: }
441: close(c);
442: }
443: if(s >= 0) {
444: close(s);
445: s = -1;
446: }
447: if(!child) {
448: while(child_to_wait_for > 0) {
449: pid = wait(&status);
450: if(pid < 0) {
451: perror("wait");
452: } else {
453: printf("child(%d) terminated with status %d\n", pid, status);
454: }
455: --child_to_wait_for;
456: }
457: printf("Bye...\n");
458: }
459: return 0;
460: }
461:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>