From 3b21897219693152b35d3051ed45da352da16d91 Mon Sep 17 00:00:00 2001
From: Abhisha Nirmalathas <a.nirmalathas1@student.unimelb.edu.au>
Date: Thu, 11 Apr 2019 22:05:27 +1000
Subject: [PATCH] adding request struct and parser

---
 Makefile        |  32 +++++++++
 hashtable.c     | 176 ++++++++++++++++++++++++++++++++++++++++++++++++
 hashtable.h     |  24 +++++++
 http-parser.c   |  57 ++++++++++++++++
 http-parser.h   |  22 ++++++
 http-response.c |  21 ++++++
 http-response.h |  14 ++++
 http-server.c   |  63 ++++++-----------
 8 files changed, 367 insertions(+), 42 deletions(-)
 create mode 100644 Makefile
 create mode 100644 hashtable.c
 create mode 100644 hashtable.h
 create mode 100644 http-parser.c
 create mode 100644 http-parser.h
 create mode 100644 http-response.c
 create mode 100644 http-response.h

diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..55a5580
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,32 @@
+CC     = gcc
+CFLAGS = -Wall -std=c99 -g
+# modify the flags here ^
+EXE    = image_tagger
+OBJ    = hashtable.o http-parser.o http-response.o http-server.o
+# add any new object files here ^
+# top (default) target
+all: $(EXE)
+
+# how to link executable
+$(EXE): $(OBJ)
+	$(CC) $(CFLAGS) -o $(EXE) $(OBJ) -lm
+
+# other dependencies
+http-parser.o: http-parser.h hashtable.h
+http-response.o: http-response.h http-parser.h hashtable.h
+http-server.o: http-parser.h
+
+# ^ add any new dependencies here (for example if you add new modules)
+
+
+# phony targets (these targets do not represent actual files)
+.PHONY: clean cleanly all CLEAN
+
+# `make clean` to remove all object files
+# `make CLEAN` to remove all object and executable files
+# `make cleanly` to `make` then immediately remove object files (inefficient)
+clean:
+	rm -f $(OBJ)
+CLEAN: clean
+	rm -f $(EXE)
+cleanly: all clean
\ No newline at end of file
diff --git a/hashtable.c b/hashtable.c
new file mode 100644
index 0000000..c4955f3
--- /dev/null
+++ b/hashtable.c
@@ -0,0 +1,176 @@
+/* This hash table has been adapted by Abhisha Nirmalathas.
+	The original hash table it was adapted from was made
+ by Matt Farrugia <matt.farrugia@unimelb.edu.au, for Design of Algorithms.
+ This hash table uses separate chaining with an array.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include "hashtable.h"
+
+typedef struct bucket Bucket;
+struct bucket {
+	char **keys; //array of words
+	char **values; //array of weighted frequencies, lower value is higher frequency
+	int capacity; //space available in bucket
+	int n_elems; //number of elements in bucket
+};
+
+struct table {
+	int size; //number of buckets
+	Bucket *buckets; //list of buckets
+};
+
+/************************************************************************/
+// moves recently accessed key & value to the front of bucket
+void move_to_front(Bucket *bucket, int hash_value);
+
+ unsigned int seed = 73802;
+ unsigned int xor_hash(const char *key, unsigned int size) {
+ 	unsigned int h = seed;
+
+ 	int i;
+ 	for (i = 0; key[i] != '\0'; i++) {
+ 		h = h ^ ((h << 5) + key[i] + (h >> 2));
+ 	}
+
+ 	return h % size;
+ }
+/************************************************************************/
+Bucket *add_to_bucket(Bucket *bucket, char *key, char *value) {
+	/*Adds new key and value to existing bucket*/
+	bucket->n_elems = bucket->n_elems + 1;
+	// allocates more memory if not enough space
+	if (bucket->capacity <= bucket->n_elems){
+		bucket->capacity = 2*(bucket->capacity + 1);
+		bucket->keys = realloc(bucket->keys,sizeof(char*) * (bucket->capacity));
+		bucket->values = realloc(bucket->values,sizeof(char*) * (bucket->capacity));
+	}
+	assert(bucket->keys);
+	assert(bucket->values);
+	//adds new key and avlue to end of bucket
+	bucket->values[bucket->n_elems-1] = value;
+	bucket->keys[bucket->n_elems-1] = key;
+	return bucket;
+}
+/************************************************************************/
+// Warning: does not free bucket->next
+void free_bucket(Bucket bucket) {
+	/*Deallocates memory in bucket*/
+	free(bucket.keys);
+	free(bucket.values);
+}
+
+
+/************************************************************************/
+HashTable *new_hash_table(int size) {
+	/*Builds a new hash table of input size*/
+	HashTable *table = malloc(sizeof *table);
+	assert(table);
+	table->size = size;
+	table->buckets = malloc(size * (sizeof *table->buckets));
+	assert(table->buckets);
+	int i;
+	for (i = 0; i < size; i++) {
+		table->buckets[i].keys = malloc(sizeof(char*));
+		table->buckets[i].values = malloc(sizeof(char*));
+		table->buckets[i].capacity = 0;
+		table->buckets[i].n_elems = 0;
+	}
+	return table;
+}
+/************************************************************************/
+void free_hash_table(HashTable *table) {
+	/*Deallocates memory from hash table*/
+	assert(table != NULL);
+	int i;
+	for (i = 0; i < table->size; i++) {
+		free_bucket(table->buckets[i]);
+	}
+	free(table->buckets);
+	free(table);
+}
+
+/************************************************************************/
+bool equal(char *a, char *b) {
+	/*Returns if a string is equal to another string*/
+	return strcmp(a, b) == 0;
+}
+
+/************************************************************************/
+void hash_table_put(HashTable *table, char *key, char *value) {
+	/*Inserts new key into hash table with weighted value*/
+	assert(table != NULL);
+	assert(key != NULL);
+	int hash_value = xor_hash(key, table->size);
+	// look for existing key
+	Bucket *bucket = &table->buckets[hash_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){
+	/*moves recently accessed key & value to the front of bucket*/
+	char* temp_key;
+	char* temp_value;
+	if (bucket->n_elems <= 1 && index >= bucket->n_elems){
+		return;
+	}
+	// swaps position of first element with recently accessed
+	temp_key = bucket->keys[index];
+	temp_value = bucket->values[index];
+	bucket->keys[index] = bucket->keys[0];
+	bucket->values[index] = bucket->values[0];
+	bucket->values[0] = temp_value;
+	bucket->keys[0] = temp_key;
+}
+/************************************************************************/
+char* hash_table_get(HashTable *table, char *key) {
+	/*Checks if key in hash table and returns value if present*/
+	char* freq = malloc(60*sizeof(char));
+	assert(table != NULL);
+	assert(key != NULL);
+
+	int hash_value = xor_hash(key, table->size);
+
+	// look for existing key
+	Bucket *bucket = &table->buckets[hash_value];
+	for (int i=0; i < bucket->n_elems; i++){
+		if (equal(bucket->keys[i], key)){
+			freq = bucket->values[i];
+			move_to_front(bucket, i);
+			break;
+		}
+	}
+	//returns value of key if found, else return -1
+	return freq;
+}
+/************************************************************************/
+bool hash_table_has(HashTable *table, char *key) {
+	/*Checks if the key exists in hash table*/
+	assert(table != NULL);
+	assert(key != NULL);
+
+	int hash_value = xor_hash(key, table->size);
+
+	// look for existing key
+	Bucket *bucket = &table->buckets[hash_value];
+	for (int i=0; i < bucket->n_elems; i++){
+		if (equal(bucket->keys[i], key)){
+			return true;
+		}
+	}
+	return false;
+}
diff --git a/hashtable.h b/hashtable.h
new file mode 100644
index 0000000..9dbea8a
--- /dev/null
+++ b/hashtable.h
@@ -0,0 +1,24 @@
+/* This hash table has been adapted by Abhisha Nirmalathas.
+	The original hash table it was adapted from was made
+ by Matt Farrugia <matt.farrugia@unimelb.edu.au, for Design of Algorithms.
+ This hash table uses separate chaining with an array.
+*/
+#include <stdbool.h>
+
+typedef struct table HashTable;
+
+
+//Creates a new hash table of input size
+HashTable *new_hash_table(int size);
+
+//Deallocates memory from hash table
+void free_hash_table(HashTable *table);
+
+//Inserts new key into hash table with weighted value
+void hash_table_put(HashTable *table, char *key, char *value);
+
+//checks if key in hash table and returns value if present
+char* hash_table_get(HashTable *table, char *key);
+
+//Checks if the key exists in hash table
+bool hash_table_has(HashTable *table, char *key);
diff --git a/http-parser.c b/http-parser.c
new file mode 100644
index 0000000..92ac25c
--- /dev/null
+++ b/http-parser.c
@@ -0,0 +1,57 @@
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "http-parser.h"
+
+#define MAX_URL_SIZE 60
+#define MAX_HEADER_SIZE 300
+#define MAX_VERSION_SIZE 10
+
+Request* parse_request(char* request_message){
+    Request *req = malloc(sizeof *req);
+	assert(req);
+    int n_headers = 10;
+    if (strncmp(request_message, "GET ", 4) == 0){
+        req->method = GET;
+        request_message += 4;
+        printf("Its a get\n");
+    }
+    else if (strncmp(request_message, "POST ", 5) == 0){
+        request_message += 5;
+        req->method = POST;
+        printf("Its a post\n");
+    }
+    req->url = strtok(request_message, " ");
+    request_message += strlen(req->url)+1;
+    req->version = strtok(request_message, "\r\n");
+    request_message+= strlen(req->version)+2;
+    req->header = new_hash_table(10);
+
+    char header_field_name[MAX_HEADER_SIZE];
+    char header_value[MAX_HEADER_SIZE];
+    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);
+    }
+    if(*request_message != '\r'||*request_message != '\n'){
+        req->body = request_message;
+    }
+    else{
+        req->body = "";
+    }
+    return req;
+}
+
+void free_request(Request* req){
+    free_hash_table(req->header);
+    free(req);
+}
\ No newline at end of file
diff --git a/http-parser.h b/http-parser.h
new file mode 100644
index 0000000..501f332
--- /dev/null
+++ b/http-parser.h
@@ -0,0 +1,22 @@
+#include <stdbool.h>
+#include "hashtable.h"
+typedef enum
+{
+    GET,
+    POST,
+    UNKNOWN
+} METHOD;
+
+typedef struct Request {
+    METHOD method;
+    char *url;
+    char *version;
+    HashTable *header;
+    char *body;
+} Request;
+
+
+//Creates a new hash table of input size
+Request* parse_request(char* request_message);
+
+void free_request(Request* req);
\ No newline at end of file
diff --git a/http-response.c b/http-response.c
new file mode 100644
index 0000000..feaf292
--- /dev/null
+++ b/http-response.c
@@ -0,0 +1,21 @@
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "http-parser.h"
+#include "http-response.h"
+
+#define MAX_URL_SIZE 60
+#define MAX_HEADER_SIZE 300
+#define MAX_VERSION_SIZE 10
+
+Response* redirect(Request* request_message){
+    
+}
+
+void free_response(Response* resp){
+    free_hash_table(resp->header);
+    free(resp);
+}
\ No newline at end of file
diff --git a/http-response.h b/http-response.h
new file mode 100644
index 0000000..7fdf1ba
--- /dev/null
+++ b/http-response.h
@@ -0,0 +1,14 @@
+#include <stdbool.h>
+#include "hashtable.h"
+
+typedef struct Response {
+    int status_code;
+    char *version;
+    HashTable *header;
+    char* phrase;
+    char *body;
+} Response;
+
+
+//Creates a new hash table of input size
+Response *redirect(Request *req);
\ No newline at end of file
diff --git a/http-server.c b/http-server.c
index 562c412..c6b45c5 100644
--- a/http-server.c
+++ b/http-server.c
@@ -19,6 +19,7 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include "http-parser.h"
 
 // constants
 static char const * const HTTP_200_FORMAT = "HTTP/1.1 200 OK\r\n\
@@ -29,13 +30,7 @@ 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;
 
-// represents the types of method
-typedef enum
-{
-    GET,
-    POST,
-    UNKNOWN
-} METHOD;
+
 
 bool get_request(char* buff, int sockfd, char* file_name){
     // get the size of the file
@@ -68,7 +63,7 @@ bool get_request(char* buff, int sockfd, char* file_name){
 bool post_request(char *buff, int sockfd, char* file_name){
 	// 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 * 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;
@@ -113,6 +108,7 @@ bool post_request(char *buff, int sockfd, char* file_name){
 	return true;
 }
 
+
 static bool handle_http_request(int sockfd)
 {
     // try to read the request
@@ -133,46 +129,28 @@ static bool handle_http_request(int sockfd)
     char * curr = buff;
 
     // parse the method
+    printf("cur is:      %s\n\n", curr);
     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;
-	
-printf("**************THE CURR IS %s\n\n\n", curr);
-   if (strncmp(curr, "?start=Start", 12)  == 0){
-	printf("matches start");
-	if (method == GET){
-        	get_request(buff,sockfd, "3_first_turn.html");
-	}
+    Request* req = parse_request(curr);
+    printf("%s\n%s\n", req->url, req->version);
+    if (strncmp(req->url, "?start=Start", 12)  == 0){
+        printf("matches start");
+        if (req->method == GET){
+                get_request(buff,sockfd, "3_first_turn.html");
+        }
+        if (req->method == POST){
+                post_request(buff,sockfd, "7_gameover.html");
+        }
     }
     // assume the only valid request URI is "/" but it can be modified to accept more files
-    else if (*curr == ' ')
-        if (method == GET)
+
+    else if (*req->url == '/')
+        if (req->method == GET)
         {
-            if (strncmp(curr, "start=Start", 11) == 0)
-            {
-                printf("is start start mort");
-            }
             printf("%s\n", curr);
             get_request(buff,sockfd, "1_welcome.html");
         }
-        else if (method == POST)
+        else if (req->method == POST)
         {
 		post_request(buff,sockfd, "2_start.html");    
         }
@@ -182,10 +160,11 @@ printf("**************THE CURR IS %s\n\n\n", curr);
     // send 404	
     else if (write(sockfd, HTTP_404, HTTP_404_LENGTH) < 0)
     {
+        free_request(req);
         perror("write");
         return false;
     }
-	
+	free_request(req);
     return true;
 }
 
-- 
GitLab