1. 程式人生 > 其它 >[lab] csapp-proxy

[lab] csapp-proxy

proxy lab

這個lab的解決方案還是相對直觀的. 要求實現一個帶記憶體快取功能的http代理.

功能可以拆解為

  1. server
  2. client
  3. 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)
}