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