这是本书的最后一个lab了,但总体上没有前面的malloclab和shelllab难,好好看书,多多参考tinyweb的代码,还是比较容易写出来的。

代码

  1. #include <stdio.h>
  2. #include "csapp.h"
  3. /* Recommended max cache and object sizes */
  4. #define MAX_CACHE_SIZE 1049000
  5. #define MAX_OBJECT_SIZE 102400
  6. #define MMAXLINE 2048
  7. #define MAXHEADER 4096
  8. #define MAXCACHEBLOCK 50
  9. struct cache_block
  10. {
  11. char header[MAXHEADER];
  12. char path[MMAXLINE];
  13. char *body;
  14. int body_size;
  15. char visit;
  16. };
  17. struct _cache
  18. {
  19. struct cache_block *block_slot[MAXCACHEBLOCK];
  20. int clock_p;
  21. int used_size;
  22. int used_block;
  23. } cache;
  24. int read_cnt;
  25. sem_t mutex, write_lock;
  26. /* You won't lose style points for including this long line in your code */
  27. 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";
  28. void doit(int connfd);
  29. void request_back_server(rio_t *rio_buf, char *host, char *port, char *path, char *real_path);
  30. void http_error(int fd, char *errnum, char *msg);
  31. int parse_request(char *request, char *host, char *port, char *path);
  32. void deal_request_header(char *header_line, int back_server_fd);
  33. void get_back_response_and_send_to_client(int fd_conn_back_server, int client_fd, char *path);
  34. void html_error(int fd, char *msg);
  35. void *thread(void *vargp);
  36. void init_cache();
  37. int read_cache(char *header, char *body, char *real_path);
  38. int write_cache(char *header, char *body, int body_size, char *real_path);
  39. int next_slot(int need_size);
  40. void evict();
  41. int main(int argc, char **argv)
  42. {
  43. //printf("%s
  44. ", user_agent_hdr);
  45. int listenfd, connfd;
  46. pthread_t tid;
  47. int *vargs = NULL;
  48. socklen_t clientlen;
  49. struct sockaddr_storage clientaddr;
  50. /* Check command line args */
  51. if (argc != 2)
  52. {
  53. fprintf(stderr, "usage: %s <port>
  54. ", argv[0]);
  55. exit(1);
  56. }
  57. init_cache();
  58. listenfd = Open_listenfd(argv[1]);
  59. while (1)
  60. {
  61. clientlen = sizeof(clientaddr);
  62. connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);
  63. //doit(connfd);
  64. if ((vargs = (int *)malloc(sizeof(int))) == NULL)
  65. {
  66. http_error(connfd, "500", "not enough memory!");
  67. Close(connfd);
  68. }
  69. else
  70. {
  71. *vargs = connfd;
  72. pthread_create(&tid, NULL, thread, (void *)vargs);
  73. }
  74. }
  75. }
  76. void *thread(void *vargp)
  77. {
  78. int connfd = *((int *)vargp);
  79. pthread_detach(pthread_self());
  80. free(vargp);
  81. doit(connfd);
  82. Close(connfd);
  83. return NULL;
  84. }
  85. void doit(int connfd)
  86. {
  87. rio_t Riobuf;
  88. char req_buf[MMAXLINE], host[MMAXLINE], port[MMAXLINE], path[MMAXLINE], real_path[MAXLINE];
  89. char *header, *body;
  90. int body_size;
  91. rio_readinitb(&Riobuf, connfd);
  92. if (!rio_readlineb(&Riobuf, req_buf, MMAXLINE))
  93. return;
  94. if (parse_request(req_buf, host, port, path) < 0)
  95. http_error(connfd, "403", "Unknown request");
  96. else
  97. {
  98. sprintf(real_path, "%s:%s%s", host, port, path);
  99. header = (char *)malloc(MAXHEADER);
  100. body = (char *)malloc(MAX_OBJECT_SIZE);
  101. if (header != NULL && body != NULL)
  102. {
  103. if ((body_size = read_cache(header, body, real_path)) >= 0)
  104. {
  105. Rio_writen(connfd, header, strlen(header));
  106. Rio_writen(connfd, "
  107. ", strlen("
  108. "));
  109. Rio_writen(connfd, body, body_size);
  110. free(header);
  111. free(body);
  112. return;
  113. }
  114. }
  115. free(header);
  116. free(body);
  117. request_back_server(&Riobuf, host, port, path, real_path);
  118. }
  119. }
  120. /*根据第一行的请求解析出host,port与请求path,正确返回0,出错返回-1*/
  121. int parse_request(char *request, char *host, char *port, char *path)
  122. {
  123. char method[MMAXLINE], path_buf[MMAXLINE], version[MMAXLINE], host_buf[MMAXLINE], port_buf[MMAXLINE];
  124. char *i, *host_buf_p, *port_buf_p;
  125. int have_port = 1;
  126. memset(path_buf, 0, MMAXLINE);
  127. sscanf(request, "%s %s %s", method, path_buf, version);
  128. if (strcmp(method, "GET"))
  129. return -1;
  130. i = path_buf;
  131. host_buf_p = host_buf;
  132. port_buf_p = port_buf;
  133. while (*i != '/' && *i != '')
  134. i++;
  135. if (*i != '')
  136. i++;
  137. if (*i != '')
  138. i++;
  139. while (*i != ':' && *i != '')
  140. {
  141. if (*i == '/')
  142. {
  143. have_port = 0;
  144. break;
  145. }
  146. *host_buf_p++ = *i++;
  147. }
  148. *host_buf_p = '';
  149. if (!have_port)
  150. {
  151. sprintf(port_buf_p, "80");
  152. }
  153. else
  154. {
  155. i++;
  156. while (*i != '/' && *i != '')
  157. {
  158. *port_buf_p++ = *i++;
  159. }
  160. *port_buf_p = '';
  161. }
  162. if (*i == '')
  163. return -1;
  164. sprintf(host, "%s", host_buf);
  165. sprintf(port, "%s", port_buf);
  166. sprintf(path, "%s", i);
  167. //sprintf(request, "%s %s %s
  168. ", method, i, "HTTP/1.0");
  169. return 0;
  170. }
  171. /*解析剩下的头部,构造并发送向后台服务器的请求,最后接受请求并返回给客户端*/
  172. void request_back_server(rio_t *rio_buf, char *host, char *port, char *path, char *real_path)
  173. {
  174. int fd_conn_back_server, fd_conn_client;
  175. char buf[MMAXLINE];
  176. fd_conn_client = rio_buf->rio_fd;
  177. fd_conn_back_server = Open_clientfd(host, port);
  178. sprintf(buf, "GET %s HTTP/1.0
  179. ", path);
  180. Rio_writen(fd_conn_back_server, buf, strlen(buf));
  181. sprintf(buf, "Host: %s:%s
  182. ", host, port);
  183. Rio_writen(fd_conn_back_server, buf, strlen(buf));
  184. sprintf(buf, "User-Agent: %s
  185. ", user_agent_hdr);
  186. Rio_writen(fd_conn_back_server, buf, strlen(buf));
  187. sprintf(buf, "Connection: close
  188. ");
  189. Rio_writen(fd_conn_back_server, buf, strlen(buf));
  190. sprintf(buf, "Proxy-Connection: close
  191. ");
  192. Rio_writen(fd_conn_back_server, buf, strlen(buf));
  193. while (1)
  194. {
  195. Rio_readlineb(rio_buf, buf, MMAXLINE);
  196. if (!strcmp(buf, "
  197. "))
  198. {
  199. Rio_writen(fd_conn_back_server, buf, strlen(buf));
  200. break;
  201. }
  202. deal_request_header(buf, fd_conn_back_server);
  203. }
  204. get_back_response_and_send_to_client(fd_conn_back_server, fd_conn_client, real_path);
  205. //Close(connfd);
  206. return;
  207. }
  208. /*处理请求头,忽略给定的头(需要修改的头),并把其余头发送给后台服务器*/
  209. void deal_request_header(char *header_line, int back_server_fd)
  210. {
  211. char key[MMAXLINE], value[MMAXLINE];
  212. sscanf(header_line, "%s %s", key, value);
  213. if (!strcmp(key, "Host:"))
  214. return;
  215. else if (!strcmp(key, "User-Agent:"))
  216. {
  217. return;
  218. }
  219. else if (!strcmp(key, "Connection:"))
  220. {
  221. return;
  222. }
  223. else if (!strcmp(key, "Proxy-Connection:"))
  224. {
  225. return;
  226. }
  227. else
  228. {
  229. Rio_writen(back_server_fd, header_line, strlen(header_line));
  230. return;
  231. }
  232. }
  233. /*读后台的返回,并且写给客户端*/
  234. void get_back_response_and_send_to_client(int fd_conn_back_server, int client_fd, char *real_path)
  235. {
  236. int body_length, body_length_record;
  237. ssize_t read_size;
  238. rio_t rio_buf;
  239. char buf[MMAXLINE], key[MMAXLINE], value[MMAXLINE];
  240. char *body_data, *temp_body_data_ptr, *temp_buf_ptr, *header_data;
  241. Rio_readinitb(&rio_buf, fd_conn_back_server);
  242. if ((header_data = (char *)malloc(MAXHEADER)) == NULL)
  243. {
  244. http_error(client_fd, "500", "not enough memory");
  245. return;
  246. }
  247. Rio_readlineb(&rio_buf, buf, MMAXLINE); //返回码
  248. Rio_writen(client_fd, buf, strlen(buf));
  249. sprintf(header_data, "%s", buf);
  250. while (1)
  251. { /*读头部*/
  252. Rio_readlineb(&rio_buf, buf, MMAXLINE);
  253. if (!strcmp(buf, "
  254. "))
  255. {
  256. Rio_writen(client_fd, buf, strlen(buf));
  257. break;
  258. }
  259. //deal_request_header(buf, fd_conn_back_server);
  260. sscanf(buf, "%s %s", key, value);
  261. if (!strcmp(key, "Content-length:"))
  262. {
  263. body_length = atoi(value);
  264. }
  265. Rio_writen(client_fd, buf, strlen(buf));
  266. strcat(header_data, buf);
  267. }
  268. if (body_length > MAX_OBJECT_SIZE) //不能缓存
  269. {
  270. while (body_length > 0)
  271. {
  272. read_size = Rio_readnb(&rio_buf, buf, MMAXLINE);
  273. body_length -= read_size;
  274. Rio_writen(client_fd, buf, read_size);
  275. }
  276. free(header_data);
  277. }
  278. else //可以缓存
  279. {
  280. if ((body_data = (char *)malloc(body_length)) == NULL)
  281. {
  282. html_error(client_fd, "not enough memory");
  283. //Close(client_fd);
  284. Close(fd_conn_back_server);
  285. return;
  286. }
  287. body_length_record = body_length;
  288. temp_body_data_ptr = body_data;
  289. while (body_length > 0)
  290. {
  291. read_size = Rio_readnb(&rio_buf, buf, MMAXLINE);
  292. body_length -= read_size;
  293. Rio_writen(client_fd, buf, read_size);
  294. temp_buf_ptr = buf; //写局部缓存
  295. while (read_size > 0)
  296. {
  297. *temp_body_data_ptr++ = *temp_buf_ptr++;
  298. read_size--;
  299. }
  300. }
  301. write_cache(header_data, body_data, body_length_record, real_path);
  302. free(header_data);
  303. free(body_data);
  304. }
  305. Close(fd_conn_back_server);
  306. //Rio_writen(client_fd, )
  307. }
  308. /*如函数名*/
  309. void http_error(int fd, char *errnum, char *msg)
  310. {
  311. char buf[MMAXLINE];
  312. /* Print the HTTP response headers */
  313. sprintf(buf, "HTTP/1.0 %s %s
  314. ", errnum, msg);
  315. Rio_writen(fd, buf, strlen(buf));
  316. sprintf(buf, "Content-type: text/html
  317. ");
  318. Rio_writen(fd, buf, strlen(buf));
  319. /* Print the HTTP response body */
  320. sprintf(buf, "<html><title>Request Error</title>");
  321. Rio_writen(fd, buf, strlen(buf));
  322. sprintf(buf, "<body bgcolor="
  323. "ffffff"
  324. ">
  325. ");
  326. Rio_writen(fd, buf, strlen(buf));
  327. sprintf(buf, "%s: %s
  328. ", errnum, msg);
  329. Rio_writen(fd, buf, strlen(buf));
  330. sprintf(buf, "<hr><em>The Tiny Proxy server</em>
  331. ");
  332. Rio_writen(fd, buf, strlen(buf));
  333. }
  334. void html_error(int fd, char *msg)
  335. {
  336. char buf[MMAXLINE];
  337. sprintf(buf, "<html><title>Request Error</title>");
  338. Rio_writen(fd, buf, strlen(buf));
  339. sprintf(buf, "<body bgcolor="
  340. "ffffff"
  341. ">
  342. ");
  343. Rio_writen(fd, buf, strlen(buf));
  344. sprintf(buf, "%s
  345. ", msg);
  346. Rio_writen(fd, buf, strlen(buf));
  347. sprintf(buf, "<hr><em>The Tiny Proxy server</em>
  348. ");
  349. Rio_writen(fd, buf, strlen(buf));
  350. }
  351. void init_cache()
  352. {
  353. memset(cache.block_slot, 0, MAXCACHEBLOCK * sizeof(struct cache_block *));
  354. cache.clock_p = 0;
  355. cache.used_block = 0;
  356. cache.used_size = 0;
  357. read_cnt = 0;
  358. sem_init(&mutex, 0, 1);
  359. sem_init(&write_lock, 0, 1);
  360. }
  361. /*找到返回body_size,并且写header与body,找不到返回-1*/
  362. int read_cache(char *header, char *body, char *real_path)
  363. {
  364. P(&mutex);
  365. if (++read_cnt == 1)
  366. P(&write_lock);
  367. V(&mutex);
  368. int i, find = 0;
  369. for (i = 0; i < MAXCACHEBLOCK; i++)
  370. {
  371. if (cache.block_slot[i] != NULL && !strcmp(cache.block_slot[i]->path, real_path))
  372. {
  373. find = 1;
  374. break;
  375. }
  376. }
  377. if (find)
  378. {
  379. struct cache_block *block = cache.block_slot[i];
  380. sprintf(header, "%s", block->header);
  381. memcpy(body, block->body, block->body_size);
  382. block->visit = 1;
  383. find = block->body_size;
  384. }
  385. else
  386. find = -1;
  387. P(&mutex);
  388. if (--read_cnt == 0)
  389. V(&write_lock);
  390. V(&mutex);
  391. return find;
  392. }
  393. /*成功返回1,失败返回0*/
  394. int write_cache(char *header, char *body, int body_size, char *real_path)
  395. {
  396. P(&write_lock);
  397. int slot;
  398. struct cache_block *block;
  399. slot = next_slot(body_size);
  400. block = cache.block_slot[slot] = (struct cache_block *)malloc(sizeof(struct cache_block));
  401. sprintf((block->path), "%s", real_path);
  402. sprintf((block->header), "%s", header);
  403. block->visit = 1;
  404. block->body_size = body_size;
  405. cache.used_block++;
  406. cache.used_size += body_size;
  407. if ((block->body = (char *)malloc(body_size)) == NULL)
  408. {
  409. V(&write_lock);
  410. return 0;
  411. }
  412. memcpy(block->body, body, body_size);
  413. V(&write_lock);
  414. return 1;
  415. }
  416. /*write已经获得了写锁,所以next_slot与evict都不用再加锁*/
  417. int next_slot(int need_size)
  418. {
  419. while (cache.used_size + need_size > MAX_CACHE_SIZE || cache.used_block >= MAXCACHEBLOCK)
  420. evict();
  421. while (cache.block_slot[cache.clock_p] != NULL)
  422. cache.clock_p = (cache.clock_p + 1) % MAXCACHEBLOCK;
  423. return cache.clock_p;
  424. }
  425. void evict()
  426. {
  427. struct cache_block *block;
  428. while (1)
  429. {
  430. block = cache.block_slot[cache.clock_p];
  431. if (block == NULL)
  432. {
  433. cache.clock_p = (cache.clock_p + 1) % MAXCACHEBLOCK;
  434. continue;
  435. }
  436. if (block->visit)
  437. {
  438. block->visit = 0;
  439. }
  440. else
  441. {
  442. free(block->body);
  443. cache.used_size -= block->body_size;
  444. cache.used_block--;
  445. free(block);
  446. cache.block_slot[cache.clock_p] = NULL;
  447. return;
  448. }
  449. cache.clock_p = (cache.clock_p + 1) % MAXCACHEBLOCK;
  450. }
  451. }

源文件

proxy.c