aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--P3/.vscode/launch.json2
-rw-r--r--P3/server.c341
2 files changed, 242 insertions, 101 deletions
diff --git a/P3/.vscode/launch.json b/P3/.vscode/launch.json
index d241d6a..4ecb279 100644
--- a/P3/.vscode/launch.json
+++ b/P3/.vscode/launch.json
@@ -9,7 +9,7 @@
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/web_server",
- "args": ["9000", "testing", "10", "10", "0", "99", "0"],
+ "args": ["9000", "testing", "1", "1", "0", "20", "10"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
diff --git a/P3/server.c b/P3/server.c
index 1e7a6c3..6d76df5 100644
--- a/P3/server.c
+++ b/P3/server.c
@@ -21,7 +21,7 @@
#define BUFF_SIZE 1024
int port, workers, dispatchers, dynFlag, qLen, cSiz = 0;
-char* path;
+char *path;
pthread_mutex_t Qlock, logLock;
/*
@@ -29,26 +29,30 @@ pthread_mutex_t Qlock, logLock;
*/
// structs:
-typedef struct request_queue {
- int fd;
- char *request;
- struct request_queue* next;
+typedef struct request_queue
+{
+ int fd;
+ char *request;
+ struct request_queue *next;
} request_t;
request_t *Q = NULL; // The request queue
-typedef struct cache_entry {
- int len;
- char *request;
- char *content;
- struct cache_entry* next;
+typedef struct cache_entry
+{
+ int len;
+ char *request;
+ char *content;
+ struct cache_entry *next;
} cache_entry_t;
cache_entry_t *dynQ = NULL; //The cache queue
/* ******************** Dynamic Pool Code [Extra Credit A] **********************/
// Extra Credit: This function implements the policy to change the worker thread pool dynamically
// depending on the number of requests
-void * dynamic_pool_size_update(void *arg) {
- while(1) {
+void *dynamic_pool_size_update(void *arg)
+{
+ while (1)
+ {
// Run at regular intervals
// Increase / decrease dynamically based on your policy
}
@@ -58,12 +62,19 @@ void * dynamic_pool_size_update(void *arg) {
/* ************************ Cache Code [Extra Credit B] **************************/
// Function to check whether the given request is present in cache
-int getCacheIndex(char *request){
+int isInCache(char *request)
+{
/// return the index if the request is present in the cache
cache_entry_t *traverse = dynQ;
+ if (dynQ == NULL)
+ return -2;
int index = 0;
- while (traverse != NULL) {
- if (!strcmp(request, traverse->request)) {
+ while (traverse != NULL)
+ {
+ if (traverse->request == NULL)
+ break;
+ if (!strcmp(request, traverse->request))
+ {
return index;
}
index++;
@@ -71,17 +82,66 @@ int getCacheIndex(char *request){
return -1;
}
+// Function to traverse cache queue to find cache
+int readFromCache(int index, char *buffer)
+{
+ // Open and read the contents of file given the request
+ cache_entry_t *traverse = dynQ;
+ for (int i = 0; i < index; i++)
+ {
+ if (traverse == NULL)
+ return -1;
+ traverse = traverse->next;
+ }
+ buffer = traverse->content;
+ return 0;
+}
+
// Function to add the request and its file content into the cache
-void addIntoCache(char *mybuf, char *memory , int memory_size){
+void addIntoCache(char *mybuf, char *memory, int memory_size)
+{
// It should add the request at an index according to the cache replacement policy
// Make sure to allocate/free memory when adding or replacing cache entries
+ cache_entry_t *traverse = dynQ;
+ if (dynQ == NULL)
+ return;
+ int cacheIndex = 0;
+ bool fullCache = false;
+ while (traverse->next != NULL)
+ {
+ if (++cacheIndex > cSiz)
+ {
+ fullCache = true;
+ break;
+ }
+ traverse = traverse->next;
+ }
+ if (fullCache)
+ {
+ cache_entry_t *temp = dynQ;
+ dynQ = dynQ->next;
+ free(temp);
+ free(temp->content);
+ free(temp->request);
+ cacheIndex--;
+ addIntoCache(mybuf, memory, memory_size);
+ }
+ else
+ {
+ cache_entry_t *temp = calloc(1, sizeof(cache_entry_t));
+ temp->request = mybuf;
+ temp->content = memory;
+ temp->len = memory_size;
+ cacheIndex++;
+ }
}
// clear the memory allocated to the cache
-void deleteCache(){
-
+void deleteCache()
+{
request_t *tempReq = NULL;
- while (Q != NULL) {
+ while (Q != NULL)
+ {
tempReq = Q;
Q = Q->next;
free(tempReq->request);
@@ -93,47 +153,64 @@ void deleteCache(){
{
tempCache = dynQ;
dynQ = dynQ->next;
+ free(tempCache->content);
+ free(tempCache->request);
free(tempCache);
}
}
// Function to initialize the cache
-void initCache(){
+int cacheLength;
+void initCache()
+{
// Allocating memory and initializing the cache array
-
+ dynQ = calloc(1, sizeof(cache_entry_t));
+ cacheLength = 0;
+ // cache_entry_t *tempTrav = dynQ;
+ // for (int i=0; i<cSiz; i++) {
+ // tempTrav->next = calloc(1, sizeof(cache_entry_t));
+ // tempTrav = tempTrav->next;
+ // }
}
/**********************************************************************************/
/* ************************************ Utilities ********************************/
// Function to get the content type from the request
-char* getContentType(char * mybuf) {
+char *getContentType(char *mybuf)
+{
// Should return the content type based on the file type in the request
// (See Section 5 in Project description for more details)
- char* ext = strrchr(mybuf, '.');
+ char *ext = strrchr(mybuf, '.');
- if (ext == NULL) {
- printf("Filetype not found. Exiting\n");
- exit(-1);
+ if (ext == NULL)
+ {
+ printf("Filetype not found. Exiting\n");
+ exit(-1);
}
- else if ((strcmp((ext + 1), "htm") == 0) || (strcmp((ext + 1), "html") == 0)) {
+ else if ((strcmp((ext + 1), "htm") == 0) || (strcmp((ext + 1), "html") == 0))
+ {
return "text/html";
}
- else if (strcmp((ext + 1), "jpg") == 0) {
+ else if (strcmp((ext + 1), "jpg") == 0)
+ {
return "image/jpeg";
}
- else if (strcmp((ext + 1), "gif") == 0) {
+ else if (strcmp((ext + 1), "gif") == 0)
+ {
return "image/gif";
}
- else {
+ else
+ {
return "text/plain";
}
}
// Function to get the size of the file in the queue
// (Thanks Matt from stackOverflow)
-unsigned long getFileSize(char *file) {
- char* temp = malloc(BUFF_SIZE);
+unsigned long getFileSize(char *file)
+{
+ char *temp = malloc(BUFF_SIZE);
sprintf(temp, ".%s", file);
FILE *f = fopen(temp, "r");
free(temp);
@@ -147,114 +224,163 @@ unsigned long getFileSize(char *file) {
// Function to open and read the file from the disk into the memory
// Add necessary arguments as needed
-int readFromDisk(char* fileName, char* buffer, long fileSize) {
- char* temp = malloc(BUFF_SIZE);
- sprintf(temp, ".%s", fileName);
- // Open and read the contents of file given the request
- int requestFile = open(temp, O_RDONLY);
- free(temp);
- if (requestFile == -1) {
- return -1; // Error handle
- }
- read(requestFile, buffer, fileSize);
- close(requestFile);
- return 0;
+int readFromDisk(char *fileName, char *buffer, long fileSize)
+{
+ char *temp = malloc(BUFF_SIZE);
+ sprintf(temp, ".%s", fileName);
+ // Open and read the contents of file given the request
+ int requestFile = open(temp, O_RDONLY);
+ free(temp);
+ if (requestFile == -1)
+ {
+ return -1; // Error handle
+ }
+ read(requestFile, buffer, fileSize);
+ close(requestFile);
+ return 0;
}
/**********************************************************************************/
// Function to receive the request from the client and add to the queue
-void * dispatch(void *arg) {
+void *dispatch(void *arg)
+{
- while (1) {
+ while (1)
+ {
// Accept client connection and get the fd
int newReq = accept_connection();
- if (newReq > INVALID) {
+ if (newReq > INVALID)
+ {
pthread_mutex_lock(&Qlock);
- request_t* traverse = Q;
-
+ request_t *traverse = Q;
+
// Get request from the client
// Add the request into the queue
- for(int i = 0; i < qLen; i++) {
- if (traverse == NULL || traverse->next == NULL) {
+ for (int i = 0; i < qLen; i++)
+ {
+ if (traverse == NULL || traverse->next == NULL)
+ {
//Add things to queue. Lock & unlock to prevent a deadlock
-
+
request_t *tempNode = (request_t *)calloc(1, sizeof(request_t));
- char* dispatchBuf = (char *) malloc(BUFF_SIZE); // Buffer to store the requested filename
- if (get_request(newReq, dispatchBuf) != 0) {
+ char *dispatchBuf = (char *)malloc(BUFF_SIZE); // Buffer to store the requested filename
+ if (get_request(newReq, dispatchBuf) != 0)
+ {
pthread_mutex_unlock(&Qlock);
free(tempNode);
free(dispatchBuf);
continue; // If get_request fails, try again
- }
+ }
tempNode->fd = newReq;
tempNode->request = dispatchBuf;
tempNode->next = NULL;
- if (traverse == NULL) {
+ if (traverse == NULL)
+ {
Q = tempNode;
- } else {
+ }
+ else
+ {
traverse->next = tempNode;
}
-
+
pthread_mutex_unlock(&Qlock);
break;
- } else {
- traverse = traverse -> next;
+ }
+ else
+ {
+ traverse = traverse->next;
}
}
}
- }
- return NULL;
+ }
+ return NULL;
}
/**********************************************************************************/
// Function to retrieve the request from the queue, process it and then return a result to the client
-void * worker(void *arg) {
- int id = *(int*) arg;
+void *worker(void *arg)
+{
+ int id = *(int *)arg;
unsigned long numbytes;
unsigned long long numReqs = 0;
- while (1) {
+ while (1)
+ {
// Get the request from the queue
pthread_mutex_lock(&Qlock);
- if (Q == NULL) {
+ if (Q == NULL)
+ {
pthread_mutex_unlock(&Qlock);
continue;
}
//Make copy of request and get rid of old one.
request_t *request = NULL;
- request = (request_t*) malloc(sizeof(request_t));
+ request = (request_t *)malloc(sizeof(request_t));
request->fd = Q->fd;
request->request = Q->request;
Q = Q->next;
pthread_mutex_unlock(&Qlock);
- // TODO! Get the data from the disk or the cache (extra credit B)
+ //Get the data from the disk or the cache (extra credit B)
numbytes = getFileSize(request->request);
- char *workerBuf = (char *) malloc(numbytes*sizeof(char));
- char *bytesError = malloc(BUFF_SIZE);
- if (numbytes != 0) {
+ char *workerBuf = (char *)malloc(numbytes * sizeof(char));
+ char *bytesError = malloc(BUFF_SIZE);
+ bool fail = false;
+ if (numbytes != 0)
+ {
//SUCC
sprintf(bytesError, "%ld", numbytes);
- } else {
+ }
+ else
+ {
//ERR
+ fail = true;
sprintf(bytesError, "%s", strerror(errno));
}
+ char *cacheTest; //HIT/MISS only
+ if (!fail)
+ {
+ int test = isInCache(request->request);
+ if (test != -1)
+ {
+ //In cache, file exists
+ cacheTest = "HIT";
+ readFromCache(test, workerBuf);
+ }
+ else
+ {
+ cacheTest = "MISS";
+ if (readFromDisk(request->request, workerBuf, numbytes) == -1)
+ {
+ //Not in cache, disk read failed
+ fail = true;
+ }
+ else
+ {
+ //Not in cache, disk read succeeds
+ addIntoCache(request->request, workerBuf, (int)numbytes);
+ }
+ }
+ }
// Log the request into the file and terminal
pthread_mutex_lock(&logLock);
- FILE* log = fopen("../webserver_log", "a");
- fprintf(log, "[%d][%lld][%d][%s][%s][%s]\n", id, ++numReqs, request->fd, request->request, bytesError, "CACHE");
- printf("[%d][%lld][%d][%s][%s][%s]\n", id, numReqs, request->fd, request->request, bytesError, "CACHE");
+ FILE *log = fopen("../webserver_log", "a");
+ fprintf(log, "[%d][%lld][%d][%s][%s][%s]\n", id, ++numReqs, request->fd, request->request, bytesError, cacheTest);
+ printf("[%d][%lld][%d][%s][%s][%s]\n", id, numReqs, request->fd, request->request, bytesError, cacheTest);
fclose(log);
pthread_mutex_unlock(&logLock);
free(bytesError);
// Return the result
- if (readFromDisk(request->request, workerBuf, numbytes) != 0) {
+ if (fail)
+ {
//ERR
return_error(request->fd, request->request);
- } else {
+ }
+ else
+ {
//SUCC
return_result(request->fd, getContentType(request->request), workerBuf, numbytes);
}
@@ -271,12 +397,15 @@ void * worker(void *arg) {
static volatile sig_atomic_t exitFlag = 0;
//Sets exit flag so process can die happily and not sad.
-static void eggs(int signo) {
+static void eggs(int signo)
+{
exitFlag |= 1;
}
-int main(int argc, char **argv) {
+int main(int argc, char **argv)
+{
// Error check on number of arguments
- if(argc != 8){
+ if (argc != 8)
+ {
printf("usage: %s port path num_dispatcher num_workers dynamic_flag queue_length cache_size\n", argv[0]);
return -1;
}
@@ -297,19 +426,23 @@ int main(int argc, char **argv) {
cSiz = atoi(argv[7]);
/* -- ERROR CHECKING -- */
- if (port < 1025 || port > 65535) {
+ if (port < 1025 || port > 65535)
+ {
printf("Invalid port. Port must be greater than 1025 or less than 65535 (inclusive).\n");
return -1;
}
- if (dispatchers > MAX_THREADS || dispatchers < 1) {
+ if (dispatchers > MAX_THREADS || dispatchers < 1)
+ {
printf("Number of dispatchers is invalid. It must be greater than 1 or less than %d (inclusive).\n", MAX_THREADS);
return -1;
}
- if (workers > MAX_THREADS || workers < 1) {
+ if (workers > MAX_THREADS || workers < 1)
+ {
printf("Number of dispatchers is invalid. It must be greater than 1 or less than %d (inclusive).\n", MAX_THREADS);
return -1;
}
- if (qLen > MAX_queue_len || qLen < 1) {
+ if (qLen > MAX_queue_len || qLen < 1)
+ {
printf("Queue length is invalid It must be greater than 1 or less than %d (inclusive).\n", MAX_queue_len);
return -1;
}
@@ -320,16 +453,18 @@ int main(int argc, char **argv) {
act.sa_handler = eggs;
act.sa_flags = 0;
if (sigemptyset(&act.sa_mask) == -1 ||
- sigaction(SIGINT, &act, NULL) == -1) {
- perror("SIGINT Handler Error");
- return -2;
+ sigaction(SIGINT, &act, NULL) == -1)
+ {
+ perror("SIGINT Handler Error");
+ return -2;
}
// Create instance of logfil
- FILE* logfile = fopen("webserver_log", "w");
+ FILE *logfile = fopen("webserver_log", "w");
fclose(logfile);
// Change the current working directory to server root directory
- if (chdir(path) == -1) {
+ if (chdir(path) == -1)
+ {
perror("Directory Change error");
return -2;
}
@@ -345,37 +480,43 @@ int main(int argc, char **argv) {
char threadName[16];
// Create dispatcher threads (make detachable????)
pthread_t dThreads[dispatchers];
- for (int i=0; i<dispatchers; i++) {
+ for (int i = 0; i < dispatchers; i++)
+ {
pthread_create(&dThreads[i], &attr, dispatch, NULL); // DEBUG! figure out last arg
sprintf(threadName, "Dispatch %d", i);
pthread_setname_np(dThreads[i], threadName);
}
//Create workers (make detachable?????)
pthread_t wThreads[workers];
- int* Wargs = malloc(sizeof(int)*workers);
- for (int i = 0; i < workers; i++) {
- Wargs[i]=i;
- pthread_create(&wThreads[i], &attr, worker, (void *) &Wargs[i]); //TODO: Worker arguments
+ int *Wargs = malloc(sizeof(int) * workers);
+ for (int i = 0; i < workers; i++)
+ {
+ Wargs[i] = i;
+ pthread_create(&wThreads[i], &attr, worker, (void *)&Wargs[i]); //TODO: Worker arguments
sprintf(threadName, "Worker %d", i);
pthread_setname_np(wThreads[i], threadName);
- }
+ }
free(Wargs);
// Create dynamic pool manager thread (extra credit A)
- if (dynFlag) {
+ if (dynFlag)
+ {
pthread_t pThread;
pthread_create(&pThread, &attr, dynamic_pool_size_update, NULL); //TODO: possible arguments
pthread_setname_np(pThread, "Pool Manager");
}
-
+
//Server loop (RUNS FOREVER)
- while (1) {
+ while (1)
+ {
//TODO: Add something else?
// Terminate server gracefully
- if (exitFlag){
+ if (exitFlag)
+ {
printf("\nSIGINT caught, exiting now.\n");
// Print the number of pending requests in the request queue
int pendReqs;
- for (pendReqs = 0; pendReqs < qLen; pendReqs++) {
+ for (pendReqs = 0; pendReqs < qLen; pendReqs++)
+ {
if (Q == NULL || Q->next == NULL)
break;
}