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