Proxy basic
#include "csapp.h"
#define MAX_CACHE_SIZE 1049000
#define MAX_OBJECT_SIZE 102400
static const char *user_agent_hdr =
"User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:10.0.3) Gecko/20120305 "
"Firefox/10.0.3\r\n";
void doit(int fd);
void parse_uri(char *uri, char *host, char *port, char *path);
void clienterror(int fd, char *cause, char *errnum, char *shortmsg, char *longmsg);
int main(int argc, char **argv) {
int listenfd, connfd;
char hostname[MAXLINE], port[MAXLINE];
socklen_t clientlen;
struct sockaddr_storage clientaddr;
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(1);
}
listenfd = Open_listenfd(argv[1]);
while (1) {
clientlen = sizeof(clientaddr);
connfd = Accept(listenfd, (SA *) &clientaddr, &clientlen);
Getnameinfo((SA *) &clientaddr, clientlen, hostname, MAXLINE, port, MAXLINE,0);
printf("Accepted connection from (%s, %s)\n", hostname, port);
doit(connfd);
Close(connfd);
}
}
void doit(int fd) {
int serverfd;
char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE];
char host[MAXLINE], port[MAXLINE], path[MAXLINE];
char request_buf[MAXLINE];
rio_t client_rio, server_rio;
int Does_send_host_header = 0;
Rio_readinitb(&client_rio, fd);
if (Rio_readlineb(&client_rio, buf, MAXLINE) == 0) {
return;
}
sscanf(buf, "%s %s %s", method, uri, version);
if (strcasecmp(method, "GET")) {
clienterror(fd, method, "501", "Not Implemented",
"Proxy does not implement this method");
return;
}
parse_uri(uri, host, port, path);
sprintf(request_buf, "GET %s HTTP/1.0\r\n", path);
while (Rio_readlineb(&client_rio, buf, MAXLINE) > 0) {
if (strcmp(buf, "\r\n") == 0)
break;
if (strstr(buf, "User-Agent:"))
continue;
if (strstr(buf, "Connection:"))
continue;
if (strstr(buf, "Proxy-Connection:"))
continue;
if (strstr(buf, "Host:")) {
Does_send_host_header = 1;
}
sprintf(request_buf, "%s%s", request_buf, buf);
}
if (!Does_send_host_header) {
sprintf(request_buf, "%sHost: %s\r\n", request_buf, host);
}
sprintf(request_buf, "%s%s", request_buf, user_agent_hdr);
sprintf(request_buf, "%sConnection: close\r\n", request_buf);
sprintf(request_buf, "%sProxy-Connection: close\r\n", request_buf);
sprintf(request_buf, "%s\r\n", request_buf);
serverfd = Open_clientfd(host, port);
Rio_writen(serverfd, request_buf, strlen(request_buf));
Rio_readinitb(&server_rio, serverfd);
size_t n;
while ((n = Rio_readnb(&server_rio, buf, MAXLINE)) > 0) {
Rio_writen(fd, buf, n);
}
Close(serverfd);
}
void parse_uri(char *uri, char *host, char *port, char *path) {
char *ptr;
if (!(ptr = strstr(uri, "http://"))) {
return;
}
ptr += 7;
char *path_ptr = strchr(ptr, '/');
if (path_ptr) {
strcpy(path, path_ptr);
*path_ptr = '\0';
} else {
strcpy(path, "/");
}
char *port_ptr = strchr(ptr, ':');
if (port_ptr) {
*port_ptr = '\0';
strcpy(port, port_ptr + 1);
} else {
strcpy(port, "80");
}
strcpy(host, ptr);
}
void clienterror(int fd, char *cause, char *errnum, char *shortmsg, char *longmsg) {
char buf[MAXLINE], body[MAXBUF];
sprintf(body, "<html><title>Proxy Error</title>");
sprintf(body, "%s<body bgcolor=\"ffffff\">\r\n", body);
sprintf(body, "%s%s: %s\r\n", body, errnum, shortmsg);
sprintf(body, "%s<p>%s: %s\r\n", body, longmsg, cause);
sprintf(body, "%s<hr><em>The Tiny Web server</em>\r\n", body);
sprintf(buf, "HTTP/1.0 %s %s\r\n", errnum, shortmsg);
Rio_writen(fd, buf, strlen(buf));
sprintf(buf, "Content-type: text/html\r\n");
Rio_writen(fd, buf, strlen(buf));
sprintf(buf, "Content-length: %d\r\n\r\n", (int) strlen(body));
Rio_writen(fd, buf, strlen(buf));
Rio_writen(fd, body, strlen(body));
}
Proxy concurrency
#include "csapp.h"
#define MAX_CACHE_SIZE 1049000
#define MAX_OBJECT_SIZE 102400
static const char *user_agent_hdr =
"User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:10.0.3) Gecko/20120305 "
"Firefox/10.0.3\r\n";
void doit(int fd);
void parse_uri(char *uri, char *host, char *port, char *path);
void clienterror(int fd, char *cause, char *errnum, char *shortmsg, char *longmsg);
void *thread_function(void *vargp);
int main(int argc, char **argv) {
int listenfd;
char hostname[MAXLINE], port[MAXLINE];
socklen_t clientlen;
struct sockaddr_storage clientaddr;
pthread_t tid;
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(1);
}
listenfd = Open_listenfd(argv[1]);
while (1) {
clientlen = sizeof(clientaddr);
int *connfd_ptr = Malloc(sizeof(int));
*connfd_ptr = Accept(listenfd, (SA *)&clientaddr, &clientlen);
Getnameinfo((SA *)&clientaddr, clientlen, hostname, MAXLINE, port, MAXLINE, 0);
printf("Accepted connection from (%s, %s)\n", hostname, port);
Pthread_create(&tid, NULL, thread_function, connfd_ptr);
}
}
void *thread_function(void *vargp) {
int connfd = *((int *)vargp);
Pthread_detach(pthread_self());
Free(vargp);
doit(connfd);
Close(connfd);
return NULL;
}
void doit(int fd) {
int serverfd;
char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE];
char host[MAXLINE], port[MAXLINE], path[MAXLINE];
char request_buf[MAXLINE];
rio_t client_rio, server_rio;
int Does_send_host_header = 0;
Rio_readinitb(&client_rio, fd);
if (Rio_readlineb(&client_rio, buf, MAXLINE) == 0) {
return;
}
sscanf(buf, "%s %s %s", method, uri, version);
if (strcasecmp(method, "GET")) {
clienterror(fd, method, "501", "Not Implemented",
"Proxy does not implement this method");
return;
}
parse_uri(uri, host, port, path);
sprintf(request_buf, "GET %s HTTP/1.0\r\n", path);
while (Rio_readlineb(&client_rio, buf, MAXLINE) > 0) {
if (strcmp(buf, "\r\n") == 0)
break;
if (strstr(buf, "User-Agent:"))
continue;
if (strstr(buf, "Connection:"))
continue;
if (strstr(buf, "Proxy-Connection:"))
continue;
if (strstr(buf, "Host:")) {
Does_send_host_header = 1;
}
sprintf(request_buf, "%s%s", request_buf, buf);
}
if (!Does_send_host_header) {
sprintf(request_buf, "%sHost: %s\r\n", request_buf, host);
}
sprintf(request_buf, "%s%s", request_buf, user_agent_hdr);
sprintf(request_buf, "%sConnection: close\r\n", request_buf);
sprintf(request_buf, "%sProxy-Connection: close\r\n", request_buf);
sprintf(request_buf, "%s\r\n", request_buf);
serverfd = Open_clientfd(host, port);
Rio_writen(serverfd, request_buf, strlen(request_buf));
Rio_readinitb(&server_rio, serverfd);
size_t n;
while ((n = Rio_readnb(&server_rio, buf, MAXLINE)) > 0) {
Rio_writen(fd, buf, n);
}
Close(serverfd);
}
void parse_uri(char *uri, char *host, char *port, char *path) {
char *ptr;
if (!(ptr = strstr(uri, "http://"))) {
return;
}
ptr += 7;
char *path_ptr = strchr(ptr, '/');
if (path_ptr) {
strcpy(path, path_ptr);
*path_ptr = '\0';
} else {
strcpy(path, "/");
}
char *port_ptr = strchr(ptr, ':');
if (port_ptr) {
*port_ptr = '\0';
strcpy(port, port_ptr + 1);
} else {
strcpy(port, "80");
}
strcpy(host, ptr);
}
void clienterror(int fd, char *cause, char *errnum, char *shortmsg, char *longmsg) {
char buf[MAXLINE], body[MAXBUF];
sprintf(body, "<html><title>Proxy Error</title>");
sprintf(body, "%s<body bgcolor=\"ffffff\">\r\n", body);
sprintf(body, "%s%s: %s\r\n", body, errnum, shortmsg);
sprintf(body, "%s<p>%s: %s\r\n", body, longmsg, cause);
sprintf(body, "%s<hr><em>The Tiny Web server</em>\r\n", body);
sprintf(buf, "HTTP/1.0 %s %s\r\n", errnum, shortmsg);
Rio_writen(fd, buf, strlen(buf));
sprintf(buf, "Content-type: text/html\r\n");
Rio_writen(fd, buf, strlen(buf));
sprintf(buf, "Content-length: %d\r\n\r\n", (int) strlen(body));
Rio_writen(fd, buf, strlen(buf));
Rio_writen(fd, body, strlen(body));
}
Proxy cache
#include "csapp.h"
#include "cache.h"
#define MAX_CACHE_SIZE 1049000
#define MAX_OBJECT_SIZE 102400
static const char *user_agent_hdr =
"User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:10.0.3) Gecko/20120305 "
"Firefox/10.0.3\r\n";
void doit(int fd);
void parse_uri(char *uri, char *host, char *port, char *path);
void clienterror(int fd, char *cause, char *errnum, char *shortmsg, char *longmsg);
void *thread_function(void *vargp);
int main(int argc, char **argv) {
int listenfd;
char hostname[MAXLINE], port[MAXLINE];
socklen_t clientlen;
struct sockaddr_storage clientaddr;
pthread_t tid;
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(1);
}
Signal(SIGPIPE, SIG_IGN);
cache_init();
listenfd = Open_listenfd(argv[1]);
while (1) {
clientlen = sizeof(clientaddr);
int *connfd_ptr = Malloc(sizeof(int));
*connfd_ptr = Accept(listenfd, (SA *)&clientaddr, &clientlen);
Getnameinfo((SA *)&clientaddr, clientlen, hostname, MAXLINE, port, MAXLINE, 0);
printf("Accepted connection from (%s, %s)\n", hostname, port);
Pthread_create(&tid, NULL, thread_function, connfd_ptr);
}
}
void *thread_function(void *vargp) {
int connfd = *((int *)vargp);
Pthread_detach(pthread_self());
Free(vargp);
doit(connfd);
Close(connfd);
return NULL;
}
void doit(int fd) {
int serverfd;
char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE];
char host[MAXLINE], port[MAXLINE], path[MAXLINE];
char request_buf[MAXLINE];
rio_t client_rio, server_rio;
int Does_send_host_header = 0;
Rio_readinitb(&client_rio, fd);
if (Rio_readlineb(&client_rio, buf, MAXLINE) == 0) {
return;
}
sscanf(buf, "%s %s %s", method, uri, version);
if (strcasecmp(method, "GET")) {
clienterror(fd, method, "501", "Not Implemented",
"Proxy does not implement this method");
return;
}
char cache_key[MAXLINE];
strcpy(cache_key, uri);
if (cache_find(cache_key, fd)) {
printf("Cache hit for %s\n", cache_key);
return;
}
printf("Cache miss for %s\n", cache_key);
parse_uri(uri, host, port, path);
sprintf(request_buf, "GET %s HTTP/1.0\r\n", path);
while (Rio_readlineb(&client_rio, buf, MAXLINE) > 0) {
if (strcmp(buf, "\r\n") == 0)
break;
if (strstr(buf, "User-Agent:"))
continue;
if (strstr(buf, "Connection:"))
continue;
if (strstr(buf, "Proxy-Connection:"))
continue;
if (strstr(buf, "Host:")) {
Does_send_host_header = 1;
}
sprintf(request_buf, "%s%s", request_buf, buf);
}
if (!Does_send_host_header) {
sprintf(request_buf, "%sHost: %s\r\n", request_buf, host);
}
sprintf(request_buf, "%s%s", request_buf, user_agent_hdr);
sprintf(request_buf, "%sConnection: close\r\n", request_buf);
sprintf(request_buf, "%sProxy-Connection: close\r\n", request_buf);
sprintf(request_buf, "%s\r\n", request_buf);
serverfd = Open_clientfd(host, port);
Rio_writen(serverfd, request_buf, strlen(request_buf));
Rio_readinitb(&server_rio, serverfd);
size_t n;
char *cache_buf = Malloc(MAX_OBJECT_SIZE);
int total_bytes_read = 0;
int can_cache = 1;
while ((n = Rio_readnb(&server_rio, buf, MAXLINE)) > 0) {
Rio_writen(fd, buf, n);
if (can_cache) {
if (total_bytes_read + n <= MAX_OBJECT_SIZE) {
memcpy(cache_buf + total_bytes_read, buf, n);
total_bytes_read += n;
} else {
can_cache = 0;
}
}
}
Close(serverfd);
if (can_cache && total_bytes_read > 0) {
cache_store(cache_key, cache_buf, total_bytes_read);
}
Free(cache_buf);
}
void parse_uri(char *uri, char *host, char *port, char *path) {
char *ptr;
if (!(ptr = strstr(uri, "http://"))) {
return;
}
ptr += 7;
char *path_ptr = strchr(ptr, '/');
if (path_ptr) {
strcpy(path, path_ptr);
*path_ptr = '\0';
} else {
strcpy(path, "/");
}
char *port_ptr = strchr(ptr, ':');
if (port_ptr) {
*port_ptr = '\0';
strcpy(port, port_ptr + 1);
} else {
strcpy(port, "80");
}
strcpy(host, ptr);
}
void clienterror(int fd, char *cause, char *errnum, char *shortmsg, char *longmsg) {
char buf[MAXLINE], body[MAXBUF];
sprintf(body, "<html><title>Proxy Error</title>");
sprintf(body, "%s<body bgcolor=\"ffffff\">\r\n", body);
sprintf(body, "%s%s: %s\r\n", body, errnum, shortmsg);
sprintf(body, "%s<p>%s: %s\r\n", body, longmsg, cause);
sprintf(body, "%s<hr><em>The Tiny Web server</em>\r\n", body);
sprintf(buf, "HTTP/1.0 %s %s\r\n", errnum, shortmsg);
Rio_writen(fd, buf, strlen(buf));
sprintf(buf, "Content-type: text/html\r\n");
Rio_writen(fd, buf, strlen(buf));
sprintf(buf, "Content-length: %d\r\n\r\n", (int) strlen(body));
Rio_writen(fd, buf, strlen(buf));
Rio_writen(fd, body, strlen(body));
}
Proxy thread pool
#include "csapp.h"
#include "cache.h"
#include "sbuf.h"
#define MAX_CACHE_SIZE 1049000
#define MAX_OBJECT_SIZE 102400
#define NTHREADS 6
#define SBUFSIZE 16
sbuf_t sbuf;
static const char *user_agent_hdr =
"User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:10.0.3) Gecko/20120305 "
"Firefox/10.0.3\r\n";
void doit(int fd);
void parse_uri(char *uri, char *host, char *port, char *path);
void clienterror(int fd, char *cause, char *errnum, char *shortmsg, char *longmsg);
void *thread_function(void *vargp);
int main(int argc, char **argv) {
int listenfd, connfd;
char hostname[MAXLINE], port[MAXLINE];
socklen_t clientlen;
struct sockaddr_storage clientaddr;
pthread_t tid;
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(1);
}
Signal(SIGPIPE, SIG_IGN);
cache_init();
sbuf_init(&sbuf, SBUFSIZE);
listenfd = Open_listenfd(argv[1]);
for (int i = 0; i < NTHREADS; i++) {
Pthread_create(&tid, NULL, thread_function, NULL);
}
while (1) {
clientlen = sizeof(clientaddr);
connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);
Getnameinfo((SA *)&clientaddr, clientlen, hostname, MAXLINE, port, MAXLINE, 0);
printf("Accepted connection from (%s, %s)\n", hostname, port);
sbuf_insert(&sbuf, connfd);
}
}
void *thread_function(void *vargp) {
Pthread_detach(pthread_self());
while (1) {
int connfd = sbuf_remove(&sbuf);
doit(connfd);
Close(connfd);
}
}
void doit(int fd) {
int serverfd;
char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE];
char host[MAXLINE], port[MAXLINE], path[MAXLINE];
char request_buf[MAXLINE];
char *p = request_buf;
rio_t client_rio, server_rio;
int Does_send_host_header = 0;
Rio_readinitb(&client_rio, fd);
if (Rio_readlineb(&client_rio, buf, MAXLINE) == 0) {
return;
}
sscanf(buf, "%s %s %s", method, uri, version);
if (strcasecmp(method, "GET")) {
clienterror(fd, method, "501", "Not Implemented",
"Proxy does not implement this method");
return;
}
char cache_key[MAXLINE];
strcpy(cache_key, uri);
if (cache_find(cache_key, fd)) {
printf("Cache hit for %s\n", cache_key);
return;
}
printf("Cache miss for %s\n", cache_key);
parse_uri(uri, host, port, path);
p += sprintf(p, "GET %s HTTP/1.0\r\n", path);
while (Rio_readlineb(&client_rio, buf, MAXLINE) > 0) {
if (strcmp(buf, "\r\n") == 0)
break;
if (strstr(buf, "User-Agent:"))
continue;
if (strstr(buf, "Connection:"))
continue;
if (strstr(buf, "Proxy-Connection:"))
continue;
if (strstr(buf, "Host:")) {
Does_send_host_header = 1;
}
p += sprintf(p, "%s", buf);
}
if (!Does_send_host_header) {
p += sprintf(p, "Host: %s\r\n", host);
}
p += sprintf(p, "%s", user_agent_hdr);
p += sprintf(p, "Connection: close\r\n");
p += sprintf(p, "Proxy-Connection: close\r\n");
p += sprintf(p, "\r\n");
serverfd = Open_clientfd(host, port);
Rio_writen(serverfd, request_buf, (p - request_buf));
Rio_readinitb(&server_rio, serverfd);
size_t n;
char *cache_buf = Malloc(MAX_OBJECT_SIZE);
int total_bytes_read = 0;
int can_cache = 1;
while ((n = Rio_readnb(&server_rio, buf, MAXLINE)) > 0) {
Rio_writen(fd, buf, n);
if (can_cache) {
if (total_bytes_read + n <= MAX_OBJECT_SIZE) {
memcpy(cache_buf + total_bytes_read, buf, n);
total_bytes_read += n;
} else {
can_cache = 0;
}
}
}
Close(serverfd);
if (can_cache && total_bytes_read > 0) {
cache_store(cache_key, cache_buf, total_bytes_read);
}
Free(cache_buf);
}
void parse_uri(char *uri, char *host, char *port, char *path) {
char *ptr;
if (!(ptr = strstr(uri, "http://"))) {
return;
}
ptr += 7;
char *path_ptr = strchr(ptr, '/');
if (path_ptr) {
strcpy(path, path_ptr);
*path_ptr = '\0';
} else {
strcpy(path, "/");
}
char *port_ptr = strchr(ptr, ':');
if (port_ptr) {
*port_ptr = '\0';
strcpy(port, port_ptr + 1);
} else {
strcpy(port, "80");
}
strcpy(host, ptr);
}
void clienterror(int fd, char *cause, char *errnum, char *shortmsg, char *longmsg) {
char buf[MAXLINE], body[MAXBUF];
sprintf(body, "<html><title>Proxy Error</title>");
sprintf(body, "%s<body bgcolor=\"ffffff\">\r\n", body);
sprintf(body, "%s%s: %s\r\n", body, errnum, shortmsg);
sprintf(body, "%s<p>%s: %s\r\n", body, longmsg, cause);
sprintf(body, "%s<hr><em>The Tiny Web server</em>\r\n", body);
sprintf(buf, "HTTP/1.0 %s %s\r\n", errnum, shortmsg);
Rio_writen(fd, buf, strlen(buf));
sprintf(buf, "Content-type: text/html\r\n");
Rio_writen(fd, buf, strlen(buf));
sprintf(buf, "Content-length: %d\r\n\r\n", (int) strlen(body));
Rio_writen(fd, buf, strlen(buf));
Rio_writen(fd, body, strlen(body));
}
sbuf.c
#include "sbuf.h"
void sbuf_init(sbuf_t *sp, int n)
{
sp->buf = Calloc(n, sizeof(int));
sp->n = n;
sp->front = sp->rear = 0;
Sem_init(&sp->mutex, 0, 1);
Sem_init(&sp->slots, 0, n);
Sem_init(&sp->items, 0, 0);
}
void sbuf_deinit(sbuf_t *sp)
{
Free(sp->buf);
}
void sbuf_insert(sbuf_t *sp, int item)
{
P(&sp->slots);
P(&sp->mutex);
sp->buf[(++sp->rear)%(sp->n)] = item;
V(&sp->mutex);
V(&sp->items);
}
int sbuf_remove(sbuf_t *sp)
{
int item;
P(&sp->items);
P(&sp->mutex);
item = sp->buf[(++sp->front)%(sp->n)];
V(&sp->mutex);
V(&sp->slots);
return item;
}
cache.c
#include "cache.h"
static CacheNode *cache_head;
static CacheNode *cache_tail;
static int total_cache_size;
static pthread_rwlock_t cache_lock;
static void evict_lru_node() {
if (cache_tail == NULL) return;
CacheNode *node_to_evict = cache_tail;
if (cache_tail->prev) {
cache_tail = cache_tail->prev;
cache_tail->next = NULL;
} else {
cache_head = NULL;
cache_tail = NULL;
}
total_cache_size -= node_to_evict->size;
Free(node_to_evict->key);
Free(node_to_evict->data);
Free(node_to_evict);
}
void cache_init() {
cache_head = NULL;
cache_tail = NULL;
total_cache_size = 0;
pthread_rwlock_init(&cache_lock, NULL);
}
int cache_find(char *key, int clientfd) {
pthread_rwlock_rdlock(&cache_lock);
CacheNode *current = cache_head;
while (current) {
if (strcmp(current->key, key) == 0) {
Rio_writen(clientfd, current->data, current->size);
pthread_rwlock_unlock(&cache_lock);
return 1;
}
current = current->next;
}
pthread_rwlock_unlock(&cache_lock);
return 0;
}
void cache_store(char *key, char *data, int size) {
if (size > MAX_OBJECT_SIZE) {
return;
}
pthread_rwlock_wrlock(&cache_lock);
while (total_cache_size + size > MAX_CACHE_SIZE) {
evict_lru_node();
}
CacheNode *new_node = Malloc(sizeof(CacheNode));
new_node->key = Malloc(strlen(key) + 1);
new_node->data = Malloc(size);
new_node->size = size;
strcpy(new_node->key, key);
memcpy(new_node->data, data, size);
new_node->prev = NULL;
new_node->next = cache_head;
if (cache_head) {
cache_head->prev = new_node;
}
cache_head = new_node;
if (cache_tail == NULL) {
cache_tail = new_node;
}
total_cache_size += size;
pthread_rwlock_unlock(&cache_lock);
}