Skip to content
Snippets Groups Projects
Select Git revision
  • 9ec8bae94edd0752e2644d8d10053a0f57ab38e5
  • master default protected
2 results

http-server.c

Blame
  • user avatar
    weijiel6 authored
    9ec8bae9
    History
    http-server.c 13.72 KiB
    /*
    ** http-server.c
    ** Code completed by Weijiel6
    ** finished time in 04/29/2019
    */
    
    #include <errno.h>
    #include <stdbool.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include <arpa/inet.h>
    #include <fcntl.h>
    #include <netdb.h>
    #include <netinet/in.h>
    #include <strings.h>
    #include <sys/select.h>
    #include <sys/sendfile.h>
    #include <sys/socket.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    // used to check whether the game is finished
    bool check_end(int sockfd, char* word_putin);
    
    bool make_file(int sockfd, char *filepath); 
    
    // constants
    static char const * const HTTP_200_FORMAT = "HTTP/1.1 200 OK\r\n\
    Content-Type: text/html\r\n\
    Content-Length: %ld\r\n\r\n";
    static char const * const HTTP_400 = "HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n";
    static int const HTTP_400_LENGTH = 47;
    static char const * const HTTP_404 = "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n";
    static int const HTTP_404_LENGTH = 45;
    
    // set two players active states
    bool player1_r , player2_r;
    
    // set tow players socket
    int player1_s, player2_s;
    
    // save players write in keywords
    char player1_w[2000][200], player2_w[2000][200];
    
    // save player write in keywords number
    int player1_w_len, player2_w_len;
    
    // flage for end the game
    bool end_game;
    
    //flage for quit the game
    bool quit_game; 
    
    // represents the types of method
    typedef enum
    {
        GET,
        POST,
        UNKNOWN
    } METHOD;
    
    static bool handle_http_request(int sockfd)
    {	
        // try to read the request
        char buff[2049];
        int n = read(sockfd, buff, 2049);
        if (n <= 0)
        {
            if (n < 0)
                perror("read");
            else
                printf("socket %d close the connection\n", sockfd);
            return false;
        }
        
        // if both two players disconnected, reset the flags
        if (player1_s < 0 && player2_s < 0){
        	end_game = false;
        	quit_game = false;
    	}
    	
    	// set the players socked when players coming in 
    	if (player1_s < 0 && sockfd != player2_s) {
    		player1_s = sockfd;
    	} else if (player2_s < 0 && sockfd != player1_s) {
    		player2_s = sockfd;
    	} else if (sockfd != player1_s && sockfd != player2_s) {
    		printf("%d %d\n",player1_s, player2_s);
    		perror("too many plaers");
    		exit(1);
    	}
    	
        // terminate the string
        buff[n] = 0;
    
        char * curr = buff;
        
        
        // parse the method
        METHOD method = UNKNOWN;
        if (strncmp(curr, "GET ", 4) == 0)
        {
            curr += 4;
            method = GET;
        }
        else if (strncmp(curr, "POST ", 5) == 0)
        {
            curr += 5;
            method = POST;
        }
        else if (write(sockfd, HTTP_400, HTTP_400_LENGTH) < 0)
        {
            perror("write");
            return false;
        }
    
        // sanitise the URI
        while (*curr == '.' || *curr == '/')
            ++curr;
    	    
        // assume the only valid request URI is "/" but it can be modified to accept more files
        if (*curr == ' ')
            if (method == GET)
            {	
            	// send the intro html
            	make_file(sockfd , "1_intro.html");
            }
            else if (method == POST)
            {	
            	if(strstr(buff, "quit=") != NULL) {
            		// if one player quit the game reset the information for it
            		if ( sockfd == player1_s) {
            			player1_s = -1;
            			player1_r = false;
            			quit_game = true;
    				} else if ( sockfd == player2_s){
    					player2_s = -1;
            			player2_r = false;
            			quit_game = true;
    				}
    				// send the game over html
    				make_file(sockfd , "7_gameover.html");
    			}
    			else if (strstr(buff, "user=") != NULL) {
    				// locate the username, it is safe to do so in this sample code, but usually the result is expected to be
    	            // copied to another buffer using strcpy or strncpy to ensure that it will not be overwritten.
    	            char * username = strstr(buff, "user=") + 5;
    	            char *buf = "<p>";
    	            char *endbuf = "</p>\n\n";
    	            
    	            // get the length
    	            int username_length = strlen(username);
    	            int buf_length = strlen(buf);
    	            int endbuf_length = strlen(endbuf);
    	            
    	            // the length needs to include the "<p>"and "</p>\n\n" before the username
    	            long added_length = username_length + 9;
    	
    	            // get the size of the file
    	            struct stat st;
    	            stat("2_start.html", &st);
    	            // increase file size to accommodate the username
    	            long size = st.st_size + added_length;
    	            n = sprintf(buff, HTTP_200_FORMAT, size);
    	            // send the header first
    	            if (write(sockfd, buff, n) < 0)
    	            {
    	                perror("write");
    	                return false;
    	            }
    	            // read the content of the HTML file
    	            int filefd = open("2_start.html", O_RDONLY);
    	            n = read(filefd, buff, 2048);
    	            if (n < 0)
    	            {
    	                perror("read");
    	                close(filefd);
    	                return false;
    	            }
    	            close(filefd);
    	            // move the trailing part backward
    	            int p1, p2;
    	            for (p1 = size - 1, p2 = p1 - added_length ; p1 >= size - 212; --p1, --p2)
    	                buff[p1] = buff[p2];
    	            // copy the username
    	            
    	            // write the "<p>" and "</p>\n\n"
    	            strncpy(buff + p2, buf, buf_length);            
    	            strncpy(buff + p2 + buf_length, username, username_length);
    	            strncpy(buff + p2 + buf_length + username_length, endbuf, endbuf_length);
    	            
    	            // set the last one to "<"
    				buff[p2+ buf_length + username_length+endbuf_length] = '<';
    	            if (write(sockfd, buff, size) < 0)
    	            {
    	                perror("write");
    	                return false;
    	            }
    			}
            }
            else
                // never used, just for completeness
                fprintf(stderr, "no other methods supported");
        else if (*curr == '?') {
        	if (method == GET)
            {	
            	// change the players active state
            	if (sockfd == player1_s) {
            		player1_r = true;
    			} else if (sockfd == player2_s) {
    				player2_r = true;
    			}
            	// send the first turn in html
    			make_file(sockfd , "3_first_turn.html");
            }
            else if (method == POST)
            {	
            	// if one player quit the game reset the information for it
            	if(strstr(buff, "quit=") != NULL) {
            		if ( sockfd == player1_s) {
            			player1_s = -1;
            			player1_r = false;
            			quit_game = true;
    				} else if ( sockfd == player2_s){
    					player2_s = -1;
            			player2_r = false;
            			quit_game = true;
    				}
    				// send the game over html
    				make_file(sockfd , "7_gameover.html");
    			}
    			else if (strstr(buff, "guess=") != NULL) {
    				
    				// get the keyward which would be wrote in
    				char * getword = strstr(buff, "keyword=") + 8;
    				
    				// if the quit game flag is true would quit the game
    				if (quit_game) {
    					
    					// send the game over html
    					make_file(sockfd , "7_gameover.html");
    				}
    				
    				// if the end game flag is ture would end the game
    				if(end_game) {
    					if (sockfd == player1_s) {
    						player1_s = -1;
    						player1_r = false;
    						player1_w_len = 0;
    					} else if (sockfd == player2_s) {
    						player2_s = -1;
    						player2_r = false;
    						player2_w_len = 0;
    					}
    					// send the end game html
    					make_file(sockfd , "6_endgame.html");
    				}
    				
    				// if both players are ready, would send accepted file
    				if (player1_r && player2_r) {
    					
    					//if one guess the same word it would end the game 
    					if (check_end(sockfd, getword)) {
    						
    						// when the player end the game, reset the information for the player
    						if (sockfd == player1_s) {
    							player1_s = -1;
    							player1_r = false;
    							player1_w_len = 0;
    						} else if (sockfd == player2_s) {
    							player2_s = -1;
    							player2_r = false;
    							player2_w_len = 0;
    						}
    						// send the end game html
    						make_file(sockfd , "6_endgame.html");
    					} else {
    						// keep put ward in and guess
    						
    						 
    						// get the size of the file
    			            struct stat st;
    			            stat("4_accepted.html", &st);
    			            n = sprintf(buff, HTTP_200_FORMAT, st.st_size);
    			            // send the header first
    			            if (write(sockfd, buff, n) < 0)
    			            {
    			                perror("write");
    			                return false;
    			            }
    			            // send the file
    			            int filefd = open("4_accepted.html", O_RDONLY);
    			            do
    			            {
    			                n = sendfile(sockfd, filefd, NULL, 2048);
    			            }
    			            while (n > 0);
    			            if (n < 0)
    			            {
    			                perror("sendfile");
    			                close(filefd);
    			                return false;
    			            }
    			            close(filefd);
    					}
    				} else {
    					// not both players active so send discarded file
    					// send the end game html
    					make_file(sockfd , "5_discarded.html");
    				}
    			}
    			else
                // never used, just for completeness
                fprintf(stderr, "no other methods supported");           
    		}
    	}
        // send 404
        else if (write(sockfd, HTTP_404, HTTP_404_LENGTH) < 0)
        {
            perror("write");
            return false;
        }
    
        return true;
    }
    
    bool make_file(int sockfd, char *filepath) {
    	char buff[2049];
        int n;
        // get the size of the file
        struct stat st;
        stat(filepath, &st);
        n = sprintf(buff, HTTP_200_FORMAT, st.st_size);
        // send the header first
        if (write(sockfd, buff, n) < 0)
        {
            perror("write");
            return false;
        }
        // send the file
        int filefd = open(filepath, O_RDONLY);
        do
        {
            n = sendfile(sockfd, filefd, NULL, 2048);
        }
        while (n > 0);
        if (n < 0)
        {
            perror("sendfile");
            close(filefd);
            return false;
        }
        close(filefd);
        return true;
    }
    
    
    // check the word list and change the end game flag and put the word into list
    bool check_end(int sockfd, char* word_putin) {
    	if (sockfd == player1_s) {
    		for (int i = 0; i < player2_w_len; i++){
    			if (strcmp(word_putin, player2_w[i]) == 0) {
    				end_game = true;
    				return true;
    			}
    		}
    		strcpy(player1_w[player1_w_len], word_putin);
    		player1_w_len++;
    	} else if (sockfd == player2_s) {
    		for (int i = 0; i < player1_w_len; i++){
    			if (strcmp(word_putin, player1_w[i]) == 0) {
    				end_game = true;
    				return true;
    			}
    		}
    		strcpy(player2_w[player2_w_len], word_putin);
    		player2_w_len++;	
    	}
    	return false;
    }
    
    int main(int argc, char * argv[])
    {
        if (argc < 3)
        {
            fprintf(stderr, "usage: %s ip port\n", argv[0]);
            return 0;
        } else {
        	fprintf(stderr, "image_tagger server is now running at IP: %s on port %s\n", argv[1], argv[2]);
    	}
    	
    	//set all the global information for the plaers
    	player1_r = player2_r = false;
    	
    	player1_s = player2_s = -1;
    	
    	player1_w_len = player2_w_len = 0;
    	
    	end_game = false;
    	
    	quit_game = false;
    
        // create TCP socket which only accept IPv4
        int sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0)
        {
            perror("socket");
            exit(EXIT_FAILURE);
        }
    
        // reuse the socket if possible
        int const reuse = 1;
        if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)) < 0)
        {
            perror("setsockopt");
            exit(EXIT_FAILURE);
        }
    
        // create and initialise address we will listen on
        struct sockaddr_in serv_addr;
        bzero(&serv_addr, sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        // if ip parameter is not specified
        serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
        serv_addr.sin_port = htons(atoi(argv[2]));
    
        // bind address to socket
        if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
        {
            perror("bind");
            exit(EXIT_FAILURE);
        }
    
        // listen on the socket
        listen(sockfd, 5);
    
        // initialise an active file descriptors set
        fd_set masterfds;
        FD_ZERO(&masterfds);
        FD_SET(sockfd, &masterfds);
        // record the maximum socket number
        int maxfd = sockfd;
    
        while (1)
        {
            // monitor file descriptors
            fd_set readfds = masterfds;
            if (select(FD_SETSIZE, &readfds, NULL, NULL, NULL) < 0)
            {
                perror("select");
                exit(EXIT_FAILURE);
            }
    
            // loop all possible descriptor
            for (int i = 0; i <= maxfd; ++i)
                // determine if the current file descriptor is active
                if (FD_ISSET(i, &readfds))
                {
                    // create new socket if there is new incoming connection request
                    if (i == sockfd)
                    {
                        struct sockaddr_in cliaddr;
                        socklen_t clilen = sizeof(cliaddr);
                        int newsockfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen);
                        if (newsockfd < 0)
                            perror("accept");
                        else
                        {
                            // add the socket to the set
                            FD_SET(newsockfd, &masterfds);
                            // update the maximum tracker
                            if (newsockfd > maxfd)
                                maxfd = newsockfd;
                            // print out the IP and the socket number
                            char ip[INET_ADDRSTRLEN];
                            printf(
                                "new connection is now running at IP: %s on port %d\n",
                                // convert to human readable string
                                inet_ntop(cliaddr.sin_family, &cliaddr.sin_addr, ip, INET_ADDRSTRLEN),
                                newsockfd
                            );
                        }
                    }
                    // a request is sent from the client
                    else if (!handle_http_request(i))
                    {
                        close(i);
                        FD_CLR(i, &masterfds);
                    }
                }
        }
    
        return 0;
    }