Skip to content
Snippets Groups Projects
Select Git revision
  • eee8e8b6ed9b04ba9bfe3ae4a89dea4e1cd62e0c
  • master default protected
  • feat/cookie
  • feat/keyword
4 results

http-server.c

Blame
  • http-server.c 10.50 KiB
    /*
    ** http-server.c
    */
    
    #include <errno.h>
    #include <stdbool.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <assert.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>
    #include "http-parser.h"
    #include "http-response.h"
    #include "user.h"
    
    // 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;
    
    
    bool player_session(char* buff, int sockfd, char* file_name, char* response){
        // get the size of the file
        printf("runnning player session functin\n");
        struct stat st;
        stat(file_name, &st);
        int n = sprintf(buff, response, st.st_size);
        // send the header first
        printf("sending header\n");
        if (write(sockfd, buff, n) < 0)
        {
            perror("write");
            return false;
        }
        // send the file
        printf("sending file\n");
        int filefd = open(file_name, O_RDONLY);
        do
        {
            n = sendfile(sockfd, filefd, NULL, 2048);
        }
        while (n > 0);
        if (n < 0)
        {
            perror("sendfile");
            close(filefd);
            return false;
        }
        printf("about to close");
        close(filefd);
        return true;
    }
    bool get_request(char* buff, int sockfd, char* file_name){
        // get the size of the file
        struct stat st;
        stat(file_name, &st);
        int 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(file_name, 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;
    }
    
    bool post_request(char *buff, int sockfd, char* file_name, char* response){
    	// 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.
        printf("USERNAME IS %s\n\n", buff);
        char * username = strcpy(buff, "user=") + 5;
        int username_length = strlen(username);
        // the length needs to include the ", " before the username
        long added_length = username_length + 2;
    
        // get the size of the file
        struct stat st;
        stat(file_name, &st);
        // increase file size to accommodate the username
        long size = st.st_size + added_length;
        int n = sprintf(buff, response, 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(file_name, 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 - 25; --p1, --p2)
            buff[p1] = buff[p2];
        ++p2;
        // put the separator
        buff[p2++] = ',';
        buff[p2++] = ' ';
        // copy the username
        strncpy(buff + p2, username, username_length);
        if (write(sockfd, buff, size) < 0)
        {
            perror("write");
            return false;
        }
    	return true;
    }
    
    
    static bool handle_http_request(int sockfd, User_list* users)
    {
        // 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;
        }
    
        // terminate the string
        buff[n] = 0;
    
        char * curr = buff;
    
        // parse the method
        Request* req = parse_request(curr);
        printf("REQUEST BODY IS \n\n%s\n", req->body);
        if (strncmp(req->url, "/welcome_page?start=Start", 24)  == 0){
            printf("matches start");
            if (req->method == GET){
                    get_request(buff,sockfd, "3_first_turn.html");
            }
            if (req->method == POST){
                Response* response = redirect(req, "gameover_page");
                
                char *resp = parse_response(response);
    		    post_request(buff,sockfd, "7_gameover.html",resp); 
                free(response);
            }
        }
        else if (strncmp(req->url, "/gameover_page", 14) == 0){
            if(req->method == GET){
                get_request(buff,sockfd, "7_gameover.html");
                printf("i return false\n");
            }
        }
        else if (strncmp(req->url, "/welcome_page", 13) == 0){
            if(req->method == GET){
                get_request(buff,sockfd, "2_start.html");
            }
            if (req->method == POST){
                Response* response = redirect(req, "gameover_page");
                
                char *resp = parse_response(response);
    		    post_request(buff,sockfd, "7_gameover.html",resp); 
                free(response);
            }
        }
        else if (*req->url == '/' && (strlen(req->url) == 1)){
            if (req->method == POST)
            {
                char *name = strchr(req->body, '=')+1;
                printf("**%s**\n", name);
                if (name != NULL){
                    // for (int i=0; i < users->n_users; i++){
                    //     if(users->users[i]->name == NULL){
                    //         users->users[i]->name = name;
                    //     }
                    // }
                    Response* response = redirect(req, "welcome_page");
                    char *resp = parse_response(response);
                    post_request(buff,sockfd, "2_start.html",resp); 
                    free(resp);
                    free(response);   
                }
            }
            else if (req->method == GET)
            {
                // printf("matches a / url but url isize is %zu\n\n", strlen(req->url));
                // Response* resp = initialise_session(req);
                // char* resp_string = parse_response(resp);
                // printf("COOKIE CREATING RESP %s\n", resp_string);
                // player_session(buff, sockfd, "1_welcome.html", resp_string);
                // free(resp_string);
                // free(resp); 
               get_request(buff,sockfd, "1_welcome.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)
        {
            free_request(req);
            perror("write");
            return false;
        }
    	free_request(req);
        return true;
    }
    
    int main(int argc, char * argv[])
    {
        if (argc < 3)
        {
            fprintf(stderr, "usage: %s ip port\n", argv[0]);
            return 0;
        }
    
        // 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]));
        char *user_id = malloc(sizeof(char)*(strlen(argv[1])+strlen(argv[2])));
        user_id[0] = '\0';
        strcat(user_id,argv[1]);
        strcat(user_id,argv[2]);
        printf("%s\n\n\\n", user_id);
        assert(user_id);
        free(user_id);
        // 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);
            }
            User_list* users = initialise_player_list();
            // 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 from %s on socket %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, users))
                    {
                        close(i);
                        FD_CLR(i, &masterfds);
                    }
                }
            }
        free(users);
        }
    
        return 0;
    }