[lab] csapp-proxy
proxy lab
这个lab的解决方案还是相对直观的. 要求实现一个带内存缓存功能的http代理.
功能可以拆解为
- server
- client
- cache
一开始还有些疑惑, proxy server 只接受一个端口参数, 那如何知道转发给谁呢? 看到测试脚本使用 curl 才明白, 经过 --proxy 参数后, 虽然请求的目的地(包括第一行的uri 与 Host 头信息)还是 http server s, 但请求是发给 proxy server p 的.
大体的请求流程如下
没有proxy
c <--> s
proxy
逻辑上请求
c ------- s
\ /
\ /
\ /
p
并且虽然 lab 要求中需要处理很多error case, 但实际给出 test case 并没有覆盖到, 再加上附带 tiny server 的例子, 如果只是追求过lab, 直接使用 csapp 里的 rio 等辅助函数就好.
proxy server
这里涉及到网络编程相关知识, 简要给出阶段1的骨干代码.
int main() {
listen = Open_listenfd();
// 循环接受请求
while (client = accept(listen)) {
handle_request(client);
}
}
// 处理一次请求
int handle_request(client) {
rio_t rio;
http_header_map header_map;
// 读取第一行
read_line(client, buf);
// method uri version
// 解析 http 头
header_map = read_headers(client);
// 根据要求, 对一些 http header 进行修改
rewrite_headers(header_map);
// 打开请求的tcp client
req_client = open_client(host);
// 向其发送 http 头
send_header(req_client, header_map);
// 对两个 socket 的数据进行复制.
proxy_content(req_client, client);
}
这里我使用了自定义类型 http_header_map 用来吧所有 header 存起来统一处理, 也可以直接打开 http client, 处理完在线发送.
如果是存起来要注意 字符串的生命周期问题, 因为数据是存在本地buf中, 后面再读取buf就被覆盖了, 因此我们要malloc出来, 再拷贝.
其次是 uri 的解析, 虽然逻辑直观, c语言写起来还是挺麻烦的.
多线程
多线程要注意的问题还是 buf 的问题, 避免使用全局缓冲区, 使用函数本地缓存或与请求fd绑定就可以.
pthread_t thread;
Pthread_create(&thread, NULL, (void *(*)(void *))handle_request,
(void *)connfd);
Pthread_detach(thread);
cache
cache 部分不要求我们完全按照http标准来控制, 算是简化了许多.
首先实现一个支持 lru的 cache struct, lru 其实已经在 cache lib 中实现过了, 这里不再赘述, 添加上缓存大小检查的代码即可.
然后是多线程访问 cache, 这里也使用最简单的 pthread_rwlock_t 来控制, 在 set 里加写锁, get 里先加读锁, 后加写锁更新访问时间.
最后是应用 cache, 逻辑如下
handle_request() {
// 解析请求
// 如果存在 cache, 就直接使用 cache 来响应.
if (cache_item = cache_hit(uri)) {
cache_serve(cache_item)
return
}
// 否则创建新的 cache item 来保存结果,
cache_item = create_cache_item();
client_serve(cache_item)
// 写入cache中.
cache_set(cache_item)
}