wserver.c 28.3 KB
Newer Older
// Main function  
int main(int argc, char **argv){
	int sock, newsock;                 // socket descriptors 
	BIO *sbio;
	SSL_CTX *ctx;
	SSL *ssl;
	int r;
	pid_t pid;
	char *proto;                        // protocol type 
	extern char *optarg;                // user input parameters
	int c;                              // user iput from getopt
	int action = 0;                     // specify client/server behavior (handshake, 200OK, serve file, browser-like) 
	int status;                         // ...
	clock_t start, end;                 // timers for cpu time estimation 
	struct timespec tps, tpe;
	double cpu_time_used;               // cpu time used 
	int loadTime = 10;                  // time used for load estimation (10 second default, user can change with option -l)

	// Handle user input parameters
	while((c = getopt(argc, argv, "c:o:s:l:")) != -1){
		switch(c){
			// Protocol 
			case 'c':	if(! (proto = strdup(optarg) )){
							err_exit("Out of memory");
						}
						if (strcmp(proto, "spp_mod") == 0){
							proto = "spp"; 
							disable_nagle = 1;
						}
						if (strcmp(proto, "ssl_mod") == 0){
							proto = "ssl"; 
							disable_nagle = 1;
						}
						if (strcmp(proto, "fwd_mod") == 0){
							proto = "fwd"; 
							disable_nagle = 1;
						}
						if (strcmp(proto, "pln_mod") == 0){
							proto = "pln"; 
							disable_nagle = 1;
						}
						if (strcmp(proto, "fwd") == 0){
							proto = "ssl"; 
						}
						break; 

			// Client/Server behavior 
			case 'o':	action = atoi(optarg); 
						if (action == 2)
							action = 3;
						break; 

			// Control slicing strategy
			case 's':	if(! (strategy = strdup(optarg) )){
							err_exit("Out of memory");
						}
						break;
			// Control load estimation period 
			case 'l':	loadTime = atoi(optarg); 
						break;
		}
	}

	// Check that input parameters are correct 
	if (argc == 1){
		usage();
	}
	if (action < 1 || action > 4){
		usage(); 
	}
	
	// Logging input parameters 
	#ifdef DEBUG
	printf("[DEBUG] Parameters count: %d\n", argc);
	char *temp_str = "undefined"; 
	if (action == 1)
		temp_str = "handshake_only";  
	if (action == 2)
		temp_str = "200_OK";  
	if (action == 3)
		temp_str = "serve_file";  
	if (action == 4)
        temp_str = "browser_like";  
	printf("\t[DEBUG] proto=%s; action=%d (%s)\n", proto, action, temp_str); 
	#endif 

	// Build SSL context
	ctx = initialize_ctx(KEYFILE, PASSWORD, proto);
	load_dh_params(ctx,DHFILE);
   
	// Socket in listen state
	sock = tcp_listen();

	// Wait for client request 
	long finishLoadEst = (long) time (NULL) + loadTime;
	int nConn = 0; 
	bool report = true; 
	while(1){
			
		#ifdef DEBUG
		printf("[DEBUG] Waiting on TCP accept...\n"); 
		#endif
		if((newsock = accept(sock, 0, 0)) < 0){
			err_exit("Problem socket accept\n");
		}else{
			#ifdef DEBUG
			printf("[DEBUG] Accepted new connection %d\n", sock); 
			#endif
		}
		// keep track of number of connections
		nConn++;

		// Fork a new process
		signal(SIGCHLD, SIG_IGN); 
		pid = fork(); 
		if (pid == 0){
			/* In chil process */
			if (pid == -1) {
				berr_exit("FORK error"); 
				return 1;
           	}
			

			#ifdef HI_DEF_TIMER
           	clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tps);
           	#else
           	start = clock();
           	#endif

			#ifdef DEBUG
			printf("[DEBUG] child process close old socket and operate on new one\n");
			#endif
			close(sock);

			if (strcmp(proto, "pln") != 0) 
			{
				sbio = BIO_new_socket(newsock, BIO_NOCLOSE);
				ssl = SSL_new(ctx);
				SSL_set_bio(ssl, sbio, sbio);
				
				// Wait on SSL Accept 
				if((r = SSL_accept(ssl) <= 0)){
					berr_exit("SSL accept error");
				} else {
					#ifdef DEBUG
					if (strcmp(proto, "ssl") == 0){ 		
						printf("[DEBUG] SSL accept OK\n"); 
					}else{
						printf("[DEBUG] SPP accept OK\n"); 
					}
					#endif
				}
			}
			#ifdef HI_DEF_TIMER
			clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tpe);
			cpu_time_used =  ( tpe.tv_sec - tps.tv_sec ) + (double)( tpe.tv_nsec - tps.tv_nsec )/ (double)1000000000L;
			#else
			end = clock();
			cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
			#endif


			if (loadTime > 0){
				printf( "CPU time=%g sec\n", cpu_time_used); 
			}
  			
			// Switch across possible client-server behavior 
			// NOTE: here we can add more complex strategies
			switch(action){
				// Handshake only 
				case 1: break; 
				
				// Respond with 200 OK
				case 2: http_simple_response(ssl, newsock, proto);
						break; 
				
				// Serve some content 
				case 3: http_serve_request(ssl, newsock, proto, false, action);
						break;
			
				// Serve a browser like behavior 
				// NOTE
				// This can only serve one connection at a time. 
				// Here we would need to fire a new thread 
				// (or re-think the code) to support multiple SSL connections 
				// FIXME - detect closed connection 
				case 4: while(1){
							if (http_serve_request_browser(ssl, newsock, proto) < 0){
								break; 
							}
						}
						break;

				// Unknown option 
				default: usage();
						 break; 
			}
			// Correctly end child process
			#ifdef DEBUG
			printf("[DEBUG] End child process (prevent zombies)\n");
			#endif
			exit(0);  
			// return 0 
		}else{
			#ifdef DEBUG
			printf("[DEBUG] parent process close new socket\n");
			#endif
			close(newsock); 
		}
	}
	wait(&status);
	// Clean context
	destroy_ctx(ctx);
	
	// Correctly end parent process
	exit(0); 
}