diff --git a/src/cache.c b/src/cache.c index c72975cdd..0713d3c7e 100644 --- a/src/cache.c +++ b/src/cache.c @@ -12,6 +12,24 @@ struct cache_entry *alloc_entry(char *path, char *content_type, void *content, i /////////////////// // IMPLEMENT ME! // /////////////////// + //allocate space for the struct were going to return + struct cache_entry *ce = malloc(sizeof *ce); //size of dereference ce if dereffed it is cache_entry + //set all fields to values being passed in to initialize + //have a pointer to something but have to make sure what you are pointing to persists(make sure it doesnt accidentally get freed) + ce->path = malloc(strlen(path) + 1); //so make a copy of path we know will persist. +1 for null terminator + strcpy(ce->path, path); //(dest, args) copy path into allocated memory. this is deep copy. + + ce->content_type = malloc(strlen(content_type) +1); + strcpy(ce->content_type, content_type); + + ce->content = malloc(content_length); //content is void pointer so dont know what type so cant use strlen, but know its length so can use that + memcpy(ce->content, content, content_length); //(dest, src, size) + + ce->content_length = content_length; //int so dont have to malloc + + ce->prev = ce->next = NULL; + + return ce; } /** @@ -22,6 +40,12 @@ void free_entry(struct cache_entry *entry) /////////////////// // IMPLEMENT ME! // /////////////////// + //have to free the cache entry passed in but have to free everything we malloc'ed space for + //free everything in stucture, then free the structure itself so entry last + free(entry->path); + free(entry->content_type); + free(entry->content); + free(entry); } /** @@ -93,7 +117,19 @@ struct cache *cache_create(int max_size, int hashsize) { /////////////////// // IMPLEMENT ME! // - /////////////////// + /////////////////// going in opposite order from cache free below. also refer to cache struct in cache.h + //allocate cache + struct cache *cache = malloc(sizeof *cache); + //functions in hashtable.h and c + cache->index = hashtable_create(hashsize, NULL); //size and pointer to a function (that takes in certain values and returns int) + //initialize head and tail null + cache->head = cache->tail = NULL; + + cache->max_size = max_size; //int so dont allocate space + + cache->cur_size = 0; + + return cache; } void cache_free(struct cache *cache) @@ -113,6 +149,15 @@ void cache_free(struct cache *cache) free(cache); } +void clean_lru(struct cache *cache) +{ + while (cache->cur_size > cache->max_size) { + struct cache_entry *oldtail = dllist_remove_tail(cache); + + hashtable_delete(cache->index, oldtail->path); + free_entry(oldtail); + } +} /** * Store an entry in the cache * @@ -125,6 +170,26 @@ void cache_put(struct cache *cache, char *path, char *content_type, void *conten /////////////////// // IMPLEMENT ME! // /////////////////// + //Allocate a new cache entry with the passed parameters + struct cache_entry *ce = alloc_entry(path, content_type, content, content_length); + //Insert the entry at the head of the doubly-linked list + dllist_insert_head(cache, ce); + //Store the entry in the hashtable as well, indexed by the entry's path + hashtable_put(cache->index, ce->path, ce); + //Increment the current size of the cache + cache->cur_size++; + clean_lru(cache); + //If the cache size is greater than the max size + // if (cache->cur_size > cache->max_size) { + // //Remove the cache entry at the tail of the linked list + // dllist_remove_tail(ce); + // //Remove that same entry from the hashtable, using the entry's path and the hashtable_delete function + // hashtable_delete(ce->path, ce); + // //Free the cache entry + // cache_free(ce); + // //Ensure the size counter for the number of entries in the cache is correct + // cache->cur_size--; + // } } /** @@ -135,4 +200,14 @@ struct cache_entry *cache_get(struct cache *cache, char *path) /////////////////// // IMPLEMENT ME! // /////////////////// + //Attempt to find the cache entry pointer by path in the hash table. If not found, return NULL + struct cache_entry *ce = hashtable_get(cache->index, path); + + if (ce == NULL) { + return NULL; + } + //Move the cache entry to the head of the doubly-linked list + dllist_move_to_head(cache, ce); + //Return the cache entry pointer + return ce; } diff --git a/src/cache.h b/src/cache.h index f64c976ad..519f68203 100644 --- a/src/cache.h +++ b/src/cache.h @@ -8,6 +8,8 @@ struct cache_entry { int content_length; void *content; + //time_t timestamp; //added for comparison implementing cache get/put + struct cache_entry *prev, *next; // Doubly-linked list }; diff --git a/src/cache_tests/cache_tests.c b/src/cache_tests/cache_tests.c index 5e245d005..a4cb58667 100644 --- a/src/cache_tests/cache_tests.c +++ b/src/cache_tests/cache_tests.c @@ -63,7 +63,7 @@ char *test_cache_put() mu_assert(check_cache_entries(cache->head, test_entry_1) == 0, "Your cache_put function did not put an entry into the head of the empty cache with the expected form"); mu_assert(check_cache_entries(cache->tail, test_entry_1) == 0, "Your cache_put function did not put an entry into the tail of the empty cache with the expected form"); mu_assert(check_cache_entries(hashtable_get(cache->index, "/1"), test_entry_1) == 0, "Your cache_put function did not put the expected entry into the hashtable"); - + // Add in a second entry to the cache cache_put(cache, test_entry_2->path, test_entry_2->content_type, test_entry_2->content, test_entry_2->content_length); // Check that the cache is handling both entries as expected @@ -72,7 +72,7 @@ char *test_cache_put() mu_assert(check_cache_entries(cache->tail, test_entry_1) == 0, "Your cache_put function did not move the oldest entry in the cache to the tail of the cache"); mu_assert(check_cache_entries(cache->head->next, test_entry_1) == 0, "Your cache_put function did not correctly set the head->next pointer of the cache"); mu_assert(check_cache_entries(hashtable_get(cache->index, "/2"), test_entry_2) == 0, "Your cache_put function did not put the expected entry into the hashtable"); - + // Add in a third entry to the cache cache_put(cache, test_entry_3->path, test_entry_3->content_type, test_entry_3->content, test_entry_3->content_length); // Check that the cache is handling all three entries as expected @@ -83,7 +83,7 @@ char *test_cache_put() mu_assert(check_cache_entries(cache->head->next->next, test_entry_1) == 0, "Your cache_put function did not update the head->next->next pointer to point to the tail entry"); mu_assert(check_cache_entries(cache->tail->prev, test_entry_2) == 0, "Your cache_put function did not update the tail->prev pointer to poin to the second-to-last entry"); mu_assert(check_cache_entries(cache->tail, test_entry_1) == 0, "Your cache_put function did not correctly update the tail pointer of the cache"); - + // Add in a fourth entry to the cache cache_put(cache, test_entry_4->path, test_entry_4->content_type, test_entry_4->content, test_entry_4->content_length); // Check that the cache removed the oldest entry and is handling the three most-recent entries correctly @@ -94,7 +94,7 @@ char *test_cache_put() mu_assert(check_cache_entries(cache->head->next->next, test_entry_2) == 0, "Your cache_put function did not update the head->next->next pointer to point to the tail entry"); mu_assert(check_cache_entries(cache->tail->prev, test_entry_3) == 0, "Your cache_put function did not update the tail->prev pointer to poin to the second-to-last entry"); mu_assert(check_cache_entries(cache->tail, test_entry_2) == 0, "Your cache_put function did not correctly handle the tail of an already-full cache"); - + cache_free(cache); return NULL; diff --git a/src/server.c b/src/server.c index ea43306fc..cc3eec6ff 100644 --- a/src/server.c +++ b/src/server.c @@ -52,16 +52,38 @@ int send_response(int fd, char *header, char *content_type, void *body, int cont { const int max_response_size = 262144; char response[max_response_size]; - // Build HTTP response and store it in response /////////////////// // IMPLEMENT ME! // /////////////////// + //time + time_t rawtime; + struct tm *info; + time (&rawtime); + info = localtime(&rawtime); + + //build header + int response_length = snprintf(response, max_response_size, + "%s\n" + "Date: %s" //asctime adds its own new line + "Content-Type: %s\n" + "Content-Length: %d\n" + "Connection: close\n" + "\n", + //"%s\n", //taking body off the response header and creating separate send + header, asctime(info), content_type, content_length //,body + ); // Send it all! int rv = send(fd, response, response_length, 0); + if (rv < 0) { + perror("send"); + } + //refactor this (creating another send) to accommodate png so send body (and its length) separately from the response so still only one request + rv = send(fd, body, content_length, 0); + if (rv < 0) { perror("send"); } @@ -76,16 +98,18 @@ int send_response(int fd, char *header, char *content_type, void *body, int cont void get_d20(int fd) { // Generate a random number between 1 and 20 inclusive - /////////////////// // IMPLEMENT ME! // /////////////////// + char str[4]; + int random_number = rand() % 21; //num = (rand() % (upper – lower + 1)) + lower + sprintf(str, "%d\n", random_number); // Use send_response() to send it back as text/plain data - /////////////////// // IMPLEMENT ME! // /////////////////// + send_response(fd, "HTTP/1.1 200 OK", "text/plain", str, strlen(str)); } /** @@ -122,6 +146,37 @@ void get_file(int fd, struct cache *cache, char *request_path) /////////////////// // IMPLEMENT ME! // /////////////////// + //read file + char filepath[4096]; + struct file_data *filedata; //loads file into memory and returns pointer to data + char *mime_type; + //fetch file. + //append path user requests (which != filepath) to server root (3490/index.html) + snprintf(filepath, sizeof filepath, "%s%s", SERVER_ROOT, request_path); + printf("\"%s\"\n", filepath); + + //time comparison for expiration of cache (implemented with cache put/get) + struct cache_entry *ce = cache_get(cache, filepath); + //expire then refresh file and delete item out of cache (eg if cur time less that time greater than 1min) + if (time(NULL) - ce->timestamp > 60) { //timestamp decl in struct cache_entry in cache.h so know when it was created + + } + + //load that file + filedata = file_load(filepath); + //if file not found + if (filedata == NULL) + { + resp_404(fd); + return; + } + //get mimetype + mime_type = mime_type_get(filepath); + printf("%s\n", mime_type); + //send file out + send_response(fd, "HTTP/1.1 200 OK", mime_type, filedata->data, filedata->size); + //then free data bc loaded these data into memory + file_free(filedata); } /** @@ -130,18 +185,20 @@ void get_file(int fd, struct cache *cache, char *request_path) * "Newlines" in HTTP can be \r\n (carriage return followed by newline) or \n * (newline) or \r (carriage return). */ -char *find_start_of_body(char *header) -{ - /////////////////// - // IMPLEMENT ME! // (Stretch) - /////////////////// -} +// char *find_start_of_body(char *header) +// { +// /////////////////// +// // IMPLEMENT ME! // (Stretch) +// /////////////////// +// (void)header; +// } /** * Handle HTTP request and send response */ void handle_http_request(int fd, struct cache *cache) { + (void)cache; //rememember to remove all these voids. reeeeeeeeemember! const int request_buffer_size = 65536; // 64K char request[request_buffer_size]; @@ -153,20 +210,32 @@ void handle_http_request(int fd, struct cache *cache) return; } - /////////////////// // IMPLEMENT ME! // /////////////////// - // Read the three components of the first request line + char method[512]; + char path[8192]; + + sscanf(request, "%s %s", method, path); + printf("method: \"%s\"\n", method); + printf("path: \"%s\"\n", path); // If GET, handle the get endpoints // Check if it's /d20 and handle that special case // Otherwise serve the requested file by calling get_file() - - + if (strcmp(method, "GET") == 0) { + if (strcmp(path, "/d20") == 0) { //strcmp returns 0 if equal + get_d20(fd); + } else { + get_file(fd, NULL, path); + //resp_404(fd); //used 404 before completing get_file + } + } + // (Stretch) If POST, handle the post request + //if (strcmp(method, "POST") == 0) } /** diff --git a/src/serverroot/cat.jpg b/src/serverroot/cat.png similarity index 100% rename from src/serverroot/cat.jpg rename to src/serverroot/cat.png