1
+ /*
2
+ * proxy.c -- a simple http proxy supporting GET operation
3
+ */
4
+
1
5
#include <stdio.h>
2
6
#include <stdlib.h>
3
7
#include <assert.h>
6
10
#include "bytes.h"
7
11
#include "cache.h"
8
12
9
- #define DEBUG
13
+ // #define DEBUG
14
+ #undef DEBUG
10
15
11
16
/* Recommended max cache and object sizes */
12
17
#define MAX_CACHE_SIZE 1049000
13
18
#define MAX_OBJECT_SIZE 102400
14
19
20
+ /* global lru cache */
15
21
lru_cache_t lru_cache ;
22
+
23
+ /* mutex to protect the lru cache update operations */
16
24
sem_t mutex ;
17
25
18
26
@@ -22,60 +30,98 @@ static const char *accept_hdr = "Accept: text/html,application/xhtml+xml,applica
22
30
static const char * accept_encoding_hdr = "Accept-Encoding: gzip, deflate\r\n" ;
23
31
24
32
33
+ /* usage */
25
34
void usage ()
26
35
{
27
- // TODO
36
+ printf ( "Usage: proxy [port]\n" );
28
37
exit (-1 );
29
38
}
30
39
31
40
32
41
42
+ /* forward_response -- forward the response back to the client
43
+ * key: the formatted url of the content. Used for lru_cache
44
+ * infd: the file descriptor of remote server. We read response from infd
45
+ * outfd: the client file descriptor. We write response back to outfd
46
+ */
33
47
void forward_response (const char * key , int infd , int outfd )
34
48
{
35
49
rio_t rio ;
36
50
rio_readinitb (& rio , infd );
37
51
52
+ /* Since we don't know the length of the response, we extend
53
+ * the response buffer dynamically.
54
+ * First, we read MAXLINE number of bytes into the buffer.
55
+ * Then we append the buffer to the response buffer.
56
+ */
57
+
58
+ /* temporary buffer */
38
59
char buf [MAXLINE ];
60
+
61
+ /* response buffer */
39
62
struct Bytes response ;
63
+
64
+ /* allocate and initialize the response buffer */
40
65
bytes_malloc (& response );
41
66
67
+ /* num_bytes is the number of bytes read for each rio_readnb operation */
42
68
int num_bytes ;
43
69
while ((num_bytes = rio_readnb_ww (& rio , buf , MAXLINE )) > 0 ) {
44
70
bytes_appendn (& response , buf , num_bytes );
45
71
}
72
+
73
+ /* The remote connection may be closed during the read operation.
74
+ * The return value is < 0.
75
+ */
46
76
if (num_bytes < 0 ) {
47
- return ;
77
+ /* goto is used here to make sure that response is freed
78
+ */
79
+ goto FORWARD_RESPONSE_RETURN ;
48
80
}
49
81
50
82
#ifdef DEBUG
51
83
fprintf (stderr , "response length: %zu\n" , bytes_length (response ));
52
84
#endif
53
85
86
+ /* the client may have closed the connection */
54
87
if (!rio_writen_ww (outfd ,
55
88
bytes_buf (response ),
56
89
bytes_length (response ))) {
57
90
goto FORWARD_RESPONSE_RETURN ;
58
91
}
59
92
93
+ /* cache the response if necessary */
60
94
if (bytes_length (response ) < MAX_OBJECT_SIZE ) {
61
95
sem_wait (& mutex );
62
96
lru_cache_insert (& lru_cache , key , bytes_buf (response ),
63
97
bytes_length (response ));
64
98
sem_post (& mutex );
65
99
}
66
100
FORWARD_RESPONSE_RETURN :
101
+ /* free the response */
67
102
bytes_free (& response );
68
103
}
69
104
105
+ /* forward -- forward the request to remote server and the response from
106
+ * remote server
107
+ */
70
108
void forward (int fromfd )
71
109
{
72
110
rio_t rio ;
73
111
rio_readinitb (& rio , fromfd );
74
- char linebuf [MAXLINE ], method [MAXLINE ], uri [MAXLINE ],
75
- version [MAXLINE ], host [MAXLINE ],
76
- port [MAXLINE ], dir [MAXLINE ],
77
- request_buf [MAXBUF ],
78
- header_name [MAXLINE ],
112
+ char linebuf [MAXLINE ], // temporary buffer for each line we read
113
+ method [MAXLINE ], // http method
114
+ uri [MAXLINE ], // uri
115
+ version [MAXLINE ], // http version.
116
+ host [MAXLINE ],
117
+ port [MAXLINE ],
118
+ dir [MAXLINE ], // the director after the host in the uri
119
+ /* TODO: is it necessary to use a dynamic buffer to store
120
+ * the request?
121
+ * For all the cases tested now, seems it's not necessary
122
+ */
123
+ request_buf [MAXBUF ], // The request is stored in the buffer
124
+ header_name [MAXLINE ], // used for parsing the header
79
125
header_value [MAXLINE ];
80
126
81
127
if (!rio_readlineb_ww (& rio , linebuf , MAXLINE )) {
@@ -95,7 +141,13 @@ void forward(int fromfd)
95
141
96
142
char formated_uri [MAXLINE ];
97
143
sprintf (formated_uri , "%s:%s%s" , host , port , dir );
144
+
145
+ /*
146
+ * if we find the content in the cache, we return it directly.
147
+ * TODO: What if the content has been modifed?
148
+ */
98
149
sem_wait (& mutex );
150
+
99
151
lru_cache_node_t * pnode = lru_cache_find (& lru_cache , formated_uri );
100
152
101
153
if (pnode ) {
@@ -109,10 +161,11 @@ void forward(int fromfd)
109
161
* forwarded as HTTP/1.0
110
162
*/
111
163
sprintf (request_buf , "%s %s %s\r\n" , method , dir , "HTTP/1.0" );
112
- // sprintf(request_buf, "%s %s %s\r\n", method, dir, version);
113
164
165
+ /* if the header contains host, we forward it directly.
166
+ * Otherwise we fill the host according to the uri
167
+ */
114
168
int has_host = 0 ;
115
- // request headers
116
169
if (rio_readlineb_ww (& rio , linebuf , MAXLINE ) < 0 ) {
117
170
return ;
118
171
}
@@ -126,18 +179,22 @@ void forward(int fromfd)
126
179
strcasecmp (header_name , "Accept-Encoding" ) == 0 ||
127
180
strcasecmp (header_name , "Connection" ) == 0 ||
128
181
strcasecmp (header_name , "Proxy-Connection" ) == 0 ) {
129
- // ignore
182
+ // we have default values for these headers
130
183
} else {
184
+ // for other headers, we forward it directly
131
185
sprintf (request_buf , "%s%s" , request_buf , linebuf );
132
186
}
133
187
if (rio_readlineb_ww (& rio , linebuf , MAXLINE ) < 0 ) {
134
188
return ;
135
189
}
136
190
}
137
191
if (!has_host ) {
192
+ /* add host according to uri parsing result */
138
193
sprintf (request_buf , "%s%s: %s:%s\r\n" , request_buf , "Host" ,
139
194
host , port );
140
195
}
196
+
197
+ /* add default value for these headers */
141
198
sprintf (request_buf , "%s%s" , request_buf ,
142
199
user_agent_hdr );
143
200
sprintf (request_buf , "%s%s" , request_buf ,
@@ -161,13 +218,12 @@ void forward(int fromfd)
161
218
return ;
162
219
}
163
220
164
- // receive data
165
- // read_response(clientfd);
166
-
221
+ /* forward the response of the server to the client */
167
222
forward_response (formated_uri , clientfd , fromfd );
168
223
close_ww (clientfd );
169
224
}
170
225
226
+ /* thread -- the things to do for each thread */
171
227
void * thread (void * vargp )
172
228
{
173
229
int connfd = * ((int * )vargp );
@@ -179,12 +235,18 @@ void *thread(void *vargp)
179
235
}
180
236
181
237
238
+ /* sigpipe_handler -- handle sig pipe
239
+ * SIGPIPE is generated when the connection is closed by another end early.
240
+ * */
182
241
void sigpipe_handler (int sig )
183
242
{
243
+ #ifdef DEBUG
184
244
fprintf (stderr , "[WARNING] Catch a sigpipe signal\n" );
245
+ #endif
185
246
}
186
247
187
248
249
+ /* main */
188
250
int main (int argc , char * * argv )
189
251
{
190
252
if (argc != 2 ) {
0 commit comments