-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathproxy.c
More file actions
314 lines (265 loc) · 9.08 KB
/
proxy.c
File metadata and controls
314 lines (265 loc) · 9.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
/* Used the following resources in my assignment:
* https://github.com/yichaoxue871007/ICS-Labs/blob/master/proxylab-handout/proxy.c
* https://github.com/mrigankdoshy/sequential-caching-web-proxy/blob/master/proxy.c
* https://github.com/jcksber/CMU_15-213_caching-web-proxy
* https://github.com/mindbergh/ProxyLab
*/
#include <stdio.h>
#include "sbuf.h"
#include "sbuf.c"
#include "csapp.h"
#include "cache.h"
/* Recommended max cache and object sizes */
#define MAX_CACHE_SIZE 1049000
#define MAX_OBJECT_SIZE 102400
#define NTHREADS 32
#define SBUFSIZE 32
/* Shared buffer of connection file descriptors */
sbuf_t sbuf;
/* Global variables */
static const char *user_agent_hdr = "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36\r\n";
static const char *connection_hdr = "Connection: close\r\n";
static const char *proxy_connection_hdr = "Proxy-Connection: close\r\n";
static const char *end_hdr = "\r\n";
static const char *default_port = "80";
/* Request handling functions */
void *thread(void *vargp);
void doit(int client);
int is_error(char *obj);
int parse_req(int client, rio_t *rio, char *host, char *port, char *path);
void parse_uri(char *uri, char *host, char *port, char *path);
void forward_req(int server, int client, char *host, char *path);
/* Error handling functions */
void flush_str(char *str);
void flush_strs(char *str, char *str2, char *str3);
/* sbuf functions */
void sbuf_init(sbuf_t *sp, int n);
void sbuf_deinit(sbuf_t *sp);
void sbuf_insert(sbuf_t *sp, int item);
int sbuf_remove(sbuf_t *sp);
/* Global web cache */
cache *C;
pthread_rwlock_t lock;
/*
* Main routine: accept connections and place them
* in the shared buffer for a worker thread to process
*/
int main(int argc, char **argv){
int listenfd, client;
pthread_t tid;
socklen_t clientlen;
struct sockaddr_storage clientaddr;
/* Check commandline arguments */
if(argc != 3){
fprintf(stderr, "ERROR: usage: %s <port> <replacement_policy>\n", argv[0]);
exit(0);
}
/* Initialize cache */
C = Malloc(sizeof(struct web_cache));
/* Check eviction policy
* 0 - LRU
* 1 - LFU
*/
if(strcmp(argv[2], "LRU") == 0){
init_cache(C, 0);
} else if(strcmp(argv[2], "LFU") == 0){
init_cache(C, 1);
} else{
fprintf(stderr, "ERROR: unsupported eviction policy\n");
exit(0);
}
/* Initialize read-write lock */
pthread_rwlock_init(&lock, NULL);
/* Listen on port specified by the user */
listenfd = Open_listenfd(argv[1]);
/* Create a thread pool */
sbuf_init(&sbuf, SBUFSIZE);
for(int i = 0; i < NTHREADS; i++){
Pthread_create(&tid, NULL, thread, NULL);
}
/* Wait for and eventually accept a connection */
while(1){
clientlen = sizeof(struct sockaddr_storage);
client = Accept(listenfd, (SA *) &clientaddr, &clientlen);
sbuf_insert(&sbuf, client);
}
free_cache(C);
pthread_rwlock_destroy(&lock);
}
void *thread(void *vargp){
Pthread_detach(pthread_self());
while(1){
int client = sbuf_remove(&sbuf);
doit(client);
Close(client);
}
}
/* Check for errors in the client request, parse the request,
* forward the request to server, and finally forward server
* response to client */
void doit(int client){
int server;
char host[MAXLINE], port[MAXLINE], path[MAXLINE];
rio_t rio;
/* Parse client request into host, port, and path */
if(parse_req(client, &rio, host, port, path) < 0){
fprintf(stderr, "ERROR: cannot read this request path\n");
flush_strs(host, port, path);
}
/* Parsing succeeded, continue */
else{
pthread_rwlock_rdlock(&lock);
line *lion = in_cache(C, host, path);
pthread_rwlock_unlock(&lock);
/* If the URL has been seen before and is in the cache,
* do not connect to the server. Instead, we send the file
* content directly from the cache
*/
if(lion != NULL){
if(rio_writen(client, lion->obj, lion->size) < 0){
fprintf(stderr, "ERROR: rio_writen error: bad connection\n");
}
printf("Debug: using cache\n");
flush_strs(host, port, path);
}
/* Otherwise, it is a new request. We connect to server
* and forward request
*/
else{
printf("Debug: no cache\n");
if((server = open_clientfd(host, port)) < 0){ // open connection to server
fprintf(stderr, "ERROR: could not establish connection to server\n");
flush_strs(host, port, path);
} else{
forward_req(server, client, host, path);
flush_strs(host, port, path);
Close(server);
}
}
}
}
/* Forward request to the server and back to the client. Store content
* into the cache */
void forward_req(int server, int client, char *host, char *path){
/* Client-side reading */
char clbuf[MAXLINE];
/* Server-side reading */
char svbuf[MAXLINE];
rio_t rio;
ssize_t n = 0;
/* Web object cache */
char object[MAX_OBJECT_SIZE];
size_t obj_size = 0;
/* ---- BUILD & FORWARD REQUEST TO SERVER ---- */
sprintf(clbuf, "GET %s HTTP/1.0\r\n", path);
sprintf(clbuf, "%sHost: %s\r\n", clbuf, host);
strcat(clbuf, user_agent_hdr);
strcat(clbuf, connection_hdr);
strcat(clbuf, proxy_connection_hdr);
strcat(clbuf, end_hdr);
/* Forward request to server */
if(rio_writen(server, clbuf, strlen(clbuf)) < 0){
fprintf(stderr, "ERROR: writing to server failed");
return;
}
/* ---- FORWARD SERVER RESPONSE TO CLIENT ---- */
Rio_readinitb(&rio, server);
/* Read n bytes from the server. If the total number of bytes
* read is less than or equal to MAX_OBJECT_SIZE, we save it
* into the cache.
*/
while((n = rio_readnb(&rio, svbuf, MAXLINE)) != 0){
/* For caching */
if((obj_size + n) <= MAX_OBJECT_SIZE){
memcpy(object + obj_size, svbuf, n);
obj_size += n;
}
/* Write to client */
Rio_writen(client, svbuf, n);
flush_str(svbuf);
}
/* Object is not cached, if the total bytes read is less
* small enough, we store it into the cache
*/
if(obj_size <= MAX_OBJECT_SIZE && !is_error(object)){
pthread_rwlock_wrlock(&lock);
add_line(C, make_line(host, path, object, obj_size));
pthread_rwlock_unlock(&lock);
}
}
/* Determine if obj is a server error or not
* 1 if it is, 0 if it isn't*/
int is_error(char *obj){
size_t obj_size = strlen(obj) + 1;
char object[obj_size];
memset(object, 0, sizeof(object));
memcpy(object, obj, obj_size);
return strstr(object, "200") == NULL ? 1 : 0;
}
int parse_req(int client, rio_t *rio, char *host, char *port, char *path){
/* Parse request into method, uri, and version */
char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE];
/* Initialize rio */
Rio_readinitb(rio, client);
/* Read the first line of the request */
if(!Rio_readlineb(rio, buf, MAXLINE)){
fprintf(stderr, "ERROR: bad request\n");
return -1;
}
/* Splice the client request */
sscanf(buf, "%s %s %s", method, uri, version);
/***************************************/
printf("---------Parsing request-----------\n");
printf("Method: %s\n", method);
printf("URI: %s\n", uri);
printf("Version: %s\n", version);
printf("\n");
/***************************************/
if(strcasecmp(method, "GET")){
fprintf(stderr, "ERROR: proxy does not implement this method\n");
return -1;
}
/* Parse the URI to get hostname, path and port*/
parse_uri(uri, host, port, path);
/*****************************************/
printf("_________Parsing URI_________\n");
printf("Host: %s\n", host);
printf("Port: %s\n", port);
printf("Path: %s\n", path);
printf("\n");
/*****************************************/
return 0;
}
void parse_uri(char *uri, char *host, char *port, char *path){
char *host_ptr, *path_ptr, *port_ptr;
char hostname[MAXLINE];
/* Ignore "http://" */
host_ptr = uri + strlen("http://");
if((path_ptr = strchr(host_ptr, '/')) == NULL){ // path is empty
strcpy(path, "/");
strcpy(hostname, host_ptr);
} else{
strcpy(path, path_ptr);
strncpy(hostname, host_ptr, (path_ptr - host_ptr));
hostname[path_ptr - host_ptr] = '\0';
}
if((port_ptr = strchr(hostname, ':')) == NULL){ // port undefined
strcpy(host, hostname);
strcpy(port, default_port);
}else{
strcpy(port, port_ptr + 1);
strncpy(host, hostname, (port_ptr - hostname));
host[port_ptr - hostname] = '\0';
}
}
/********************
* CLEAN-UP FUNCTIONS
********************/
void flush_str(char *str){
if(str) memset(str, 0, sizeof(*str));
}
void flush_strs(char *str1, char *str2, char *str3){
if(str1) flush_str(str1);
if(str2) flush_str(str2);
if(str3) flush_str(str3);
}