#define _GNU_SOURCE #include #include #include #include "csapp.h" #include #define MAX_CACHE_SIZE (1<<20) #define MAX_OBJECT_SIZE (100*(1<<10)) typedef struct cacheNode{ char *url; char *buffer; struct cacheNode *next; int buffSize; int timeCnt; } cacheNode; void *doit(void *fdp); void read_requesthdrs(rio_t *rp, int fd); void parseUrl(char **url, char **hostname, char **port, char **filename); void freeNode(cacheNode *node); void insertNode(cacheNode *node); cacheNode* cacheContains(char *url); int mod_open_clientfd(char *hostname, int port); int counter = 0; int cacheSize = 0; cacheNode *head = NULL; sem_t gethost_mutex; sem_t mutex; int main(int argc, char **argv) { int listenfd, port, clientlen = sizeof(struct sockaddr_in); int *connfdp; struct sockaddr_in clientaddr; pthread_t tid; /* Check command line args */ if (argc != 2) { fprintf(stderr, "usage: %s \n", argv[0]); exit(1); } port = atoi(argv[1]); /* handle the SIGPIPE signal */ signal(SIGPIPE, SIG_IGN); Sem_init(&gethost_mutex, 0, 1); Sem_init(&mutex, 0, 1); listenfd = Open_listenfd(port); while (1) { connfdp = Malloc(sizeof(int)); *connfdp = Accept(listenfd, (SA *)&clientaddr, (unsigned int *) &clientlen); Pthread_create(&tid, NULL, doit, connfdp); Pthread_detach(tid); } } /* * doit - handle one HTTP request/response transaction */ void *doit(void *fdp) { char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE]; char *filename, *hostname, *port, *url; char requestLine[MAXLINE]; int serverfd, bytesRead; int fd = *((int *) fdp); cacheNode *foundNode; cacheNode *newNode; rio_t rio, rio2; Free(fdp); url = uri; /* Read request line and headers */ Rio_readinitb(&rio, fd); Rio_readlineb(&rio, buf, MAXLINE); sscanf(buf, "%s %s %s", method, url, version); parseUrl(&url, &hostname, &port, &filename); printf("url --> %s, hostname --> %s, port --> %s, filename --> %s\n", url, hostname, port, filename); if ( (foundNode = cacheContains(url)) != NULL ){ Rio_writen(fd, foundNode->buffer, foundNode->buffSize); foundNode->timeCnt = counter; // P(&mutex); counter++; // V(&mutex); } else{ /* open connection to correct host and port */ serverfd = Open_clientfd(hostname, atoi(port)); /* parse the request line, and create string */ strcpy(requestLine, method); strcat(requestLine, " "); strcat(requestLine, filename); strcat(requestLine, " HTTP/1.0\r\n"); printf("write: %s\n", requestLine); /* writes the request line */ Rio_writen(serverfd, requestLine, strlen(requestLine)); /* then read the rest of the request headers */ read_requesthdrs(&rio, serverfd); printf("-----------------------------------------------------\n"); /* write the server response to the client */ Rio_readinitb(&rio2, serverfd); while ( ((bytesRead = Rio_readnb(&rio2, buf, MAXBUF)) > 0) ){ if(bytesRead <= MAX_OBJECT_SIZE){ newNode = Malloc(sizeof(cacheNode)); newNode->url = Malloc( strlen(url)+1 ); printf("NUM BYTES READ: %d\n", bytesRead); newNode->buffer = Malloc(bytesRead); newNode->next = Malloc(sizeof(cacheNode*)); memcpy((void *) newNode->url, (void *) url, (strlen(url)+1) ); memcpy((void *) newNode->buffer, (void *) buf, bytesRead); newNode->buffSize = bytesRead; newNode->timeCnt = counter; // P(&mutex); counter++; insertNode(newNode); // V(&mutex); } Rio_writen(fd, buf, bytesRead); } Close(serverfd); } Close(fd); return NULL; } void insertNode(cacheNode *node){ cacheNode *lru, *lruPrev, *temp, *temp2; int replacedSizes = 0; // printf("cacheSize --> %d, buffSize --> %d, maxCache --> %d\n",cacheSize,node->buffSize,MAX_CACHE_SIZE ); if (cacheSize + node->buffSize < MAX_CACHE_SIZE){ if (head == NULL){ head = node; node->next = NULL; } else{ node->next = head; head = node; } cacheSize += node->buffSize; } else{ /* iterate and delete LRU cache blocks */ while(replacedSizes < node->buffSize){ lru = head; lruPrev = NULL; temp = head; temp2 = head->next; while(temp2 != NULL){ if (temp2->timeCnt < lru->timeCnt){ lru = temp2; lruPrev = temp; } temp = temp2; temp2 = temp2->next; } if(lruPrev->next != NULL) lruPrev->next = lru->next; replacedSizes += lru->buffSize; cacheSize -= lru->buffSize; freeNode(lru); } /* insert the node */ node->next = head; head = node; cacheSize += node->buffSize; } } void freeNode(cacheNode *node){ Free(node->buffer); Free(node->url); Free(node->next); Free(node); } cacheNode* cacheContains(char *url){ cacheNode *temp = head; while(temp != NULL){ if (!strcmp(temp->url, url)) return temp; temp = temp->next; } return NULL; } /* * read_requesthdrs - read and parse HTTP request headers */ void read_requesthdrs(rio_t *rp, int fd) { char buf[MAXBUF]; char *connection; int lineLength; lineLength = Rio_readlineb(rp, buf, MAXBUF); printf("write: --> %s", buf); Rio_writen(fd, buf, lineLength); while(strcmp(buf, "\r\n")) { lineLength = Rio_readlineb(rp, buf, MAXBUF); if (strstr(buf, "Proxy-Connection")){ connection = "Proxy-Connection: close\r\n"; printf("write: %s", connection); Rio_writen(fd, connection, strlen(connection)); } else if(strstr(buf, "Connection")){ connection = "Connection: close\r\n"; printf("write: %s", connection); Rio_writen(fd, connection, strlen(connection)); } else if (!(strstr(buf, "Keep-Alive"))){ printf("write: %s", buf); Rio_writen(fd, buf, lineLength); } } } void parseUrl(char **url, char **hostname, char **port, char **filename){ char * portVal; char *temp = *url + 7; int totLen = 0; int uriLen = 0; int portLen = 0; int hostLen = 0; *filename = strstr(temp, "/"); *port = strstr(temp, ":"); totLen = strlen(temp); if(*filename) uriLen = strlen(*filename); else *filename = "/"; /* return the port */ if(*port){ portLen = strlen(*port) - uriLen; portVal = strndup(*port, portLen); portVal++; *port = portVal; } else *port = "80"; /* default is to return the hostname */ hostLen = totLen - (portLen + uriLen); *hostname = strndup(temp, hostLen); } struct hostent *gethostbyname_ts(char *name){ struct hostent *sharedp, *unsharedp; unsharedp = Malloc(sizeof(struct hostent)); P(&gethost_mutex); sharedp = Gethostbyname(name); *unsharedp = *sharedp; V(&gethost_mutex); return unsharedp; } int mod_open_clientfd(char *hostname, int port) { int clientfd; struct hostent *hp; struct sockaddr_in serveraddr; if ((clientfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) return -1; /* check errno for cause of error */ /* Fill in the server's IP address and port */ if ((hp = gethostbyname_ts(hostname)) == NULL) return -2; /* check h_errno for cause of error */ bzero((char *) &serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; bcopy((char *)hp->h_addr_list[0], (char *)&serveraddr.sin_addr.s_addr, hp->h_length); serveraddr.sin_port = htons(port); /* Establish a connection with the server */ if (connect(clientfd, (SA *) &serveraddr, sizeof(serveraddr)) < 0) return -1; //Free(hp); return clientfd; }