標籤:
今天學習了《Python核心編程》中Web伺服器相關章節。於是走讀了一下python的源碼。
本人電腦上安裝的是python2.6,相應的源碼檔案存放在 /usr/lib/python2.6/ 路徑下。
1 BaseHTTPServer淺析
開啟 /usr/lib/python2.6/BaseHTTPServer.py 檔案。
1.1 HTTPServer類
最上面定義了類 HTTPServer,繼承於 SocketServer.TCPServer,它不斷接收資料,並將接收到的資料交給 RequestHandler 處理。
它沒有在TCPServer的基礎上添加大量的功能,只加了一個server_bind()成員函數。
1.2 BaseHTTPRequestHandler類
看到類 BaseHTTPRequestHandler,這個類負責處理接收到的HTTP請求,如POST,GET之類的。
看到它的成員函數 handle()
請求處理就是調用handle()函數進行的。首先,它將類成員變數close_connection置為1,如果在handle_one_request()執行中沒有將其置為0,那麼handle()就返回了。
那在什麼情況下close_connection會被置為0呢?如果請求的header裡有 Connection:keep-alive時會被清0。見parse_request()中:
從上面的while迴圈可以看到,如果close_connection為0,那麼就繼續執行handle_one_request(),直到close_connection為1為至。
那麼 handle_one_request() 又在幹什麼呢?顧名思義,就是處理一個請求。
L312:從rfile讀取請求的資料,也就是HTTP報文資料。
L313~L315:如果讀失敗退出。
L316~L317:調用parse_request()對HTTP報文header進行解析,如果失敗則退出。
L318:根據command,產生處理函數名,如GET命令產生的是do_GET。
L319~L323:檢查當前類是否有 do_XXX() 成員函數,如果存在 do_XXX() 這個成員函數。
再看一下 parse_request() 是如何分析HTTP header的。主要分兩步:
(1)讀對報文資料的第一行,格式是:<命名> <路徑> <HTTP版本>,通常是:“GET / HTTP/1.1”。
分析版號是否正確,並解析出command, path, version,並儲存到對應的成員變數中。
(2)檢查headers是中的Connection,如果是keep-alive,那麼就得將close_connection置為0,以儲存串連。
從對BaseHTTPRequestHandler的分析可以得知,如果我們要響應POST,GET命令,那必須得繼承於BaseHTTPRequestHandler,並定義好do_GET()與do_POST()函數。
除了上述的三個重要的函數外,BaseHTTPRequestHandler 還提供了很多有用的成員函數:
send_error(code, message=None)
send_respond(code, message=None)
send_header(keyword, value)
end_headers()
...
2 CGIHTTPServer淺析
開啟 /usr/lib/python2.6/CGIHTTPServer.py 檔案。檔案裡只定義了一個 CGIHTTPRequestHandler 類,繼承於 SimpleHTTPServerHandler。
其實 SimpleHTTPRequestHandler 是繼承於 BaseHTTPRequestHandler 的。
它實現了 do_POST() 函數:
意思很簡單,如果是CGI,那麼就執行CGI,否則報錯。
那怎麼才算是CGI呢?我們跟蹤一下 is_cgi() 函數:
看起來很簡單,也就是在目錄 cgi_directories 下的檔案,認為是cgi檔案。在L89定義了 cgi_directories,也就是在 /cgi-bin 或 /htbin 目錄下的都認為是 cgi。
_url_collapse_path_split(path) 函數是用於規整路徑的,防止路徑中出現過多 ./ 或 .. / 出現的防問漏洞。
那麼怎麼執行cgi的呢?我們一起跟一下 run_cgi() 函數。
待續……
BaseHTTPServer與CGIHTTPServer學習劄記