Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C-Web-Server-Quan Nguyen #319

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions src/cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

}

/**
Expand All @@ -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);
}

/**
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}
}
}

/**
Expand All @@ -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;
}
143 changes: 98 additions & 45 deletions src/server.c
Original file line number Diff line number Diff line change
@@ -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.)
*/

Expand All @@ -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"
Expand All @@ -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)
Expand All @@ -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));
}

/**
Expand All @@ -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);
Expand All @@ -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).
*/
Expand All @@ -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
}
Expand All @@ -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;
}