From 932b2a3906185f78bb48673372cb131f7dbc2753 Mon Sep 17 00:00:00 2001 From: Kevin Sooter Date: Mon, 11 Mar 2019 13:37:39 -0500 Subject: [PATCH 1/8] Initial Commit --- README.md | 76 +++++++++++++++++++++++++++---------------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 8fd528143..61ee1122f 100644 --- a/README.md +++ b/README.md @@ -4,23 +4,24 @@ In this project, we'll finish the implementation of a web server in C. What you need to write: -* HTTP request parser -* HTTP response builder -* LRU cache - * Doubly linked list (some functionality provided) - * Use existing hashtable functionality (below) +- HTTP request parser +- HTTP response builder +- LRU cache -* Your code will interface with the existing code. Understanding the existing + - Doubly linked list (some functionality provided) + - Use existing hashtable functionality (below) + +- Your code will interface with the existing code. Understanding the existing code is an expected part of this challenge. What's already here: -* `net.h` and `net.c` contain low-level networking code -* `mime.h` and `mime.c` contains functionality for determining the MIME type of a file -* `file.h` and `file.c` contains handy file-reading code that you may want to utilize, namely the `file_load()` and `file_free()` functions for reading file data and deallocating file data, respectively (or you could just perform these operations manually as well) -* `hashtable.h` and `hashtable.c` contain an implementation of a hashtable (this one is a bit more complicated than what you built in the Hashtables sprint) -* `llist.h` and `llist.c` contain an implementation of a doubly-linked list (used solely by the hashable--you don't need it) -* `cache.h` and `cache.c` are where you will implement the LRU cache functionality for days 3 and 4 +- `net.h` and `net.c` contain low-level networking code +- `mime.h` and `mime.c` contains functionality for determining the MIME type of a file +- `file.h` and `file.c` contains handy file-reading code that you may want to utilize, namely the `file_load()` and `file_free()` functions for reading file data and deallocating file data, respectively (or you could just perform these operations manually as well) +- `hashtable.h` and `hashtable.c` contain an implementation of a hashtable (this one is a bit more complicated than what you built in the Hashtables sprint) +- `llist.h` and `llist.c` contain an implementation of a doubly-linked list (used solely by the hashable--you don't need it) +- `cache.h` and `cache.c` are where you will implement the LRU cache functionality for days 3 and 4 ## What is a Web Server? @@ -29,17 +30,17 @@ requests for HTML pages), and returns responses (e.g. HTML pages). Other common ## Reading -* [Networking Background](guides/net.md) -* [Doubly-Linked Lists](guides/dllist.md) -* [LRU Caches](guides/lrucache.md) -* [MIME types](guides/mime.md) +- [Networking Background](guides/net.md) +- [Doubly-Linked Lists](guides/dllist.md) +- [LRU Caches](guides/lrucache.md) +- [MIME types](guides/mime.md) ## Assignment We will write a simple web server that returns files and some specialized data on a certain endpoint. -* `http://localhost:3490/d20` should return a random number between 1 and 20 inclusive as `text/plain` data. -* Any other URL should map to the `serverroot` directory and files that lie within. For example: +- `http://localhost:3490/d20` should return a random number between 1 and 20 inclusive as `text/plain` data. +- Any other URL should map to the `serverroot` directory and files that lie within. For example: ``` http://localhost:3490/index.html @@ -72,7 +73,7 @@ _Read through all the main and stretch goals before writing any code to get an o 1. Implement `send_response()`. This function is responsible for formatting all the pieces that make up an HTTP response into the proper format that clients expect. In other words, it needs to build a complete HTTP response with the given parameters. It should write the response to the string in the `response` variable. - + The total length of the header **and** body should be stored in the `response_length` variable so that the `send()` call knows how many bytes to send out over the wire. @@ -86,7 +87,7 @@ _Read through all the main and stretch goals before writing any code to get an o > the header. But the `response_length` variable used by `send()` is the > total length of both header and body. - You can test whether you've gotten `send_response` working by calling the `resp_404` function from somewhere inside the `main` function, and seeing if the client receives the 404 response. + You can test whether you've gotten `send_response` working by calling the `resp_404` function from somewhere inside the `main` function, and seeing if the client receives the 404 response. 2. Examine `handle_http_request()` in the file `server.c`. @@ -153,24 +154,24 @@ The hashtable code is already written and can be found in `hashtable.c`. Algorithm: - * Allocate a new cache entry with the passed parameters. - * Insert the entry at the head of the doubly-linked list. - * Store the entry in the hashtable as well, indexed by the entry's `path`. - * Increment the current size of the cache. - * If the cache size is greater than the max size: - * Remove the cache entry at the tail of the linked list. - * Remove that same entry from the hashtable, using the entry's `path` and the `hashtable_delete` function. - * Free the cache entry. - * Ensure the size counter for the number of entries in the cache is correct. + - Allocate a new cache entry with the passed parameters. + - Insert the entry at the head of the doubly-linked list. + - Store the entry in the hashtable as well, indexed by the entry's `path`. + - Increment the current size of the cache. + - If the cache size is greater than the max size: + - Remove the cache entry at the tail of the linked list. + - Remove that same entry from the hashtable, using the entry's `path` and the `hashtable_delete` function. + - Free the cache entry. + - Ensure the size counter for the number of entries in the cache is correct. 2. Implement `cache_get()` in `cache.c`. Algorithm: - * Attempt to find the cache entry pointer by `path` in the hash table. - * If not found, return `NULL`. - * Move the cache entry to the head of the doubly-linked list. - * Return the cache entry pointer. + - Attempt to find the cache entry pointer by `path` in the hash table. + - If not found, return `NULL`. + - Move the cache entry to the head of the doubly-linked list. + - Return the cache entry pointer. 3. Add caching functionality to `server.c`. @@ -181,11 +182,11 @@ The hashtable code is already written and can be found in `hashtable.c`. If it's not there: - * Load the file from disk (see `file.c`) - * Store it in the cache - * Serve it + - Load the file from disk (see `file.c`) + - Store it in the cache + - Serve it -There's a set of unit tests included to ensure that your cache implementation is functioning correctly. From the `src` directory, run `make tests` in order to run the unit tests against your implementation. +There's a set of unit tests included to ensure that your cache implementation is functioning correctly. From the `src` directory, run `make tests` in order to run the unit tests against your implementation. ### Stretch Goals @@ -247,4 +248,3 @@ When a new connection comes in, launch a thread to handle it. Be sure to lock the cache when a thread accesses it so the threads don't step on each other's toes and corrupt the cache. Also have thread cleanup handlers to handle threads that have died. - From 8609489bef2a32e0caf242137a3139aeb6885e64 Mon Sep 17 00:00:00 2001 From: Kevin Sooter Date: Mon, 11 Mar 2019 15:23:11 -0500 Subject: [PATCH 2/8] Completed send_response --- src/server.c | 67 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/src/server.c b/src/server.c index ea43306fc..62e185e0d 100644 --- a/src/server.c +++ b/src/server.c @@ -34,7 +34,7 @@ #include "mime.h" #include "cache.h" -#define PORT "3490" // the port users will be connecting to +#define PORT "3490" // the port users will be connecting to #define SERVER_FILES "./serverfiles" #define SERVER_ROOT "./serverroot" @@ -52,31 +52,53 @@ 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]; + int response_length; + time_t rawtime; + struct tm *timestamp; + char buffer[100]; + + time(&rawtime); + + timestamp = localtime(&rawtime); + strftime(buffer, 100, "%a %b %d %T %Z %Y", timestamp); // Build HTTP response and store it in response /////////////////// // IMPLEMENT ME! // /////////////////// + char *newBody = body; + + sprintf(response, "%s\n" + "Date: %s\n" + "Connection: close\n" + "Content-Length: %d\n" + "Content-Type: %s\n" + "\n" + "%s\n", + header, buffer, content_length, content_type, newBody); + + response_length = strlen(response); // Send it all! + printf("response: %s\n", response); int rv = send(fd, response, response_length, 0); - if (rv < 0) { + if (rv < 0) + { perror("send"); } return rv; } - /** * Send a /d20 endpoint response */ void get_d20(int fd) { // Generate a random number between 1 and 20 inclusive - + /////////////////// // IMPLEMENT ME! // /////////////////// @@ -94,21 +116,21 @@ void get_d20(int fd) void resp_404(int fd) { char filepath[4096]; - struct file_data *filedata; + struct file_data *filedata; char *mime_type; // Fetch the 404.html file snprintf(filepath, sizeof filepath, "%s/404.html", SERVER_FILES); filedata = file_load(filepath); - if (filedata == NULL) { + if (filedata == NULL) + { // TODO: make this non-fatal fprintf(stderr, "cannot find system 404 file\n"); exit(3); } mime_type = mime_type_get(filepath); - send_response(fd, "HTTP/1.1 404 NOT FOUND", mime_type, filedata->data, filedata->size); file_free(filedata); @@ -148,12 +170,12 @@ void handle_http_request(int fd, struct cache *cache) // Read request int bytes_recvd = recv(fd, request, request_buffer_size - 1, 0); - if (bytes_recvd < 0) { + if (bytes_recvd < 0) + { perror("recv"); return; } - /////////////////// // IMPLEMENT ME! // /////////////////// @@ -165,7 +187,6 @@ void handle_http_request(int fd, struct cache *cache) // Check if it's /d20 and handle that special case // Otherwise serve the requested file by calling get_file() - // (Stretch) If POST, handle the post request } @@ -174,7 +195,7 @@ void handle_http_request(int fd, struct cache *cache) */ int main(void) { - int newfd; // listen on sock_fd, new connection on newfd + int newfd; // listen on sock_fd, new connection on newfd struct sockaddr_storage their_addr; // connector's address information char s[INET6_ADDRSTRLEN]; @@ -182,8 +203,8 @@ int main(void) // Get a listening socket int listenfd = get_listener_socket(PORT); - - if (listenfd < 0) { + if (listenfd < 0) + { fprintf(stderr, "webserver: fatal error getting listening socket\n"); exit(1); } @@ -193,27 +214,30 @@ int main(void) // This is the main loop that accepts incoming connections and // forks a handler process to take care of it. The main parent // process then goes back to waiting for new connections. - - while(1) { - socklen_t sin_size = sizeof their_addr; + while (1) + { + socklen_t sin_size = sizeof their_addr; // Parent process will block on the accept() call until someone // makes a new connection: newfd = accept(listenfd, (struct sockaddr *)&their_addr, &sin_size); - if (newfd == -1) { + + if (newfd == -1) + { perror("accept"); continue; } - // Print out a message that we got the connection inet_ntop(their_addr.ss_family, - get_in_addr((struct sockaddr *)&their_addr), - s, sizeof s); + get_in_addr((struct sockaddr *)&their_addr), + s, sizeof s); printf("server: got connection from %s\n", s); - + // newfd is a new socket descriptor for the new connection. // listenfd is still listening for new connections. + resp_404(newfd); + handle_http_request(newfd, cache); close(newfd); @@ -223,4 +247,3 @@ int main(void) return 0; } - From 1df6fe575f448eaeedea18376f03ce3345d1ac7d Mon Sep 17 00:00:00 2001 From: Kevin Sooter Date: Mon, 11 Mar 2019 16:14:24 -0500 Subject: [PATCH 3/8] Completed handle_http_request() --- src/server.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/server.c b/src/server.c index 62e185e0d..f60802355 100644 --- a/src/server.c +++ b/src/server.c @@ -81,7 +81,6 @@ int send_response(int fd, char *header, char *content_type, void *body, int cont response_length = strlen(response); // Send it all! - printf("response: %s\n", response); int rv = send(fd, response, response_length, 0); if (rv < 0) @@ -97,14 +96,12 @@ 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 + // Generate a random number between 1 and 20 inclusive /////////////////// // IMPLEMENT ME! // /////////////////// - // Use send_response() to send it back as text/plain data - /////////////////// // IMPLEMENT ME! // /////////////////// @@ -179,13 +176,27 @@ void handle_http_request(int fd, struct cache *cache) /////////////////// // IMPLEMENT ME! // /////////////////// + char *method[50]; + char *path[200]; + char *HTTP[50]; // Read the three components of the first request line - + sscanf(request, "%s %s %s", method, path, HTTP); // If GET, handle the get endpoints + if (strcmp(method, "GET") == 0) + { - // Check if it's /d20 and handle that special case - // Otherwise serve the requested file by calling get_file() + // Check if it's /d20 and handle that special case + if (strncmp(path, "/d20", 4) == 0) + { + get_d20(fd); + } + else + { + resp_404(fd); + } + // Otherwise serve the requested file by calling get_file() + } // (Stretch) If POST, handle the post request } @@ -236,8 +247,6 @@ int main(void) // newfd is a new socket descriptor for the new connection. // listenfd is still listening for new connections. - resp_404(newfd); - handle_http_request(newfd, cache); close(newfd); From 03898d9548d60826b7d0a94346760aade66bcf3b Mon Sep 17 00:00:00 2001 From: Kevin Sooter Date: Tue, 12 Mar 2019 10:20:22 -0500 Subject: [PATCH 4/8] Completed get_d20. Add void to clear warnings --- src/cache.c | 28 ++++++++++++++++++++-------- src/server.c | 34 +++++++++++++++++----------------- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/src/cache.c b/src/cache.c index c72975cdd..f315a845f 100644 --- a/src/cache.c +++ b/src/cache.c @@ -12,6 +12,10 @@ struct cache_entry *alloc_entry(char *path, char *content_type, void *content, i /////////////////// // IMPLEMENT ME! // /////////////////// + (void)path; + (void)content_type; + (void)content; + (void)content_length; } /** @@ -30,10 +34,13 @@ void free_entry(struct cache_entry *entry) void dllist_insert_head(struct cache *cache, struct cache_entry *ce) { // Insert at the head of the list - if (cache->head == NULL) { + if (cache->head == NULL) + { cache->head = cache->tail = ce; ce->prev = ce->next = NULL; - } else { + } + else + { cache->head->prev = ce; ce->next = cache->head; ce->prev = NULL; @@ -46,13 +53,16 @@ void dllist_insert_head(struct cache *cache, struct cache_entry *ce) */ void dllist_move_to_head(struct cache *cache, struct cache_entry *ce) { - if (ce != cache->head) { - if (ce == cache->tail) { + if (ce != cache->head) + { + if (ce == cache->tail) + { // We're the tail cache->tail = ce->prev; cache->tail->next = NULL; - - } else { + } + else + { // We're neither the head nor the tail ce->prev->next = ce->next; ce->next->prev = ce->prev; @@ -65,7 +75,6 @@ void dllist_move_to_head(struct cache *cache, struct cache_entry *ce) } } - /** * Removes the tail from the list and returns it * @@ -94,6 +103,8 @@ struct cache *cache_create(int max_size, int hashsize) /////////////////// // IMPLEMENT ME! // /////////////////// + (void)max_size; + (void)hashsize; } void cache_free(struct cache *cache) @@ -102,7 +113,8 @@ void cache_free(struct cache *cache) hashtable_destroy(cache->index); - while (cur_entry != NULL) { + while (cur_entry != NULL) + { struct cache_entry *next_entry = cur_entry->next; free_entry(cur_entry); diff --git a/src/server.c b/src/server.c index f60802355..671e1c2b1 100644 --- a/src/server.c +++ b/src/server.c @@ -63,10 +63,6 @@ int send_response(int fd, char *header, char *content_type, void *body, int cont timestamp = localtime(&rawtime); strftime(buffer, 100, "%a %b %d %T %Z %Y", timestamp); // Build HTTP response and store it in response - - /////////////////// - // IMPLEMENT ME! // - /////////////////// char *newBody = body; sprintf(response, "%s\n" @@ -81,6 +77,8 @@ int send_response(int fd, char *header, char *content_type, void *body, int cont response_length = strlen(response); // Send it all! + // To get binary working memcopy or two sends (one response, two body) + // Talked about in Tuesday lecture int rv = send(fd, response, response_length, 0); if (rv < 0) @@ -96,15 +94,13 @@ int send_response(int fd, char *header, char *content_type, void *body, int cont */ void get_d20(int fd) { - + char str[5]; // Generate a random number between 1 and 20 inclusive - /////////////////// - // IMPLEMENT ME! // - /////////////////// + int num = (rand() % (20 - 1 + 1) + 1); + // Convert number to string + sprintf(str, "%d", num); // 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)); } /** @@ -138,6 +134,9 @@ void resp_404(int fd) */ void get_file(int fd, struct cache *cache, char *request_path) { + (void)fd; + (void)cache; + (void)request_path; /////////////////// // IMPLEMENT ME! // /////////////////// @@ -151,9 +150,11 @@ void get_file(int fd, struct cache *cache, char *request_path) */ char *find_start_of_body(char *header) { + (void)header; /////////////////// // IMPLEMENT ME! // (Stretch) /////////////////// + return NULL; } /** @@ -161,6 +162,8 @@ char *find_start_of_body(char *header) */ void handle_http_request(int fd, struct cache *cache) { + (void)cache; + const int request_buffer_size = 65536; // 64K char request[request_buffer_size]; @@ -173,12 +176,9 @@ void handle_http_request(int fd, struct cache *cache) return; } - /////////////////// - // IMPLEMENT ME! // - /////////////////// - char *method[50]; - char *path[200]; - char *HTTP[50]; + char method[50]; + char path[200]; + char HTTP[50]; // Read the three components of the first request line sscanf(request, "%s %s %s", method, path, HTTP); From 3f0feb41f1120221539e2f3f11e4227e1521114e Mon Sep 17 00:00:00 2001 From: Kevin Sooter Date: Tue, 12 Mar 2019 11:10:38 -0500 Subject: [PATCH 5/8] Implemented arbitrary file serving --- src/mime.c | 49 +++++++++++++++++++++++++++++++++++++------------ src/server.c | 28 ++++++++++++++++++++-------- 2 files changed, 57 insertions(+), 20 deletions(-) diff --git a/src/mime.c b/src/mime.c index 4013b5ca8..cd43bbc87 100644 --- a/src/mime.c +++ b/src/mime.c @@ -9,7 +9,8 @@ */ char *strlower(char *s) { - for (char *p = s; *p != '\0'; p++) { + for (char *p = s; *p != '\0'; p++) + { *p = tolower(*p); } @@ -22,25 +23,49 @@ char *strlower(char *s) char *mime_type_get(char *filename) { char *ext = strrchr(filename, '.'); - - if (ext == NULL) { + if (ext == NULL) + { return DEFAULT_MIME_TYPE; } - + ext++; strlower(ext); // TODO: this is O(n) and it should be O(1) - if (strcmp(ext, "html") == 0 || strcmp(ext, "htm") == 0) { return "text/html"; } - if (strcmp(ext, "jpeg") == 0 || strcmp(ext, "jpg") == 0) { return "image/jpg"; } - if (strcmp(ext, "css") == 0) { return "text/css"; } - if (strcmp(ext, "js") == 0) { return "application/javascript"; } - if (strcmp(ext, "json") == 0) { return "application/json"; } - if (strcmp(ext, "txt") == 0) { return "text/plain"; } - if (strcmp(ext, "gif") == 0) { return "image/gif"; } - if (strcmp(ext, "png") == 0) { return "image/png"; } + if (strcmp(ext, "html") == 0 || strcmp(ext, "htm") == 0) + { + return "text/html"; + } + if (strcmp(ext, "jpeg") == 0 || strcmp(ext, "jpg") == 0) + { + return "image/jpg"; + } + if (strcmp(ext, "css") == 0) + { + return "text/css"; + } + if (strcmp(ext, "js") == 0) + { + return "application/javascript"; + } + if (strcmp(ext, "json") == 0) + { + return "application/json"; + } + if (strcmp(ext, "txt") == 0) + { + return "text/plain"; + } + if (strcmp(ext, "gif") == 0) + { + return "image/gif"; + } + if (strcmp(ext, "png") == 0) + { + return "image/png"; + } return DEFAULT_MIME_TYPE; } \ No newline at end of file diff --git a/src/server.c b/src/server.c index 671e1c2b1..55450c238 100644 --- a/src/server.c +++ b/src/server.c @@ -97,7 +97,7 @@ void get_d20(int fd) char str[5]; // Generate a random number between 1 and 20 inclusive int num = (rand() % (20 - 1 + 1) + 1); - // Convert number to string + // Conver number to string sprintf(str, "%d", num); // Use send_response() to send it back as text/plain data send_response(fd, "HTTP/1.1 200 OK", "text/plain", str, strlen(str)); @@ -134,12 +134,24 @@ void resp_404(int fd) */ void get_file(int fd, struct cache *cache, char *request_path) { - (void)fd; (void)cache; - (void)request_path; - /////////////////// - // IMPLEMENT ME! // - /////////////////// + + char filepath[4096]; + struct file_data *filedata; + char *mime_type; + + snprintf(filepath, sizeof filepath, "%s%s", SERVER_ROOT, request_path); + filedata = file_load(filepath); + + if (filedata == NULL) + { + resp_404(fd); + } + else + { + mime_type = mime_type_get(filepath); + send_response(fd, "HTTP/1.1 200 OK", mime_type, filedata->data, filedata->size); + } } /** @@ -193,9 +205,9 @@ void handle_http_request(int fd, struct cache *cache) } else { - resp_404(fd); + // Otherwise serve the requested file by calling get_file() + get_file(fd, cache, path); } - // Otherwise serve the requested file by calling get_file() } // (Stretch) If POST, handle the post request From 9a1bb42c7ddcf5320e3f6b4829f74e81f76023d4 Mon Sep 17 00:00:00 2001 From: Kevin Sooter Date: Tue, 12 Mar 2019 14:56:26 -0500 Subject: [PATCH 6/8] MVP is done. Attempting to dive into the cache now. --- src/server.c | 39 ++++++++++++++++++++++----------------- src/serverroot/index.html | 4 +++- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/server.c b/src/server.c index 55450c238..0b48493c6 100644 --- a/src/server.c +++ b/src/server.c @@ -52,7 +52,6 @@ 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]; - int response_length; time_t rawtime; struct tm *timestamp; @@ -63,18 +62,16 @@ int send_response(int fd, char *header, char *content_type, void *body, int cont timestamp = localtime(&rawtime); strftime(buffer, 100, "%a %b %d %T %Z %Y", timestamp); // Build HTTP response and store it in response - char *newBody = body; - sprintf(response, "%s\n" - "Date: %s\n" - "Connection: close\n" - "Content-Length: %d\n" - "Content-Type: %s\n" - "\n" - "%s\n", - header, buffer, content_length, content_type, newBody); - - response_length = strlen(response); + int response_length = snprintf( + response, max_response_size, + "%s\n" + "Date: %s\n" + "Connection: close\n" + "Content-Length: %d\n" + "Content-Type: %s\n" + "\n", + header, buffer, content_length, content_type); // Send it all! // To get binary working memcopy or two sends (one response, two body) @@ -86,6 +83,13 @@ int send_response(int fd, char *header, char *content_type, void *body, int cont perror("send"); } + rv = send(fd, body, content_length, 0); + + if (rv < 0) + { + perror("send"); + } + return rv; } @@ -146,12 +150,13 @@ void get_file(int fd, struct cache *cache, char *request_path) if (filedata == NULL) { resp_404(fd); + return; } - else - { - mime_type = mime_type_get(filepath); - send_response(fd, "HTTP/1.1 200 OK", mime_type, filedata->data, filedata->size); - } + + mime_type = mime_type_get(filepath); + send_response(fd, "HTTP/1.1 200 OK", mime_type, filedata->data, filedata->size); + + file_free(filedata); } /** diff --git a/src/serverroot/index.html b/src/serverroot/index.html index 0ecbe58fe..972373256 100644 --- a/src/serverroot/index.html +++ b/src/serverroot/index.html @@ -9,5 +9,7 @@

Home page!

This is the default home page. +
+ cat - \ No newline at end of file + From 1a006d5c59ed20021848603e3c10cec18d8713e9 Mon Sep 17 00:00:00 2001 From: Kevin Sooter Date: Wed, 13 Mar 2019 11:02:08 -0500 Subject: [PATCH 7/8] Completed cache_put() and cache_get() --- src/cache.c | 86 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 65 insertions(+), 21 deletions(-) diff --git a/src/cache.c b/src/cache.c index f315a845f..00e0b86e1 100644 --- a/src/cache.c +++ b/src/cache.c @@ -9,13 +9,23 @@ */ struct cache_entry *alloc_entry(char *path, char *content_type, void *content, int content_length) { - /////////////////// - // IMPLEMENT ME! // - /////////////////// - (void)path; - (void)content_type; - (void)content; - (void)content_length; + struct cache_entry *ce = malloc(sizeof *ce); + + if (ce == NULL) + { + return NULL; + } + + ce->path = strdup(path); + ce->content_type = strdup(content_type); + ce->content_length = content_length; + + ce->content = malloc(content_length); + memcpy(ce->content, content, content_length); + + ce->prev = ce->next = NULL; + + return ce; } /** @@ -23,9 +33,10 @@ struct cache_entry *alloc_entry(char *path, char *content_type, void *content, i */ void free_entry(struct cache_entry *entry) { - /////////////////// - // IMPLEMENT ME! // - /////////////////// + free(entry->path); + free(entry->content_type); + free(entry->content); + free(entry); } /** @@ -100,11 +111,19 @@ struct cache_entry *dllist_remove_tail(struct cache *cache) */ struct cache *cache_create(int max_size, int hashsize) { - /////////////////// - // IMPLEMENT ME! // - /////////////////// - (void)max_size; - (void)hashsize; + struct cache *cache = malloc(sizeof *cache); + + if (cache == NULL) + { + return NULL; + } + + cache->index = hashtable_create(hashsize, NULL); + cache->head = cache->tail = NULL; + cache->max_size = max_size; + cache->cur_size = 0; + + return cache; } void cache_free(struct cache *cache) @@ -134,9 +153,26 @@ void cache_free(struct cache *cache) */ void cache_put(struct cache *cache, char *path, char *content_type, void *content, int content_length) { - /////////////////// - // IMPLEMENT ME! // - /////////////////// + // Allocate a new cache entry with the passed parameters. + struct cache_entry *cache_entry = alloc_entry(path, content_type, content, content_length); + // Insert the entry at the head of the doubly-linked list. + dllist_insert_head(cache, cache_entry); + // Store the entry in the hashtable as well, indexed by the entry's path. + hashtable_put(cache->index, path, cache_entry); + // Increment the current size of the cache. + cache->cur_size++; + // 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. + struct cache_entry *oldtail = dllist_remove_tail(cache); + // Remove that same entry from the hashtable, using the entry's path and the hashtable_delete function. + hashtable_delete(cache->index, oldtail->path); + // Free the cache entry. + free_entry(oldtail); + // Ensure the size counter for the number of entries in the cache is correct. + printf("CUrrent size: %d, should be: %d\n", cache->cur_size, cache->max_size - 1); + } } /** @@ -144,7 +180,15 @@ void cache_put(struct cache *cache, char *path, char *content_type, void *conten */ 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. + struct cache_entry *ce = hashtable_get(cache->index, path); + // If not found, return NULL. + 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; } From 477d0a31b479e3a6eddee579a80a710dd5397c66 Mon Sep 17 00:00:00 2001 From: Kevin Sooter Date: Wed, 13 Mar 2019 15:42:58 -0500 Subject: [PATCH 8/8] All tests are passing. MVP completed. --- src/server.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/server.c b/src/server.c index 0b48493c6..305487f84 100644 --- a/src/server.c +++ b/src/server.c @@ -138,25 +138,35 @@ void resp_404(int fd) */ void get_file(int fd, struct cache *cache, char *request_path) { - (void)cache; - char filepath[4096]; struct file_data *filedata; char *mime_type; + struct cache_entry *ce; snprintf(filepath, sizeof filepath, "%s%s", SERVER_ROOT, request_path); - filedata = file_load(filepath); + ce = cache_get(cache, filepath); - if (filedata == NULL) + if (ce) { - resp_404(fd); - return; + send_response(fd, "HTTP/1.1 200 OK", ce->content_type, ce->content, ce->content_length); } + else + { + filedata = file_load(filepath); - mime_type = mime_type_get(filepath); - send_response(fd, "HTTP/1.1 200 OK", mime_type, filedata->data, filedata->size); + if (filedata == NULL) + { + resp_404(fd); + return; + } - file_free(filedata); + mime_type = mime_type_get(filepath); + send_response(fd, "HTTP/1.1 200 OK", mime_type, filedata->data, filedata->size); + + cache_put(cache, filepath, mime_type, filedata->data, filedata->size); + + file_free(filedata); + } } /** @@ -179,8 +189,6 @@ char *find_start_of_body(char *header) */ void handle_http_request(int fd, struct cache *cache) { - (void)cache; - const int request_buffer_size = 65536; // 64K char request[request_buffer_size]; @@ -244,10 +252,11 @@ int main(void) // process then goes back to waiting for new connections. while (1) { - + // puts(">> 1"); socklen_t sin_size = sizeof their_addr; // Parent process will block on the accept() call until someone // makes a new connection: + // puts(">> 2"); newfd = accept(listenfd, (struct sockaddr *)&their_addr, &sin_size); if (newfd == -1) @@ -256,6 +265,7 @@ int main(void) continue; } // Print out a message that we got the connection + // puts(">> 3"); inet_ntop(their_addr.ss_family, get_in_addr((struct sockaddr *)&their_addr), s, sizeof s); @@ -263,9 +273,9 @@ int main(void) // newfd is a new socket descriptor for the new connection. // listenfd is still listening for new connections. - + // puts(">> 4"); handle_http_request(newfd, cache); - + // puts(">> 5"); close(newfd); }