/* Partner Names: Jonathan Beebe - Mark Loh - Description: This is our proxy that uses concurrent processes. It implements a cache with a naive replacement policy, always adding to the head of the list and replacing the head of the list */ #define _GNU_SOURCE #include #include #include #include "csapp.h" #define MAX_CACHE_SIZE (1<<20) #define MAX_OBJECT_SIZE (100*(1<<10)) typedef struct cacheNode{ char *url; char *buffer; struct cacheNode *next; int bufSize; int timeCnt; } cacheNode; /* function prototypes */ 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); cacheNode *cacheContains(char *url); void insertNode(cacheNode *node); int new_open_clientfd(char *hostname, int port); struct hostent *gethostbyname_ts(char *name); cacheNode *head = NULL; int counter = 0; int cacheSize = 0; int readCnt = 0; sem_t mutex, gethost_mutex, w; /* main function */ 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(&mutex, 0, 1); Sem_init(&gethost_mutex, 0, 1); Sem_init(&w, 0, 1); /* open the neccessary ports */ listenfd = Open_listenfd(port); while (1) { connfdp = Malloc(sizeof(int)); *connfdp = Accept(listenfd, (SA *)&clientaddr, (unsigned int *) &clientlen); /* spawn the new threads */ 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], requestLine[MAXLINE]; char *filename, *hostname, *port, *url, *tempBuf = NULL; int serverfd, bytesRead, fd = *((int *) fdp), totalBytesRead = 0; rio_t rio, rio2; cacheNode *foundNode, *newNode; /* stored it in the file descriptor - fd, thus free the pointer */ 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); /* calls our parse Url function */ parseUrl(&url, &hostname, &port, &filename); if ( (foundNode = cacheContains(url)) != NULL){ P(&mutex); foundNode->timeCnt = counter; counter++; readCnt++; if (readCnt == 1) P(&w); V(&mutex); Rio_writen(fd, foundNode->buffer, foundNode->bufSize); P(&mutex); readCnt--; if (readCnt == 0) V(&w); 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"); /* writes the request line */ Rio_writen(serverfd, requestLine, strlen(requestLine)); /* then read the rest of the request headers */ read_requesthdrs(&rio, serverfd); /* writes information back to the server */ Rio_readinitb(&rio2, serverfd); while ( ((bytesRead = Rio_readnb(&rio2, buf, MAXBUF)) > 0) ){ totalBytesRead += bytesRead; tempBuf = Realloc(tempBuf, totalBytesRead); memcpy(tempBuf + (totalBytesRead - bytesRead), buf, bytesRead); Rio_writen(fd, buf, bytesRead); } if (totalBytesRead <= MAX_OBJECT_SIZE){ newNode = Malloc(sizeof(cacheNode)); newNode->url = Malloc(strlen(url) + 1); memcpy(newNode->url, url, strlen(url) + 1); newNode->next = Malloc(sizeof(cacheNode*)); newNode->bufSize = totalBytesRead; newNode->buffer = Malloc(totalBytesRead); memcpy(newNode->buffer, tempBuf, totalBytesRead); P(&w); newNode->timeCnt = counter; counter++; insertNode(newNode); V(&w); } Close(serverfd); } Close(rio.rio_fd); return NULL; } void insertNode(cacheNode *node){ cacheNode *lru, *lruPrev, *temp, *temp2; if (head == NULL){ head = node; node->next = NULL; cacheSize = node->bufSize; } else if ((cacheSize + node->bufSize) < MAX_CACHE_SIZE){ node->next = head; head = node; cacheSize += node->bufSize; } else{ while((MAX_CACHE_SIZE - cacheSize) < node->bufSize){ lru = head; lruPrev = NULL; temp = head; temp2 = head->next; while(temp2 != NULL){ if(temp2->timeCnt < lru->timeCnt){ lruPrev = temp; lru = temp2; } temp = temp2; temp2 = temp2->next; } if (lruPrev != NULL) lruPrev->next = lru->next; else head = lru->next; cacheSize -= lru->bufSize; freeNode(lru); } node->next = head; head = node; cacheSize += node->bufSize; } } /* * read_requesthdrs - read and parse HTTP request headers */ void read_requesthdrs(rio_t *rp, int fd) { char buf[MAXBUF]; char *connection; int lineLength; /* read in the Host: line */ lineLength = Rio_readlineb(rp, buf, MAXBUF); Rio_writen(fd, buf, lineLength); /* reads in request headers until last line */ while(strcmp(buf, "\r\n")) { lineLength = Rio_readlineb(rp, buf, MAXBUF); /* handle header cases described in write-up */ if (strstr(buf, "Proxy-Connection")){ connection = "Proxy-Connection: close\r\n"; Rio_writen(fd, connection, strlen(connection)); } else if(strstr(buf, "Connection")){ connection = "Connection: close\r\n"; Rio_writen(fd, connection, strlen(connection)); } else if (!(strstr(buf, "Keep-Alive"))){ Rio_writen(fd, buf, lineLength); } } } 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; } /* parses the given URL */ void parseUrl(char **url, char **hostname, char **port, char **filename){ char * portVal; /* move past the http:// */ 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); /* check to see if there is a filename at the end */ 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); } int new_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; } 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; }