File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpc / minihttptestserver.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:16:22 2012 UTC (12 years, 4 months ago) by misho
Branches: miniupnpc, elwix, MAIN
CVS tags: v1_6, HEAD
miniupnpc

    1: /* $Id: minihttptestserver.c,v 1.1.1.1 2012/02/21 23:16:22 misho 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>