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_set
try:
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_keys
or environ["REQUEST_METHOD"] == "HEAD"
or code < 200
or code in (204, 304)
):
self.close_connection = True
self.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 = None
elif 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 = None
try:
execute(self.server.app)
except (_ConnectionError, socket.timeout) as e:
self.connection_dropped(e, environ)
except Exception:
if self.server.passthrough_errors:
raise
from .debug.tbtools import get_current_traceback
traceback = get_current_traceback(ignore_system_exceptions=True)
try:
if not headers_sent:
del headers_set[:]
execute(InternalServerError())
except Exception:
pass
self.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_set
try:
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_keys
or environ["REQUEST_METHOD"] == "HEAD"
or code < 200
or code in (204, 304)
):
self.close_connection = True
self.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 = None
elif 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:
raise
from .debug.tbtools import get_current_traceback
traceback = get_current_traceback(ignore_system_exceptions=True)
try:
if not headers_sent:
del headers_set[:]
execute(InternalServerError())
except Exception:
pass
self.server.log("error", "Error on request:\n%s", traceback.plaintext)
这部分代码处理各种异常。如果发生连接错误或超时,会调用 connection_dropped
方法处理。如果发生其他异常且 passthrough_errors
未启用,会记录错误并返回 InternalServerError
响应。
总结
通过使用 socketserver
模块和 WSGI 规范,可以实现一个简单但功能强大的网络服务器。本文中的代码展示了如何处理请求头、创建 WSGI 环境、发送响应数据、处理错误等。希望通过这篇教程,你能够更好地理解和使用 socketserver
模块来开发自己的网络服务器应用。更多详细信息和示例请参考官方文档。