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
模块来开发自己的网络服务器应用。更多详细信息和示例请参考官方文档。