Python socketserver
和 WSGI 服務端實現教程
在本文中,我們將詳細解析一個使用 socketserver
模塊實現的簡單 WSGI 服務器。該服務器能夠處理 HTTP 請求,支持 WSGI 應用,并正確處理響應頭和錯誤。
代碼概述
這段代碼定義了一個 run_wsgi
方法,用于處理 HTTP 請求并運行 WSGI 應用。它實現了以下功能:
- 處理
Expect: 100-continue
請求頭。 - 創建 WSGI 環境字典。
- 定義
write
和start_response
函數來處理 WSGI 響應。 - 執行 WSGI 應用,并將響應發送回客戶端。
- 處理連接錯誤和超時。
- 在發生異常時記錄錯誤,并返回
InternalServerError
響應。
代碼詳細解析
def run_wsgi(self):# 處理 'Expect: 100-continue' 請求頭if self.headers.get("Expect", "").lower().strip() == "100-continue":self.wfile.write(b"HTTP/1.1 100 Continue\r\n\r\n")# 創建 WSGI 環境字典self.environ = environ = self.make_environ()headers_set = []headers_sent = []# 定義 write 函數,用于發送響應數據def write(data):assert headers_set, "write() before start_response"if not headers_sent:status, response_headers = headers_sent[:] = headers_settry:code, msg = status.split(None, 1)except ValueError:code, msg = status, ""code = int(code)self.send_response(code, msg)header_keys = set()for key, value in response_headers:self.send_header(key, value)key = key.lower()header_keys.add(key)if not ("content-length" in header_keysor environ["REQUEST_METHOD"] == "HEAD"or code < 200or code in (204, 304)):self.close_connection = Trueself.send_header("Connection", "close")if "server" not in header_keys:self.send_header("Server", self.version_string())if "date" not in header_keys:self.send_header("Date", self.date_time_string())self.end_headers()assert isinstance(data, bytes), "applications must write bytes"self.wfile.write(data)self.wfile.flush()# 定義 start_response 函數,用于設置響應頭def start_response(status, response_headers, exc_info=None):if exc_info:try:if headers_sent:reraise(*exc_info)finally:exc_info = Noneelif headers_set:raise AssertionError("Headers already set")headers_set[:] = [status, response_headers]return write# 執行 WSGI 應用def execute(app):application_iter = app(environ, start_response)try:for data in application_iter:write(data)if not headers_sent:write(b"")finally:if hasattr(application_iter, "close"):application_iter.close()application_iter = Nonetry:execute(self.server.app)except (_ConnectionError, socket.timeout) as e:self.connection_dropped(e, environ)except Exception:if self.server.passthrough_errors:raisefrom .debug.tbtools import get_current_tracebacktraceback = get_current_traceback(ignore_system_exceptions=True)try:if not headers_sent:del headers_set[:]execute(InternalServerError())except Exception:passself.server.log("error", "Error on request:\n%s", traceback.plaintext)
分步驟講解
- 處理
Expect: 100-continue
請求頭
if self.headers.get("Expect", "").lower().strip() == "100-continue":self.wfile.write(b"HTTP/1.1 100 Continue\r\n\r\n")
當客戶端發送帶有 Expect: 100-continue
請求頭的請求時,服務器應先響應 100 Continue
,然后再繼續處理請求。這段代碼處理了這一邏輯。
- 創建 WSGI 環境字典
self.environ = environ = self.make_environ()
這行代碼調用 make_environ
方法,創建一個 WSGI 環境字典 environ
,包含了請求的所有必要信息,如請求方法、路徑、頭信息等。
- 定義
write
函數
def write(data):assert headers_set, "write() before start_response"if not headers_sent:status, response_headers = headers_sent[:] = headers_settry:code, msg = status.split(None, 1)except ValueError:code, msg = status, ""code = int(code)self.send_response(code, msg)header_keys = set()for key, value in response_headers:self.send_header(key, value)key = key.lower()header_keys.add(key)if not ("content-length" in header_keysor environ["REQUEST_METHOD"] == "HEAD"or code < 200or code in (204, 304)):self.close_connection = Trueself.send_header("Connection", "close")if "server" not in header_keys:self.send_header("Server", self.version_string())if "date" not in header_keys:self.send_header("Date", self.date_time_string())self.end_headers()assert isinstance(data, bytes), "applications must write bytes"self.wfile.write(data)self.wfile.flush()
write
函數負責發送響應數據。在首次調用時,它還會發送響應頭。它確保在 start_response
被調用后才允許發送數據,并確保數據是字節類型。
- 定義
start_response
函數
def start_response(status, response_headers, exc_info=None):if exc_info:try:if headers_sent:reraise(*exc_info)finally:exc_info = Noneelif headers_set:raise AssertionError("Headers already set")headers_set[:] = [status, response_headers]return write
start_response
函數用于設置響應頭。它會在 WSGI 應用中被調用,接受狀態碼和響應頭列表。如果發生異常且響應頭已經發送,它會重新引發異常。
- 執行 WSGI 應用
def execute(app):application_iter = app(environ, start_response)try:for data in application_iter:write(data)if not headers_sent:write(b"")finally:if hasattr(application_iter, "close"):application_iter.close()application_iter = None
execute
函數執行 WSGI 應用,并迭代應用的輸出。它調用 write
函數發送響應數據,并在最后關閉應用迭代器。
- 錯誤處理
try:execute(self.server.app)
except (_ConnectionError, socket.timeout) as e:self.connection_dropped(e, environ)
except Exception:if self.server.passthrough_errors:raisefrom .debug.tbtools import get_current_tracebacktraceback = get_current_traceback(ignore_system_exceptions=True)try:if not headers_sent:del headers_set[:]execute(InternalServerError())except Exception:passself.server.log("error", "Error on request:\n%s", traceback.plaintext)
這部分代碼處理各種異常。如果發生連接錯誤或超時,會調用 connection_dropped
方法處理。如果發生其他異常且 passthrough_errors
未啟用,會記錄錯誤并返回 InternalServerError
響應。
總結
通過使用 socketserver
模塊和 WSGI 規范,可以實現一個簡單但功能強大的網絡服務器。本文中的代碼展示了如何處理請求頭、創建 WSGI 環境、發送響應數據、處理錯誤等。希望通過這篇教程,你能夠更好地理解和使用 socketserver
模塊來開發自己的網絡服務器應用。更多詳細信息和示例請參考官方文檔。