diff options
Diffstat (limited to '')
-rw-r--r-- | P3/server.c | 341 |
1 files changed, 241 insertions, 100 deletions
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; } |