http.server --- HTTP 服务器

源代码: Lib/http/server.py


这个模块定义了用于实现 HTTP 服务器的类。

警告

http.server 不推荐用于生产环境。它仅仅实现了 basic security checks 的要求。

Availability: not WASI.

此模块在 WebAssembly 平台上无效或不可用。 请参阅 WebAssembly 平台 了解详情。

HTTPServer 是 socketserver.TCPServer 的一个子类。它会创建和侦听 HTTP 套接字,并将请求分发给处理程序。创建和运行 HTTP 服务器的代码类似如下所示:

def run(server_class=HTTPServer, handler_class=BaseHTTPRequestHandler):
    server_address = ('', 8000)
    httpd = server_class(server_address, handler_class)
    httpd.serve_forever()
class http.server.HTTPServer(server_address, RequestHandlerClass)

该类在 TCPServer 类之上通过将服务器地址保存至名为 server_name 和 server_port 的实例变量来构建。 服务器可通过处理器访问,通常是通过处理器的 server 实例变量。

server_name

HTTP 服务器的完整限定域名。

server_port

从 server_address 获取的 HTTP 服务器端口号。

class http.server.ThreadingHTTPServer(server_address, RequestHandlerClass)

该类相似于 HTTPServer ,只是会利用 ThreadingMixIn 对请求进行多线程处理。当需要对 Web 浏览器预先打开套接字进行处理时,这就很有用,这时 HTTPServer 会一直等待请求。

Added in version 3.7.

实例化 HTTPServer 和 ThreadingHTTPServer 时,必须给出一个 RequestHandlerClass,本模块提供了该对象的三种变体:

class http.server.BaseHTTPRequestHandler(request, client_address, server)

这个类用于处理到达服务器的 HTTP 请求。它本身无法响应任何实际的 HTTP 请求;它必须被子类化以处理每个请求方法 (例如 'GET' 或 'POST')。 BaseHTTPRequestHandler 提供了多个供子类使用的类和实例变量以及方法。

这个处理器将解析请求和标头,然后调用特定请求类型对应的方法。 方法名称将根据请求来构造。 例如,对于请求方法 SPAM,将不带参数地调用 do_SPAM() 方法。 所有相关信息会被保存在该处理器的实例变量中。 子类不重写或扩展 __init__() 方法。

BaseHTTPRequestHandler 具有下列实例变量:

client_address

包含 (host, port) 形式的指向客户端地址的元组。

server

包含服务器实例。

close_connection

应当在 handle_one_request() 返回之前设定的布尔值,指明是否要期待另一个请求,还是应当关闭连接。

requestline

包含 HTTP 请求行的字符串表示。 末尾的 CRLF 会被去除。 该属性应当由 handle_one_request() 来设定。 如果无有效请求行被处理,则它应当被设为空字符串。

command

包含具体的命令(请求类型)。 例如 'GET'。

path

包含请求路径。 如果 URL 的查询部分存在,则 path 会包括这个查询部分。 如果使用 RFC 3986 的术语,这里的 path 包括 hier-part 和 query。

request_version

包含请求的版本字符串。 例如 'HTTP/1.0'。

headers

存放由 MessageClass 类变量所指定的类的实例。 该实例会解析并管理 HTTP 请求中的标头。 http.client 中的 parse_headers() 函数将被用来解析标头并且它需要 HTTP 请求提供有效的 RFC 5322 风格的标头。

rfile

一个 io.BufferedIOBase 输入流,准备从可选的输入数据的开头进行读取。

wfile

包含用于写入响应并发回给客户端的输出流。 在写入流时必须正确遵守 HTTP 协议以便成功地实现与 HTTP 客户端的互操作。

在 3.6 版本发生变更: 这是一个 io.BufferedIOBase 流。

BaseHTTPRequestHandler 具有下列属性:

server_version

指定服务器软件版本。 你可能会想要重写该属性。 该属性的格式为多个以空格分隔的字符串,其中每个字符串的形式为 name[/version]。 例如 'BaseHTTP/0.2'。

sys_version

包含 Python 系统版本,采用 version_string 方法和 server_version 类变量所支持的形式。 例如 'Python/1.4'。

error_message_format

指定应当被 send_error() 方法用来构建发给客户端的错误响应的格式字符串。 该字符串默认使用来自 responses 的变量根据传给 send_error() 的状态码来填充。

error_content_type

指定发送给客户端的错误响应的 Content-Type HTTP 标头。 默认值为 'text/html'。

protocol_version

指定服务器所符合的 HTTP 版本。 它会在响应中发送以便让客户端知道服务器对于未来请求的通信能力。 如果设置为 'HTTP/1.1',服务器将允许 HTTP 持久连接;但是,你的服务器 必须 在所有对客户端的响应中包括一个准确的 Content-Length 标头 (使用 send_header())。 为了保持向下兼容性,该设置默认为 'HTTP/1.0'。

MessageClass

指定一个 email.message.Message 这样的类来解析 HTTP 标头。 通常该属性不会被重写,其默认值为 http.client.HTTPMessage。

responses

该属性包含一个整数错误代码与由短消息和长消息组成的二元组的映射。 例如,{code: (shortmessage, longmessage)}。 shortmessage 通常是作为消息响应中的 message 键,而 longmessage 则是作为 explain 键。 该属性会被 send_response_only() 和 send_error() 方法所使用。

BaseHTTPRequestHandler 实例具有下列方法:

handle()

调用 handle_one_request() 一次(或者如果启用了永久连接则为多次)来处理传入的 HTTP 请求。 你应该永远不需要重写它;而是要实现适当的 do_*() 方法。

handle_one_request()

此方法将解析并请请求分配给适当的 do_*() 方法。 你应该永远不需要重写它。

handle_expect_100()

当一个符合 HTTP/1.1 标准的服务器接收到一个 Expect: 100-continue 请求标头时,它会以一个 100 Continue 加 200 OK 标头作为响应。 如果服务器不希望客户端继续,则可以重写该方法以引发错误。 例如,服务器可以选择发送 417 Expectation Failed 作为响应标头并 return False。

Added in version 3.2.

send_error(code, message=None, explain=None)

发送并记录回复给客户端的完整的错误信息。 数字形式的 code 指明 HTTP 错误代码,可选的 message 为简短的易于人类阅读的错误描述。 explain 参数可被用于提供更详细的错误信息;它将使用 error_message_format 属性来进行格式化并在一组完整的标头之后作为响应体被发送。 responses 属性保存了 message 和 explain 的默认值并将在未提供值时被使用;对于未知代码这两者的默认值均为字符串 ???。 如果方法为 HEAD 或响应代码为下列值一则响应体将为空: 1xx, 204 No Content, 205 Reset Content, 304 Not Modified。

在 3.4 版本发生变更: 错误响应包括一个 Content-Length 标头。 增加了 explain 参数。

send_response(code, message=None)

将一个响应标头添加到标头缓冲区并记录被接受的请求。 HTTP 响应行会被写入到内部缓冲区,后面是 Server 和 Date 标头。 这两个标头的值将分别通过 version_string() 和 date_time_string() 方法获取。 如果服务器不打算使用 send_header() 方法发送任何其他标头,则 send_response() 后面应该跟一个 end_headers() 调用。

在 3.3 版本发生变更: 标头会被存储到内部缓冲区并且需要显式地调用 end_headers()。

send_header(keyword, value)

将 HTTP 标头添加到内部缓冲区,它将在 end_headers() 或 flush_headers() 被唤起时写入输出流。 keyword 应当指定标头关键字,并以 value 指定其值。 请注意,在 send_header 调用结束之后,必须调用 end_headers() 以便完成操作。

此方法不会拒绝包含 CRLF 序列的输入。

在 3.2 版本发生变更: 标头将被存入内部缓冲区。

send_response_only(code, message=None)

仅发送响应标头,用于服务器向客户端发送 100 Continue 响应的情况。 标头不会被缓冲,而是直接发送到输出流。 如果未指定 message,则会发送与响应 code 相对应的 HTTP 消息。

此方法不会拒绝包含 CRLF 序列的 message。

Added in version 3.2.

end_headers()

将一个空行(指明响应中 HTTP 标头的结束) 添加到标头缓冲区并调用 flush_headers()。

在 3.2 版本发生变更: 已缓冲的标头会被写入到输出流。

flush_headers()

最终将标头发送到输出流并清空内部标头缓冲区。

Added in version 3.3.

log_request(code='-', size='-')

记录一次被接受(成功)的请求。 code 应当指定与请求相关联的 HTTP 代码。 如果请求的大小可用,则它应当作为 size 形参传入。

log_error(...)

当请求无法完成时记录一次错误。 默认情况下,它会将消息传给 log_message(),因此它接受同样的参数 (format 和一些额外的值)。

log_message(format, ...)

将任意一条消息记录到 sys.stderr。 此方法通常会被重写以创建自定义的错误日志记录机制。 format 参数是标准 printf 风格的格式字符串,其中传给 log_message() 的附加参数将用作格式化的输入。 每条记录的消息都会加上客户端 IP 地址以及当前日期和时间作为前缀。

version_string()

返回服务器软件的版本字符串。 该值为 server_version 与 sys_version 属性的组合。

date_time_string(timestamp=None)

返回由 timestamp 所给定的日期和时间(参数应为 None 或为 time.time() 所返回的格式),格式化为一个消息标头。 如果省略 timestamp,则会使用当前日期和时间。

结果看起来像 'Sun, 06 Nov 1994 08:49:37 GMT'。

log_date_time_string()

返回当前的日期和时间,为日志格式化

address_string()

返回客户端的地址

在 3.3 版本发生变更: 在之前版本中,会执行一次名称查找。 为了避免名称解析的时延,现在将总是返回 IP 地址。

class http.server.SimpleHTTPRequestHandler(request, client_address, server, directory=None)

这个类会为目录 directory 及以下的文件提供发布服务,或者如果未提供 directory 则为当前目录,直接将目录结构映射到 HTTP 请求。

在 3.7 版本发生变更: 增加了 directory 形参。

在 3.9 版本发生变更: directory 形参接受一个 path-like object。

诸如解析请求之类的大量工作都是由基类 BaseHTTPRequestHandler 完成的。本类实现了 do_GET() 和 do_HEAD() 函数。

以下是 SimpleHTTPRequestHandler 的类属性。

server_version

这会是 "SimpleHTTP/" + __version__,其中 __version__ 定义于模块级别。

index_pages

指定将会被当作目录索引页的文件名。

默认为 ("index.html", "index.htm")。

Added in version 3.12.

extensions_map

将后缀映射为 MIME 类型的字典,其中包含了覆盖系统默认值的自定义映射关系。不区分大小写,因此字典键只应为小写值。

在 3.9 版本发生变更: 此字典不再填充默认的系统映射,而只包含覆盖值。

SimpleHTTPRequestHandler 类定义了以下方法:

do_HEAD()

本方法为 'HEAD' 请求提供服务:它将发送等同于 GET 请求的头文件。关于合法头部信息的更完整解释,请参阅 do_GET() 方法。

do_GET()

通过将请求解释为相对于当前工作目录的路径,将请求映射到某个本地文件。

如果请求被映射到一个目录,则会按照 index_pages 的指定检查该目录中是否存在索引页面。 如果找到,则返回该文件的内容;否则会调用 list_directory() 方法生成目录列表。 该方法使用 os.listdir() 来扫描目录,如果 listdir() 失败,则返回 404 错误响应。

如果请求被映射到文件,则会打开该文件。 打开文件时的任何 OSError 异常都会被映射为 404, 'File not found' 错误。 如果请求中带有 'If-Modified-Since' 标头,而在此时间点之后文件未作修改,则会发送 304, 'Not Modified' 的响应。 否则会调用 guess_type() 方法猜测内容的类型,该方法会反过来用到 extensions_map 变量,并返回文件内容。

将会输出 'Content-type:' 头部信息,带上猜出的内容类型,然后是 'Content-Length:' 头部信息,带有文件的大小,以及 'Last-Modified:' 头部信息,带有文件的修改时间。

随后跟一个空行来指明标头的结束,再随后是输出文件内容。

示例用法请见在 Lib/http/server.py 中 test 函数的实现。

在 3.7 版本发生变更: 为 'If-Modified-Since' 头部信息提供支持。

list_directory(path)

当不存在索引页面时用于列出 path 内容的辅助函数。

这将返回一个 文件型对象 (必须由调用者关闭)或表示错误的 None,在后一种情况下调用者无需进一步操作。 在任一情况下,标头都会被发送。

guess_type(path)

推断给定 path 处的文件类型。

这将返回形如 type/subtype 的字符串,可用于 MIME Content-type 标头。

默认实现会在 extensions_map 中查找文件扩展名,回退到 mimetypes.guess_file_type(),然后回退到 'application/octet-stream'。

在 3.13 版本发生变更: 增加 mimetypes.guess_file_type() 作为回退项。

SimpleHTTPRequestHandler 类可用于像下面这样创建一个非常基本的 Web 服务器以当前目录为相对基准来发布文件:

import http.server
import socketserver

PORT = 8000

Handler = http.server.SimpleHTTPRequestHandler

with socketserver.TCPServer(("", PORT), Handler) as httpd:
    print("serving at port", PORT)
    httpd.serve_forever()

还可以对 SimpleHTTPRequestHandler 进行子类化以增强其行为,例如通过重写类属性 index_pages 来使用不同的索引文件名。

class http.server.CGIHTTPRequestHandler(request, client_address, server)

该类可为当前及以下目录中的文件或输出 CGI 脚本提供服务。注意,把 HTTP 分层结构映射到本地目录结构,这与 SimpleHTTPRequestHandler 完全一样。

备注

由 CGIHTTPRequestHandler 类运行的 CGI 脚本不能进行重定向操作(HTTP 代码302),因为在执行 CGI 脚本之前会发送代码 200(接下来就输出脚本)。这样状态码就冲突了。

然而,如果这个类猜测它是一个 CGI 脚本,那么就会运行该 CGI 脚本,而不是作为文件提供出去。 只会识别基于目录的 CGI —— 另有一种常用的服务器设置,即标识 CGI 脚本是通过特殊的扩展名。

如果请求指向 cgi_directories 路径之下的某个位置,则 do_GET() 和 do_HEAD() 函数已被修改为运行 CGI 脚本并提供输出,而不是提供文件。

CGIHTTPRequestHandler 定义了以下数据成员:

cgi_directories

默认为 ['/cgi-bin', '/htbin'],视作 CGI 脚本所在目录。

CGIHTTPRequestHandler 定义了以下方法:

do_POST()

本方法服务于 'POST' 请求,仅用于 CGI 脚本。如果试图向非 CGI 网址发送 POST 请求,则会输出错误 501:Can only POST to CGI scripts"。

请注意,为了保证安全性,CGI 脚本将以用户 nobody 的 UID 运行。CGI 脚本运行错误将被转换为错误 403。

从 3.13 版起不建议使用,将在 3.15 版中移除: CGIHTTPRequestHandler 将在 3.15 中移除。 早在十多年前 CGI 就已不被认为是一个好的解决方案。 目前此代码已长期未得到维护并且极少被实际使用。 保留它可能导致进一步的 安全考量。

命令行接口

http.server 也可使用解释器的 -m 开关来直接唤起。 下面的示例演示了如何提供相对于当前目录的文件:

python -m http.server [OPTIONS] [port]

可以接受以下选项:

port

服务器默认监听 8000 端口。 该默认值可以通过传入所需的端口号作为参数来覆盖:

python -m http.server 9000
-b, --bind <address>

指定一个它要绑定的特定地址。 同时支持 IPv4 和 IPv6 地址。 默认情况下,服务器会将自身绑定到所有接口。 例如,以下命令将使服务器仅绑定到 localhost:

python -m http.server --bind 127.0.0.1

Added in version 3.4.

在 3.8 版本发生变更: 在 --bind 选项中支持 IPv6。

-d, --directory <dir>

指定一个它要提供文件服务的目录。 默认情况下,服务器使用当前目录。 例如,以下命令使用特定目录:

python -m http.server --directory /tmp/

Added in version 3.7.

-p, --protocol <version>

指定服务器所采用的 HTTP 版本。 在默认情况下,服务器会采用 HTTP/1.0。 例如,以下命令将运行一个支持 HTTP/1.1 的服务器:

python -m http.server --protocol HTTP/1.1

Added in version 3.11.

--cgi

可以通过在命令行中传递 --cgi 选项来启用 CGIHTTPRequestHandler:

python -m http.server --cgi

从 3.13 版起不建议使用,将在 3.15 版中移除: http.server 命令行的 --cgi 支持已被移除因为 CGIHTTPRequestHandler 将要被移除。

警告

CGIHTTPRequestHandler 和 --cgi 命令行选项不适合不受信任的客户端使用且容易被恶意利用。 应当始终在安全的环境中使用。

安全考量

SimpleHTTPRequestHandler 在处理请求时会遵循符号链接,这使得提供指定目录之外的文件成为可能。

BaseHTTPRequestHandler.send_header() 和 BaseHTTPRequestHandler.send_response_only() 方法假定输入已清理,并且不执行输入验证(例如检查是否存在 CRLF 序列)。 不受信任的输入可能会导致 HTTP 标头注入攻击。

较早版本的 Python 不会擦除从 python -m http.server 或默认的 BaseHTTPRequestHandler .log_message 实现发送到 stderr 的日志消息中的控制字符。 这可以允许连接到你的服务器的远程客户端向你的终端发送邪恶的控制代码。

在 3.12 版本发生变更: 控制字符会在 stderr 日志中被擦除。