-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathproxy.c
223 lines (189 loc) · 5.77 KB
/
proxy.c
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
#include <stdio.h>
#include "csapp.h"
#include "cache.h"
#include "sbuf.h"
/* Recommended max cache and object sizes */
#define MAX_CACHE_SIZE 1049000
#define MAX_OBJECT_SIZE 102400
#define PROTLEN 7 /* Length of http:// */
#define VERLEN 8 /* Length of HTTP/1.0 and HTTP/1.0 */
#define NTHREADS 4 /* Number of worker threads */
#define SBUFSIZE 16 /* The size of the request buffer */
/* Headers for http request */
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";
static const char *host_key = "Host";
static const char *connection_hdr = "Connection: close\r\n";
static const char *proxy_conn_hdr = "Proxy-Connection: close\r\n";
sbuf_t sbuf; /* Shared buffer of connected descriptors */
/* Routines for the web proxy */
void process_request(int connfd);
void parse_url(char *url, char *hostname, char *port, char *uri);
void build_requesthdrs(rio_t *rp, char *hostname, char *request_hdrs);
void *thread(void *vargp);
void sig_int_handler(int signal);
/* Global variables for synchronizing accesses to cache */
int readcount; /* Number of threads reading cache */
sem_t mutex; /* Semaphore protecting readcount */
sem_t mutex_w; /* Semaphore protecting writing to cache */
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[1]);
exit(1);
}
/* Install signal handler */
Signal(SIGPIPE, SIG_IGN); /* Ignore SIGPIPE (broken pipe) */
Signal(SIGINT, sig_int_handler);
init_cache();
readcount = 0;
Sem_init(&mutex, 0, 1);
Sem_init(&mutex_w, 0, 1);
int listenfd = Open_listenfd(argv[1]);
pthread_t tid;
sbuf_init(&sbuf, SBUFSIZE);
for (int i = 0; i < NTHREADS; ++i) {
Pthread_create(&tid, NULL, thread, NULL);
}
struct sockaddr_storage clientaddr;
socklen_t clientlen;
char hostname[MAXLINE], port[MAXLINE];
int connfd;
while (1) {
clientlen = sizeof(struct sockaddr_storage);
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);
}
return 0;
}
void *thread(void *vargp) {
Pthread_detach(pthread_self());
while (1) {
int connfd = sbuf_remove(&sbuf);
process_request(connfd);
Close(connfd);
}
return NULL;
}
void process_request(int connfd) {
char buf[MAXLINE];
rio_t rio;
Rio_readinitb(&rio, connfd);
if (!rio_readlineb(&rio, buf, MAXLINE)) /* Read request line */
return; /* Nothing to read */
/* Parse the request line */
char method[MAXLINE], url[MAXLINE], version[MAXLINE];
if (sscanf(buf, "%s %s %s", method, url, version) != 3) {
printf("Invalid HTTP request\n");
return;
}
/* Check for valid request line */
if (strcmp(method, "GET")) {
printf("Method not implemented\n");
return;
}
if (strncasecmp(url, "http://", PROTLEN)) {
printf("Invalid URL\n");
return;
}
if (strncasecmp(version, "HTTP/1.0", VERLEN)
&& strncasecmp(version, "HTTP/1.1", VERLEN)) {
printf("Invalid request version\n");
return;
}
char hostname[MAXLINE], port[MAXLINE], uri[MAXLINE];
parse_url(url, hostname, port, uri);
/* Create HTTP request to server */
char http_request[MAXLINE], request_hdrs[MAXLINE];
sprintf(http_request, "GET %s HTTP/1.0\r\n", uri);
build_requesthdrs(&rio, hostname, request_hdrs);
strcat(http_request, request_hdrs);
P(&mutex);
readcount++;
if (readcount == 1)
P(&mutex_w);
V(&mutex);
cache_object *cache_obj = find_cache_object(url);
P(&mutex);
readcount--;
if (readcount == 0)
V(&mutex_w);
V(&mutex);
if (cache_obj != NULL) {
Rio_writen(connfd, cache_obj->buf, cache_obj->size);
printf("Retrieved web object from proxy cache\n");
return;
}
/* Connect to the server */
/* FD that proxy uses to communicate with server */
int proxyfd = Open_clientfd(hostname, port);
Rio_writen(proxyfd, http_request, strlen(http_request));
/* Forward response from server to client */
rio_t rioServer;
Rio_readinitb(&rioServer, proxyfd);
size_t n, total_byte = 0;
/* For caching */
char cache_object[MAX_OBJECT_SIZE];
char *p = cache_object;
while ((n = Rio_readlineb(&rioServer, buf, MAXLINE)) != 0) {
Rio_writen(connfd, buf, n);
total_byte += n;
if (total_byte < MAX_OBJECT_SIZE) {
strcpy(p, buf);
p += n;
}
}
if (total_byte < MAX_OBJECT_SIZE) {
P(&mutex_w);
insert_obj(url, cache_object, total_byte);
V(&mutex_w);
}
Close(proxyfd);
}
void parse_url(char *url, char *hostname, char *port, char *uri) {
url += PROTLEN; /* Skip http:// */
strcpy(hostname, url);
char *p = index(hostname, '/');
if (p) {
strcpy(uri, p);
*p = '\0'; /* Remove uri from hostname */
} else {
strcpy(uri, "/");
}
/* Get port */
if (strstr(hostname, ":")) {
p = index(hostname, ':');
strcpy(port, p + 1);
*p = '\0'; /* Remove port from hostname */
} else {
strcpy(port, "80");/* Default port */
}
}
void build_requesthdrs(rio_t *rp, char *hostname, char *request_hdrs) {
char buf[MAXLINE], has_host = 0;
char host_hdr[MAXLINE], other_hdrs[MAXLINE];
while (1) {
Rio_readlineb(rp, buf, MAXLINE);
if (!strcmp(buf, "\r\n"))
break;
if (!strncasecmp(buf, host_key, strlen(host_key))) {
has_host = 1;
strcpy(host_hdr, buf);
} else
strcat(other_hdrs, buf);
}
if (!has_host) {
sprintf(host_hdr, "Host: %s\r\n", hostname);
}
/* Combine all headers */
sprintf(request_hdrs, "%s%s%s%s%s%s", host_hdr, user_agent_hdr,
connection_hdr, proxy_conn_hdr, other_hdrs, "\r\n");
return;
}
/* Handle interrupt signal */
void sig_int_handler(int signal) {
printf("\n");
free_cache();
exit(0);
}