diff --git a/hashtable.c b/hashtable.c index a02d701a184cc1ad77aca95678480583a7b5b54a..4238967ba680b9bac7e68eedbc90db66818c5f27 100644 --- a/hashtable.c +++ b/hashtable.c @@ -54,6 +54,8 @@ Bucket *add_to_bucket(Bucket *bucket, char *key, char *value) { //adds new key and avlue to end of bucket bucket->values[bucket->n_elems-1] = value; bucket->keys[bucket->n_elems-1] = key; + // printf("key is %s\n\n", bucket->keys[bucket->n_elems-1]); + // printf("value is %s\n\n", bucket->values[bucket->n_elems-1]); return bucket; } /************************************************************************/ @@ -111,14 +113,12 @@ void hash_table_put(HashTable *table, char *key, char *value) { for (int i=0; i < bucket->n_elems; i++){ if (equal(bucket->keys[i], key)){ bucket->values[i] = value; - move_to_front(bucket, i); return; } } // adds key and value as not in hash table bucket = add_to_bucket(bucket, key, value); table->buckets[hash_value] = *bucket; - move_to_front(bucket, bucket->n_elems - 1); } /************************************************************************/ void move_to_front(Bucket *bucket, int index){ @@ -176,7 +176,7 @@ bool hash_table_has(HashTable *table, char *key) { return false; } char* print_hash_map(HashTable *table){ - char* headers = calloc(100,sizeof(char)); + char* headers = calloc(800,sizeof(char)); assert(headers); for (int i = 0; i < table->size; i++) { if (table->buckets[i].n_elems > 0){ @@ -204,4 +204,15 @@ void free_cookie(HashTable *headers) { free(bucket->values[i]); } } -} \ No newline at end of file +} + +void free_hash_map(HashTable *table){ + for (int i = 0; i < table->size; i++) { + if (table->buckets[i].n_elems > 0){ + for (int j=0; j< table->buckets[i].n_elems; j++){ + free(table->buckets[i].keys[j]); + free(table->buckets[i].values[j]); + } + } + } +} diff --git a/hashtable.h b/hashtable.h index 4f503861db0dfb7cb9a084f685148586a1352ab5..0a266fdf7d2e6f37cb38f82f4106e4c9728b12ed 100644 --- a/hashtable.h +++ b/hashtable.h @@ -25,4 +25,6 @@ bool hash_table_has(HashTable *table, char *key); char* print_hash_map(HashTable *table); -void free_cookie(HashTable *headers); \ No newline at end of file +void free_cookie(HashTable *headers); + +void free_hash_map(HashTable *table); \ No newline at end of file diff --git a/http-parser.c b/http-parser.c index a5a5a27e83f58a38fe73c91a9e568c9d37fb8416..1d80197a72e17b826d2e120b5c2a47f76b931eb5 100644 --- a/http-parser.c +++ b/http-parser.c @@ -9,20 +9,20 @@ #include <string.h> #include <assert.h> #include "http-parser.h" +#include "hashtable.h" // user-determined constants for requests #define MAX_URL_SIZE 60 -#define MAX_HEADER_SIZE 600 +#define MAX_HEADER_SIZE 200 #define MAX_VERSION_SIZE 10 -#define MAX_HEADERS 10 +#define MAX_HEADERS 20 Request* parse_request(char* request_message){ /** * Reads a requests and creates a request object containing the parsed information * */ - Request *req = calloc(1, sizeof(Request)); + Request *req = calloc(1, sizeof *req); assert(req); - memset(req, 0, sizeof(Request)); // parses the method of the request if (strncmp(request_message, "GET ", 4) == 0){ @@ -47,20 +47,13 @@ Request* parse_request(char* request_message){ //parses the headers req->header = new_hash_table(MAX_HEADERS); - char header_field_name[MAX_HEADER_SIZE]; - char header_value[MAX_HEADER_SIZE]; + // char *header_field_name = calloc(MAX_HEADER_SIZE, sizeof(char)); + // char *header_value = calloc(MAX_HEADER_SIZE, sizeof(char)); while(*request_message != '\r' && *request_message != '\n'){ - // printf("req message first char %d\n", *request_message); - strcpy(header_field_name,strtok(request_message, " ")); - // printf("header field name: %s\n", header_field_name); - request_message += strcspn(request_message, " ")+1; - strcpy(header_value,strtok(request_message, "\r\n")); - // printf("header value: %s\n", header_value); - request_message += strcspn(request_message, "\r\n")+2; - hash_table_put(req->header, header_field_name, header_value); + request_message = insert_headers(request_message, req); + } request_message += strcspn(request_message, "\r\n")+2; - // parses the body if(*request_message != '\r'||*request_message != '\n'){ req->body = request_message; @@ -71,10 +64,26 @@ Request* parse_request(char* request_message){ return req; } +char* insert_headers(char* request_message, Request * req){ + char *header_field_name = calloc(MAX_HEADER_SIZE, sizeof(char)); + char *header_value = calloc(MAX_HEADER_SIZE, sizeof(char)); + memset(header_field_name,0,strlen(header_field_name)); + memset(header_value,0,strlen(header_value)); + size_t field_len = strcspn(request_message, " "); + memcpy(header_field_name, request_message,field_len); + request_message += field_len+1; + size_t value_len = strcspn(request_message, "\r\n"); + memcpy(header_value,request_message, value_len); + request_message += value_len+2; + hash_table_put(req->header, header_field_name, header_value); + return request_message; +} + void free_request(Request* req){ /** * Deallocates memory * */ + free_hash_map(req->header); free_hash_table(req->header); free(req); } diff --git a/http-parser.h b/http-parser.h index 203079e02a40928617fcebe05b6c2d1f8dbfadfd..03433854d3b4f6e63972565c8201da129f92b148 100644 --- a/http-parser.h +++ b/http-parser.h @@ -27,4 +27,6 @@ typedef struct Request { Request* parse_request(char* request_message); // Deallocates memory -void free_request(Request* req); \ No newline at end of file +void free_request(Request* req); + +char* insert_headers(char* request_message, Request * req); \ No newline at end of file diff --git a/http-response.c b/http-response.c index 626b5d95ee0c02f51e5055ec4daacbd8937f5868..66efeb7ae8bdf3db240c0195c4b4cc07c0598cc2 100644 --- a/http-response.c +++ b/http-response.c @@ -16,7 +16,7 @@ #define MAX_URL_SIZE 60 #define MAX_HEADER_SIZE 300 #define MAX_VERSION_SIZE 10 -#define MAX_N_HEADERS 2 +#define MAX_N_HEADERS 4 char* cookie_generator(){ /** @@ -24,7 +24,7 @@ char* cookie_generator(){ * */ char* cookie_value; cookie_value = (char*) malloc(sizeof(char)*60); - snprintf(cookie_value, 60, "sessionToken=%d; Expires=Wed, 01 Apr 2019 10:10:10 GMT", rand()); + snprintf(cookie_value, 60, "id=%d", rand() % (111-0+1)); return cookie_value; } @@ -44,7 +44,9 @@ Response* initialise_session(Request* request){ // sets the cookies char *cookie = cookie_generator(); + hash_table_put(resp->header, "Content-length: ", "%ld"); hash_table_put(resp->header, "Set-cookie: ", cookie); + hash_table_put(resp->header, "Content-type: ", "text/html"); // sends empty body resp->body=""; @@ -69,8 +71,14 @@ char* parse_response(Response* response){ strncat(response_string, headers, strlen(headers)); strcat(response_string, "\r\n"); strcat(response_string, response->body); - // free(headers); - return response_string; + strcat(response_string, "\r\n"); + char * new_response_string = calloc(100,sizeof(char)); + assert(new_response_string); + strncpy(new_response_string, response_string, strlen(response_string)); + free(headers); + free(response_string); + return new_response_string; + // return response_string; } diff --git a/http-server.c b/http-server.c index 7317bc02bf79bbdb64dd3b838d4fc7564419bea1..17528a24ab792ac0a12b464115d32be39196d373 100644 --- a/http-server.c +++ b/http-server.c @@ -25,6 +25,7 @@ #include <sys/types.h> #include <unistd.h> #include "response-formatter.h" +#include "hashtable.h" #include "http-parser.h" #include "http-response.h" #include "hashtable.h" @@ -40,7 +41,10 @@ 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; -volatile sig_atomic_t kill; +// global structures required for memory deallocation +static Request* req; +static User_list* user_list; +int keep_alive = 1; bool player_session(char* buff, int sockfd, char* file_name, char* response){ @@ -56,21 +60,23 @@ bool player_session(char* buff, int sockfd, char* file_name, char* response){ perror("write"); return false; } - // send the file + // read the content of the HTML file int filefd = open(file_name, O_RDONLY); - do - { - n = sendfile(sockfd, filefd, NULL, 2048); - } - while (n > 0); + n = read(filefd, buff, st.st_size); + clean_trailing_buffer(buff); if (n < 0) { - perror("sendfile"); + perror("read"); close(filefd); return false; } close(filefd); - return true; + if (write(sockfd, buff, st.st_size) < 0) + { + perror("write"); + return false; + } + return true; } bool text_render_response(char *buff, int sockfd, char* file_name, char* text){ /** @@ -220,7 +226,7 @@ bool text_render_game_play(char *buff, int sockfd, char* file_name, char* text, return true; } -static bool handle_http_request(int sockfd, User_list *users){ +static bool handle_http_request(int sockfd, User_list *user_list){ /** * Parses the http request and sends the approapriate response * */ @@ -241,71 +247,66 @@ static bool handle_http_request(int sockfd, User_list *users){ char * curr = buff; // parse the method - Request* req = parse_request(curr); - User* user = get_current_user(users, sockfd); - // printf("REQUEST BODY IS \n\n%s\n", req->url); - + req = parse_request(curr); + char* cookie = hash_table_get(req->header, "Cookie:"); + int id = atoi(cookie+3); + User* user = get_current_user(user_list, id); // user should quit game if(strncmp(req->body, "quit=Quit", 9) == 0){ - printf("run\n\n\n\n\\n"); - change_player_status(sockfd,users, QUIT); - response(buff,sockfd, "7_gameover.html"); - // free_request(req); - // return false; + if (user != NULL){ + change_player_status(id,user_list, QUIT); + response(buff,sockfd, "7_gameover.html"); + } } // user starting game else if (strncmp(req->url, "/?start=Start", 24) == 0){ - // user on first turn - if (req->method == GET){ - change_player_status(sockfd, users, READY); - int round = change_player_round(sockfd, users); - game_change(buff,sockfd, "3_first_turn.html", round); - } - if (req->method == POST){ - if(strncmp(req->body, "keyword=", 8) == 0){ - - // checks if other player has quit - if(should_player_quit(users)){ - printf("is quit"); - change_player_status(sockfd,users, QUIT); - response(buff,sockfd, "7_gameover.html"); - // free_request(req); - // keep_alive = 0; - // return false; - } - - // checks if other player has not completed previous round - else if (different_round_discard(sockfd,users)){ - game_change(buff,sockfd, "5_discarded.html", user->round); - } + if (user != NULL){ + if (req->method == GET){ + change_player_status(id, user_list, READY); + int round = change_player_round(id, user_list); + game_change(buff,sockfd, "3_first_turn.html", round); + } + if (req->method == POST){ + if(strncmp(req->body, "keyword=", 8) == 0){ - // checks if game won - else if(player_won(users)){ - response(buff,sockfd, "6_endgame.html"); - change_player_status(sockfd, users, WAIT); - } + // checks if other player has quit + if(should_player_quit(user_list)){ + change_player_status(id,user_list, QUIT); + response(buff,sockfd, "7_gameover.html"); + } - //checks if the other player is not ready and discards keyword - else if(!players_ready(users) && user!= NULL){ - game_change(buff,sockfd, "5_discarded.html", user->round); - //request(buff,sockfd, "5_discarded.html"); - } - else{ - char* keyword = add_keyword(sockfd, users, req->body); + // checks if other player has not completed previous round + else if (different_round_discard(id,user_list)){ + game_change(buff,sockfd, "5_discarded.html", user->round); + } - // game ends as submitted keyword has been submitted by other player - if(has_match_ended(users, keyword, sockfd)){ + // checks if game won + else if(player_won(user_list)){ response(buff,sockfd, "6_endgame.html"); - change_all_status(users, COMPLETE); + change_player_status(id, user_list, WAIT); + } + + //checks if the other player is not ready and discards keyword + else if(!players_ready(user_list) && user!= NULL){ + game_change(buff,sockfd, "5_discarded.html", user->round); + //request(buff,sockfd, "5_discarded.html"); } else{ - // keyword hasn't been submitted yet so is accepted and rendered on screen - if(user!= NULL){ - char* keywords = return_all_keywords(user); - text_render_game_play(buff,sockfd, "4_accepted.html", keywords, user->round); - free(keywords); + char* keyword = add_keyword(id, user_list, req->body); + // game ends as submitted keyword has been submitted by other player + if(has_match_ended(user_list, keyword, id)){ + response(buff,sockfd, "6_endgame.html"); + change_all_status(user_list, COMPLETE); + } + else{ + // keyword hasn't been submitted yet so is accepted and rendered on screen + if(user!= NULL){ + char* keywords = return_all_keywords(user); + text_render_game_play(buff,sockfd, "4_accepted.html", keywords, user->round); + free(keywords); + } } } } @@ -319,44 +320,40 @@ static bool handle_http_request(int sockfd, User_list *users){ // user's name stored and rendered on screen if (req->method == POST) { - char *name = strchr(req->body, '=')+1; - // printf("**%s**\n", name); + size_t name_len = strlen(req->body+5); + char *name = calloc(name_len+1, sizeof(char)); + assert(name); + memcpy(name,req->body+5, name_len); if (name != NULL){ - for (int i=0; i < users->n_users; i++){ - if(users->users[i]->id == sockfd){ - users->users[i]->name = name; + for (int i=0; i < user_list->n_users; i++){ + if(user_list->users[i]->id == id){ + user_list->users[i]->name = name; } } - // get_request(buff,sockfd, "2_start.html"); text_render_response(buff,sockfd, "2_start.html", req->body+5); } } - // user is created - else if (req->method == GET) + // new user is created + else if (req->method == GET && !hash_table_has(req->header, "Cookie:")) { - // 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); - // User* new_player = new_user(sockfd); - // add_user(new_player, users); - // char* cookie = hash_table_get(resp->header, "Set-cookie: ")+13; - // cookie = strtok(cookie, ";"); - // printf("the cookie token is %s*****\n", cookie); - // free(resp_string); - // free_response(resp); - - // ensures user is not already playing/in system - for(int i =0; i< users->n_users; i++){ - if(sockfd == users->users[i]->id){ - return false; - } + // create a new user and assign it a new cookie + Response* resp = initialise_session(req); + char* resp_string = parse_response(resp); + char* cookie = hash_table_get(resp->header, "Set-cookie: "); + User* new_player = new_user(atoi(cookie+3)); + add_user(new_player, user_list); + player_session(buff, sockfd, "1_welcome.html", resp_string); + free(cookie); + free(resp_string); + free_response(resp); + } + else if (req->method == GET){ + if (user != NULL){ + // Resets returning user to round 1 + user->round = 0; + text_render_response(buff,sockfd, "2_start.html", user->name); } - User* new_player = new_user(sockfd); - add_user(new_player, users); - response(buff,sockfd, "1_welcome.html"); } else // unrecognised methods @@ -369,25 +366,7 @@ static bool handle_http_request(int sockfd, User_list *users){ perror("write"); return false; } - printf("the numer of users is %d\n", users->n_users); - for(int i=0; i < users->n_users; i++){ - printf("USER ID %d", users->users[i]->id); - if(users->users[i]->status == READY){ - printf("is ready\n"); - } - if(users->users[i]->status == WAIT){ - printf("is wait\n"); - } - if(users->users[i]->status == QUIT){ - printf("is quit\n"); - } - if(users->users[i]->status == COMPLETE){ - printf("is complete\n"); - } - if(users->users[i]->status == RESTART){ - printf("is restart\n"); - } - } + free(cookie); free_request(req); return true; } @@ -396,8 +375,10 @@ void exit_handler(int signum){ /** * Closes TCP Connection */ - kill = 1; - printf("keep alive changed"); + keep_alive = 0; + if (user_list){ + free_users(user_list); + } } @@ -433,6 +414,7 @@ int main(int argc, char * argv[]) // if ip parameter is not specified serv_addr.sin_addr.s_addr = inet_addr(argv[1]); serv_addr.sin_port = htons(atoi(argv[2])); + printf("image_tagger server is now running at IP: %s on port %s\n", argv[1], argv[2]); // bind address to socket if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { @@ -451,7 +433,7 @@ int main(int argc, char * argv[]) int maxfd = sockfd; // game players - User_list* users = initialise_player_list(); + user_list = initialise_player_list(); signal(SIGINT, exit_handler); while (!kill) { @@ -494,7 +476,7 @@ int main(int argc, char * argv[]) } } // a request is sent from the client - else if (!handle_http_request(i,users)) + else if (!handle_http_request(i,user_list)) { close(i); FD_CLR(i, &masterfds); @@ -502,7 +484,5 @@ int main(int argc, char * argv[]) } } } - printf("break loop"); - free_users(users); return 0; } diff --git a/user.c b/user.c index 1bf913a767920d33a34e19ba387133921d30dd2e..d5cd6a493b7e26c68d4fb673aa6173a376d39da4 100644 --- a/user.c +++ b/user.c @@ -11,8 +11,8 @@ #include "user.h" // implementation specific constants for users -#define INITIAL_KEYWORDS 5 -#define INITAL_N_USERS 5 +#define INITIAL_KEYWORDS 20 +#define INITAL_N_USERS 10 #define INITIAL_KEYWORD_LENGTH 30 User* new_user(int id){ @@ -60,9 +60,6 @@ void resize_keywords(User* user, char* keyword){ // adds keyword to user user->keywords[user->n_keywords] = keyword; user->n_keywords++; - for(int j=0; j<user->n_keywords; j++){ - printf("%d: %s\n",j, user->keywords[j]); - } } @@ -108,6 +105,7 @@ void free_users(User_list* user_list){ // free(&users->users[i]->keywords[j]); // } free(user_list->users[i]->keywords); + free(user_list->users[i]->name); free(user_list->users[i]); } free(user_list->users); @@ -118,22 +116,36 @@ bool should_player_quit(User_list* user_list){ /** * Checks if any player has quit, and tells player to quit if so * */ + int count = 0; for(int i=0; i < user_list->n_users; i++){ if (user_list->users[i]->status == QUIT){ - return true; + count++; } } + // checking edge case of 3 users, with one inactive user that has quit + if(count + 1 == user_list->n_users){ + return true; + } return false; } bool players_ready(User_list* user_list){ /** * checks if all players are ready * */ + int count =0; for(int i=0; i < user_list->n_users; i++){ + // other player is not ready if (user_list->users[i]->status != READY){ + count++; + } + // only one player, need to wait + else if (user_list->n_users == 1){ return false; } } + if(count + 1 == user_list->n_users){ + return false; + } return true; } void change_player_status(int user_id, User_list* user_list, STATUS status){ @@ -195,11 +207,8 @@ bool keyword_match(User* user, char* keyword){ /** * Checks if keyword has already been submitted * */ - printf("keyword is %s\n\n", keyword); for(int i=0; i <user->n_keywords;i++){ - printf("words being matched is with %s and %s\n", keyword, user->keywords[i]); if(strcmp(keyword, user->keywords[i]) == 0){ - printf("match is with %s and %s\n", keyword, user->keywords[i]); return true; } } @@ -263,9 +272,8 @@ char* return_all_keywords(User* user){ /** * Returns all the keywords for a particular user * */ - printf("%d\n\n\n", user->n_keywords); - int length = (user->n_keywords)*(INITIAL_KEYWORD_LENGTH+1); + int length = (user->n_keywords)*(INITIAL_KEYWORD_LENGTH+3); char *keywords = calloc(length, user->n_keywords); assert(keywords); @@ -276,6 +284,5 @@ char* return_all_keywords(User* user){ } // terminating byte keywords[strlen(keywords)-1] = '\0'; - printf("**** %s \n ", keywords); return keywords; } \ No newline at end of file