proxylab.pdf
specifies the requirements.proxylab-handout.tar
provides the auto-grader and helper code (e.g. csapp.h
, csapp.c
, and the tiny
folder).# Get my port number (got 8982):
./port-for-user.pl pvc
# Build and start the tiny (web) server:
cd tiny
make && ./tiny 8981 &
cd -
# Build and start the proxy server:
make && ./proxy 8982 &
# Use the proxy via curl:
curl -v --proxy http://localhost:8982 http://localhost:8981/home.html
curl -v --proxy http://localhost:8982 http://localhost:8981/cgi-bin/adder\?15000\&213
curl -v --proxy http://localhost:8982 http://localhost:8981/cgi-bin/minus\?15000\&213 # triggers 404
# Auto grade the solution:
./driver.sh
Just mock echoserveri.c
and echo.c
.
char const */* the address of the first char in uri_to_server */
parse_uri(char const *uri_from_client, char *hostname/* output */);
Note that uri_to_server
is the suffix of uri_from_client
.
parse_uri()
The parse_uri()
implemented in tiny.c
modifies URI:
char *ptr = index(uri, '?');
*ptr = '\0';
strcat(filename, uri);
It can be avoided by using strncat()
instead of strcat()
:
char const *ptr = index(uri, '?');
strncat(filename, uri, ptr - uri);
int/* server_fd */ connect_to_server(char *hostname);
Replace the headers begin with GET
or Host
or User-Agent
or Connection
or Proxy-Connection
.
Send other headers unmodified.
void forward_request(int server_fd,
char const *method/* GET */,
char const *uri_to_server,
char const *hostname,
char const *buf/* the buffer holding request_from_client */);
void forward_response(int server_fd, int client_fd,
char const *uri_from_client/* the key for cache */,
char *buf/* buffer for holding reponse_from_server */);
The argument uri_from_client
is not necessary here, but is useful for implementing the LRU cache.
The test on this part
So, a simple create-on-request policy is enough for passing the test on this part:
void *routine(void *vargp) {
int client_fd = *((int *)vargp);
Pthread_detach(Pthread_self());
Free(vargp);
serve(client_fd);
return NULL;
}
void serve_by_thread(int client_fd) {
int *client_fd_ptr = Malloc(sizeof(int));
*client_fd_ptr = client_fd;
pthread_t tid;
Pthread_create(&tid, NULL, routine, client_fd_ptr);
}
A more efficient but complicated version like echoservert-pre.c
in the textbook is also possible.
The binary sem_t
for protecting the pool could be replaced by a pthread_mutex_t
, which is used in my solution.
uthash
and utlist
Types:
struct _node;
typedef struct _node node_t;
struct _item;
typedef struct _item item_t;
struct _lru;
typedef struct _lru lru_t;
struct _item {
char *key; // URI from a client
struct {
node_t *node; // node in a `list`
char *data; // response from a server
int size; // size of the response
} value;
UT_hash_handle hh; /* makes this structure hashable */
};
struct _node {
item_t *item;
node_t *prev, *next; /* needed for doubly-linked lists */
};
struct _lru {
item_t *map;
node_t *list;
int capacity; // max size of the sum of all item->value.data
int size; // current size of the sum of all item->value.data
};
Methods:
lru_t *lru_construct(int capacity);
void lru_destruct(lru_t *lru);
item_t *lru_find(lru_t const *lru, char const *key);
void lru_print(lru_t const *lru);
void lru_emplace(lru_t *lru, char const *key, char const *data, int size);
void lru_sink(lru_t *lru, item_t *item);
void lru_pop(lru_t *lru);
Tests:
make lru_test
./lru_test
valgrind --leak-check=yes ./lru_test
pthread_rwlock_t
lru_find()
is read-only:
pthread_rwlock_rdlock(&lru_rwlock);
item_t *item = lru_find(lru, uri_from_client);
if (item) {
/* .. */
Rio_writen(client_fd, item->value.data, item->value.size);
}
pthread_rwlock_unlock(&lru_rwlock);
lru_emplace()
is a writing operation:
pthread_rwlock_wrlock(&lru_rwlock);
// The item might already been emplaced by another thread, so find it again:
if (!lru_find(lru, uri_from_client)) {
lru_emplace(lru, uri_from_client, buf, size);
}
pthread_rwlock_unlock(&lru_rwlock);
lru_sink()
is also a writing operation:
pthread_rwlock_wrlock(&lru_rwlock);
// The item might already been popped by another thread, so find it again:
if ((item = lru_find(lru, uri_from_client)) != NULL) {
lru_sink(lru, item);
}
pthread_rwlock_unlock(&lru_rwlock);