diff --git a/README.md b/README.md index 8fd528143..1133d1feb 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # A Simple Web Server in C + In this project, we'll finish the implementation of a web server in C. What you need to write: diff --git a/src/cache.c b/src/cache.c index c72975cdd..390f26c18 100644 --- a/src/cache.c +++ b/src/cache.c @@ -9,9 +9,15 @@ */ struct cache_entry *alloc_entry(char *path, char *content_type, void *content, int content_length) { - /////////////////// - // IMPLEMENT ME! // - /////////////////// + /////////////////// + // IMPLEMENT ME! // + /////////////////// + struct cache_entry *ce = malloc(sizeof(struct cache_entry)); + ce->path = path; + ce->content_length = content_length; + ce->content_type = content_type; + ce->content = content; + return ce; } /** @@ -19,9 +25,14 @@ struct cache_entry *alloc_entry(char *path, char *content_type, void *content, i */ void free_entry(struct cache_entry *entry) { - /////////////////// - // IMPLEMENT ME! // - /////////////////// + /////////////////// + // IMPLEMENT ME! // + /////////////////// + if (entry != NULL) + { + + free(entry); + } } /** @@ -29,16 +40,19 @@ 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) { - cache->head = cache->tail = ce; - ce->prev = ce->next = NULL; - } else { - cache->head->prev = ce; - ce->next = cache->head; - ce->prev = NULL; - cache->head = ce; - } + // Insert at the head of the list + if (cache->head == NULL) + { + cache->head = cache->tail = ce; + ce->prev = ce->next = NULL; + } + else + { + cache->head->prev = ce; + ce->next = cache->head; + ce->prev = NULL; + cache->head = ce; + } } /** @@ -46,25 +60,27 @@ 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) { - // We're the tail - cache->tail = ce->prev; - cache->tail->next = NULL; - - } else { - // We're neither the head nor the tail - ce->prev->next = ce->next; - ce->next->prev = ce->prev; - } - - ce->next = cache->head; - cache->head->prev = ce; - ce->prev = NULL; - cache->head = ce; + if (ce != cache->head) + { + if (ce == cache->tail) + { + // We're the tail + cache->tail = ce->prev; + cache->tail->next = NULL; + } + else + { + // We're neither the head nor the tail + ce->prev->next = ce->next; + ce->next->prev = ce->prev; } -} + ce->next = cache->head; + cache->head->prev = ce; + ce->prev = NULL; + cache->head = ce; + } +} /** * Removes the tail from the list and returns it @@ -73,14 +89,14 @@ void dllist_move_to_head(struct cache *cache, struct cache_entry *ce) */ struct cache_entry *dllist_remove_tail(struct cache *cache) { - struct cache_entry *oldtail = cache->tail; + struct cache_entry *oldtail = cache->tail; - cache->tail = oldtail->prev; - cache->tail->next = NULL; + cache->tail = oldtail->prev; + cache->tail->next = NULL; - cache->cur_size--; + cache->cur_size--; - return oldtail; + return oldtail; } /** @@ -91,26 +107,35 @@ struct cache_entry *dllist_remove_tail(struct cache *cache) */ struct cache *cache_create(int max_size, int hashsize) { - /////////////////// - // IMPLEMENT ME! // - /////////////////// + /////////////////// + // IMPLEMENT ME! // + /////////////////// + struct hashtable *ht = hashtable_create(hashsize, NULL); + struct cache *cache_inst = malloc(sizeof(struct cache)); + cache_inst->head = NULL; + cache_inst->tail = NULL; + cache_inst->cur_size = 0; + cache_inst->max_size = max_size; + cache_inst->index = ht; + return cache_inst; } void cache_free(struct cache *cache) { - struct cache_entry *cur_entry = cache->head; + struct cache_entry *cur_entry = cache->head; - hashtable_destroy(cache->index); + hashtable_destroy(cache->index); - while (cur_entry != NULL) { - struct cache_entry *next_entry = cur_entry->next; + while (cur_entry != NULL) + { + struct cache_entry *next_entry = cur_entry->next; - free_entry(cur_entry); + free_entry(cur_entry); - cur_entry = next_entry; - } + cur_entry = next_entry; + } - free(cache); + free(cache); } /** @@ -122,9 +147,34 @@ void cache_free(struct cache *cache) */ void cache_put(struct cache *cache, char *path, char *content_type, void *content, int content_length) { - /////////////////// - // IMPLEMENT ME! // - /////////////////// + /////////////////// + // IMPLEMENT ME! // + /////////////////// + struct cache_entry *entry = alloc_entry(path, content_type, content, content_length); + + dllist_insert_head(cache, entry); + + hashtable_put(cache->index, entry->path, entry); + + // Incremtn 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 entry from the hashtable, using the entry's path + hashtable_delete(cache->index, cache->tail->path); + // Remove the cache entry at the tail of the linked list + // Free the cache entry + free_entry(dllist_remove_tail(cache)); + + // Ensure the size counter for the number of entries in the cache is correct. + if (cache->cur_size > cache->max_size) + { + printf("Cache's current size is bigger than its capacity."); + } + } } /** @@ -132,7 +182,16 @@ 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! // - /////////////////// -} + /////////////////// + // IMPLEMENT ME! // + /////////////////// + struct cache_entry *entry = hashtable_get(cache->index, path); + if (entry != NULL) + { + dllist_move_to_head(cache, entry); + } + else + { + return NULL; + } +} \ No newline at end of file diff --git a/src/server.c b/src/server.c index ea43306fc..afbf62a39 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" @@ -50,42 +50,50 @@ */ int send_response(int fd, char *header, char *content_type, void *body, int content_length) { - const int max_response_size = 262144; - char response[max_response_size]; - - // Build HTTP response and store it in response - - /////////////////// - // IMPLEMENT ME! // - /////////////////// - - // Send it all! - int rv = send(fd, response, response_length, 0); - - if (rv < 0) { - perror("send"); - } - - return rv; + const int max_response_size = 262144; + char response[max_response_size]; + + // Build HTTP response and store it in response + time_t rawtime; + struct tm *info; + time(&rawtime); + info = localtime(&rawtime); + char *formatted_time = asctime(info); + /////////////////// + // IMPLEMENT ME! // + /////////////////// + + int response_length = sprintf(response, "%s\n" + "DATE: %s" + "Connection: close\n" + "Content-Length: %d\n" + "Content-Type: %s\n\n" + "%s\n", + header, formatted_time, content_length, content_type, body); + + // Send it all! + int rv = send(fd, response, response_length, 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! // - /////////////////// + srand(getpid() + time(NULL)); + char res_body[8]; - // Use send_response() to send it back as text/plain data + sprintf(res_body, "%d\n", (rand() % 20) + 1); + printf(res_body); - /////////////////// - // IMPLEMENT ME! // - /////////////////// + send_response(fd, "HTTP/1.1 200 OK", "text/plain", res_body, sizeof(res_body)); } /** @@ -93,25 +101,26 @@ void get_d20(int fd) */ void resp_404(int fd) { - char filepath[4096]; - 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) { - // TODO: make this non-fatal - fprintf(stderr, "cannot find system 404 file\n"); - exit(3); - } + char filepath[4096]; + struct file_data *filedata; + char *mime_type; - mime_type = mime_type_get(filepath); + // Fetch the 404.html file + snprintf(filepath, sizeof filepath, "%s/404.html", SERVER_FILES); + filedata = file_load(filepath); - send_response(fd, "HTTP/1.1 404 NOT FOUND", mime_type, filedata->data, filedata->size); + if (filedata == NULL) + { + // TODO: make this non-fatal + fprintf(stderr, "cannot find system 404 file\n"); + exit(3); + } - file_free(filedata); + mime_type = mime_type_get(filepath); + + send_response(fd, "HTTP/1.1 404 NOT FOUND", mime_type, filedata->data, filedata->size); + + file_free(filedata); } /** @@ -119,9 +128,27 @@ void resp_404(int fd) */ void get_file(int fd, struct cache *cache, char *request_path) { - /////////////////// - // IMPLEMENT ME! // - /////////////////// + + char filepath[4096]; + struct file_data *filedata; + char *mime_type; + printf("The request path: %s\n", request_path); + snprintf(filepath, sizeof filepath, "%s/%s", SERVER_ROOT, request_path); + + filedata = file_load(filepath); + + 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); } /** @@ -132,9 +159,9 @@ void get_file(int fd, struct cache *cache, char *request_path) */ char *find_start_of_body(char *header) { - /////////////////// - // IMPLEMENT ME! // (Stretch) - /////////////////// + /////////////////// + // IMPLEMENT ME! // (Stretch) + /////////////////// } /** @@ -142,31 +169,43 @@ char *find_start_of_body(char *header) */ void handle_http_request(int fd, struct cache *cache) { - const int request_buffer_size = 65536; // 64K - char request[request_buffer_size]; - - // Read request - int bytes_recvd = recv(fd, request, request_buffer_size - 1, 0); - - if (bytes_recvd < 0) { - perror("recv"); - return; + const int request_buffer_size = 65536; // 64K + char request[request_buffer_size]; + char request_type[8]; + char resource[1024]; + char request_protocol[16]; + // Read request + int bytes_recvd = recv(fd, request, request_buffer_size - 1, 0); + + if (bytes_recvd < 0) + { + perror("recv"); + return; + } + + sscanf(request, "%s %s %s", request_type, resource, request_protocol); + + printf("Got request: %s %s %s\n:", request_type, resource, request_protocol); + + if (strcmp(request_type, " GET")) + { + if (strcmp(resource, "/d20") == 0) + { + printf("d20 has been reached\n"); + get_d20(fd); } - - - /////////////////// - // IMPLEMENT ME! // - /////////////////// - - // Read the three components of the first request line - - // 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() - - - // (Stretch) If POST, handle the post request + else if (strcmp(resource, "/") == 0) + { + printf("Root has been reached\n"); + get_file(fd, cache, "index.html"); + } + else + { + printf("404 has been reached\n"); + resp_404(fd); + } + } + // (Stretch) If POST, handle the post request } /** @@ -174,53 +213,56 @@ void handle_http_request(int fd, struct cache *cache) */ int main(void) { - int newfd; // listen on sock_fd, new connection on newfd - struct sockaddr_storage their_addr; // connector's address information - char s[INET6_ADDRSTRLEN]; + int newfd; // listen on sock_fd, new connection on newfd + struct sockaddr_storage their_addr; // connector's address information + char s[INET6_ADDRSTRLEN]; + + struct cache *cache = cache_create(10, 0); + + // Get a listening socket + int listenfd = get_listener_socket(PORT); + + if (listenfd < 0) + { + fprintf(stderr, "webserver: fatal error getting listening socket\n"); + exit(1); + } + + printf("webserver: waiting for connections on port %s...\n", PORT); + + // 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; + + // 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) + { + perror("accept"); + continue; + } - struct cache *cache = cache_create(10, 0); + // 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); + printf("server: got connection from %s\n", s); - // Get a listening socket - int listenfd = get_listener_socket(PORT); + // newfd is a new socket descriptor for the new connection. + // listenfd is still listening for new connections. - if (listenfd < 0) { - fprintf(stderr, "webserver: fatal error getting listening socket\n"); - exit(1); - } + handle_http_request(newfd, cache); + // resp_404(newfd); - printf("webserver: waiting for connections on port %s...\n", PORT); - - // 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; - - // 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) { - 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); - 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. - - handle_http_request(newfd, cache); - - close(newfd); - } + close(newfd); + } - // Unreachable code + // Unreachable code - return 0; + return 0; } -