Annotation of embedaddon/miniupnpc/minihttptestserver.c, revision 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>