File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpc / minihttptestserver.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Jul 22 00:36:10 2013 UTC (10 years, 10 months ago) by misho
Branches: miniupnpc, elwix, MAIN
CVS tags: v1_8p0, v1_8, HEAD
1.8

    1: /* $Id: minihttptestserver.c,v 1.1.1.2 2013/07/22 00:36:10 misho Exp $ */
    2: /* Project : miniUPnP
    3:  * Author : Thomas Bernard
    4:  * Copyright (c) 2011-2012 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: #include <errno.h>
   20: 
   21: #define CRAP_LENGTH (2048)
   22: 
   23: volatile sig_atomic_t quit = 0;
   24: volatile sig_atomic_t child_to_wait_for = 0;
   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: 	}
  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;
  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) {
  167: 			if(errno != EINTR) {
  168: 				perror("write");
  169: 				return;
  170: 			}
  171: 			/* if errno == EINTR, try again */
  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 */
  199: 	while(request_len < (int)sizeof(request_buffer) && !headers_found) {
  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;
  227: 	for(i = 0; i < (int)sizeof(request_method) - 1; i++) {
  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++;
  236: 	for(i = 0; i < (int)sizeof(request_uri) - 1; i++) {
  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++;
  245: 	for(i = 0; i < (int)sizeof(http_version) - 1; i++) {
  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";
  258: 		const char * pc;
  259: 		/* 405 Method Not Allowed */
  260: 		/* The response MUST include an Allow header containing a list
  261: 		 * of valid methods for the requested resource. */
  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: 		}
  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);
  383: 		addr->sin6_addr = in6addr_loopback;
  384: 	} else {
  385: 		struct sockaddr_in * addr = (struct sockaddr_in *)&server_addr;
  386: 		addr->sin_family = AF_INET;
  387: 		addr->sin_port = htons(port);
  388: 		addr->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
  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);
  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: 			}
  427: 			free(buffer);
  428: 			fclose(f);
  429: 		} else {
  430: 			fprintf(stderr, "error opening file %s for writing\n", expected_file_name);
  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>