diff --git a/src/cache.c b/src/cache.c index c72975cdd..59f7d0ac3 100644 --- a/src/cache.c +++ b/src/cache.c @@ -12,6 +12,23 @@ struct cache_entry *alloc_entry(char *path, char *content_type, void *content, i /////////////////// // IMPLEMENT ME! // /////////////////// + struct cache_entry *new_entry = malloc(sizeof(struct cache_entry)); + + new_entry->path = malloc(strlen(path) + 1); + strcpy(new_entry->path, path); + + new_entry->content_type = malloc(strlen(content_type) + 1); + strcpy(new_entry->content_type, content_type); + + new_entry->content = malloc(content_length); + memcpy(new_entry->content, content, content_length); + + new_entry->content_length = content_length; + + new_entry->prev = new_entry->next = NULL; + + return new_entry; + } /** @@ -22,6 +39,10 @@ void free_entry(struct cache_entry *entry) /////////////////// // IMPLEMENT ME! // /////////////////// + free(entry->path); + free(entry->content_type); + free(entry->content); + free(entry); } /** @@ -94,6 +115,15 @@ struct cache *cache_create(int max_size, int hashsize) /////////////////// // IMPLEMENT ME! // /////////////////// + struct cache *new_cache = malloc(sizeof(struct cache)); + struct hashtable *hash_table = hashtable_create(hashsize, NULL); + + new_cache->head = new_cache->tail = NULL; + new_cache->cur_size = 0; + new_cache->max_size = max_size; + new_cache->index = hash_table; + + return new_cache; } void cache_free(struct cache *cache) @@ -125,6 +155,22 @@ void cache_put(struct cache *cache, char *path, char *content_type, void *conten /////////////////// // IMPLEMENT ME! // /////////////////// + struct cache_entry *new_entry = alloc_entry(path, content_type, content, content_length); + dllist_insert_head(cache, new_entry); + hashtable_put(cache->index, path, new_entry); + + cache->cur_size += 1; + + if (cache->cur_size > cache->max_size) + { + struct cache_entry *old_entry = dllist_remove_tail(cache); + hashtable_delete(cache->index, old_entry->path); + free_entry(old_entry); + if (cache->cur_size > cache->max_size) + { + cache->cur_size -= 1; + } + } } /** @@ -135,4 +181,11 @@ struct cache_entry *cache_get(struct cache *cache, char *path) /////////////////// // IMPLEMENT ME! // /////////////////// + if (hashtable_get(cache->index, path) == NULL) + { + return NULL; + } + else + dllist_move_to_head(cache, hashtable_get(cache->index, path)); + return cache->head; } diff --git a/src/server.c b/src/server.c index ea43306fc..a1d232b69 100644 --- a/src/server.c +++ b/src/server.c @@ -1,18 +1,18 @@ /** * webserver.c -- A webserver written in C - * + * * Test with curl (if you don't have it, install it): - * + * * curl -D - http://localhost:3490/ * curl -D - http://localhost:3490/d20 * curl -D - http://localhost:3490/date - * + * * You can also test the above URLs in your browser! They should work! - * + * * Posting Data: - * + * * curl -D - -X POST -H 'Content-Type: text/plain' -d 'Hello, sample data!' http://localhost:3490/save - * + * * (Posting data is harder to test from a browser.) */ @@ -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" @@ -45,7 +45,7 @@ * header: "HTTP/1.1 404 NOT FOUND" or "HTTP/1.1 200 OK", etc. * content_type: "text/plain", etc. * body: the data to send. - * + * * Return the value from the send() function. */ int send_response(int fd, char *header, char *content_type, void *body, int content_length) @@ -59,33 +59,52 @@ int send_response(int fd, char *header, char *content_type, void *body, int cont // IMPLEMENT ME! // /////////////////// + int response_length = snprintf(response, max_response_size, + "%s\n" + "Content-Type: %s\n" + "Content-Length: %d\n" + "Connection: close\n" + "\n", + header, content_type, content_length); + // Send it all! int rv = send(fd, response, response_length, 0); - if (rv < 0) { + if (rv < 0) + { perror("send"); } + rv = send(fd, body, content_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! // /////////////////// + int random_num = (rand() % 20) + 1; + char response_body[16]; + sprintf(response_body, "%d\n", random_num); // Use send_response() to send it back as text/plain data /////////////////// // IMPLEMENT ME! // /////////////////// + send_response(fd, "HTTP/1.1 200 OK", "text/plain", response_body, strlen(response_body)); } /** @@ -94,14 +113,14 @@ 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); @@ -122,11 +141,47 @@ void get_file(int fd, struct cache *cache, char *request_path) /////////////////// // IMPLEMENT ME! // /////////////////// + + struct cache_entry *cache_entry = cache_get(cache, request_path); + + if (cache_entry != NULL) + { + send_response(fd, "HTTP/1.1 200 OK", cache_entry->content_type, cache_entry->content, cache_entry->content_length); + } + else + { + char filepath[4096]; + struct file_data *filedata; + char *mime_type; + if (strcmp(request_path, "/") == 0 || strcmp(request_path, "/index.html") == 0) + { + snprintf(filepath, sizeof filepath, "%s/index.html", SERVER_ROOT); + } + else + { + snprintf(filepath, sizeof filepath, "%s%s", SERVER_ROOT, request_path); + } + + filedata = file_load(filepath); + + if (filedata == NULL) + { + resp_404(fd); + return; + } + + mime_type = mime_type_get(filepath); + + cache_put(cache, request_path, mime_type, filedata->data, filedata->size); + send_response(fd, "HTTP/1.1 200 OK", mime_type, filedata->data, filedata->size); + + file_free(filedata); + } } /** * Search for the end of the HTTP header - * + * * "Newlines" in HTTP can be \r\n (carriage return followed by newline) or \n * (newline) or \r (carriage return). */ @@ -144,27 +199,37 @@ void handle_http_request(int fd, struct cache *cache) { const int request_buffer_size = 65536; // 64K char request[request_buffer_size]; + char method[200]; + char path[8192]; + char protocol[1785]; // 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! // /////////////////// - // 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() + sscanf(request, "%s %s %s", method, path, protocol); + if (strcmp(method, "GET") == 0) + { + if (strcmp(path, "/d20") == 0) + { + get_d20(fd); + } + else + { + get_file(fd, cache, path); + } + } + resp_404(fd); // (Stretch) If POST, handle the post request } @@ -174,53 +239,41 @@ 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 + int newfd; + struct sockaddr_storage their_addr; char s[INET6_ADDRSTRLEN]; struct cache *cache = cache_create(10, 0); - // 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); } 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) { + 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. - handle_http_request(newfd, cache); close(newfd); } - // Unreachable code - return 0; }