"http.server" --- HTTP 服务器
*****************************

**源代码：** Lib/http/server.py

======================================================================

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

警告:

  "http.server" is not recommended for production. It only implements
  basic security checks.

适用范围: 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" 中保存 HTTP 服务器地址。处理程序可通过实例变量
   "server" 访问 HTTP 服务器。

class http.server.ThreadingHTTPServer(server_address, RequestHandlerClass)

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

   Added in version 3.7.

class http.server.HTTPSServer(server_address, RequestHandlerClass, bind_and_activate=True, *, certfile, keyfile=None, password=None, alpn_protocols=None)

   使用 "ssl" 模块的封装套接字的 "HTTPServer" 子类。如果 "ssl" 模块不
   可用，实例化 "HTTPSServer" 对象将失败，并返回 "RuntimeError"。

   *certfile* 参数是 SSL 证书链文件的路径，*keyfile* 是包含私钥的文件
   的路径。

   可以为受PKCS#8保护和包装的文件指定 *password* ，但要注意，这可能会
   以明文形式暴露硬编码的密码。

   参见:

     参见 "ssl.SSLContext.load_cert_chain()" 获取有关 *certfile*、
     *keyfile* 和 *password* 的可接受值的更多信息。

   当指定时，*alpn_protocols* 参数必须是指定服务器支持的“应用层协议协
   商”（ALPN）协议的字符串序列。ALPN 允许服务器和客户端在 TLS 握手期间
   协商应用协议。

   ]``，表示服务器支持 HTTP/1.1。

   Added in version 3.14.

class http.server.ThreadingHTTPSServer(server_address, RequestHandlerClass, bind_and_activate=True, *, certfile, keyfile=None, password=None, alpn_protocols=None)

   此类与 "HTTPSServer" 相同，但通过继承 "ThreadingMixIn" 使用线程来处
   理请求。这类似于仅使用 "HTTPSServer" 的 "ThreadingHTTPServer"。

   Added in version 3.14.

实例化 "HTTPServer"、 "ThreadingHTTPServer"、"HTTPSServer" 和
"ThreadingHTTPSServer" 时，必须给出一个 *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 或响应代码为下列值之一则响应体将为
      空："1*xx*", "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()" 以便完成操作。

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

   send_response_only(code, message=None)

      只发送响应标头，用于当 "100 Continue" 响应被服务器发送给客户端的
      场合。标头不会被缓冲而是直接发送到输出流。如果未指定 *message*，
      则会发送与响应 *code*  相对应的 HTTP 消息。

      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__" 定义于
      模块级别。

   extensions_map

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

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

   "SimpleHTTPRequestHandler" 类定义了以下方法：

   do_HEAD()

      本方法为 "'HEAD'" 请求提供服务：它将发送等同于 "GET" 请求的标头
      。关于可能的标头的更完整解释，请参阅 "do_GET()" 方法。

   do_GET()

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

      如果请求被映射到目录，则会依次检查该目录是否存在 "index.html" 或
      "index.htm" 文件。若存在则返回文件内容；否则会调用
      "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'" 头部信息提供支持。

"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" 以使用不同的 index 文件名。

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" can also be invoked directly using the "-m" switch of
the interpreter.  The following example illustrates how to serve files
relative to the current directory:

   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" command line
   "--cgi" support is being removed because "CGIHTTPRequestHandler" is
   being removed.

警告:

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

--tls-cert

   为 HTTPS 连接指定一个 TLS 证书链:

      python -m http.server --tls-cert fullchain.pem

   Added in version 3.14.

--tls-key

   为 HTTPS 连接指定一个私有密钥文件。

   该选项要求已指定 "--tls-cert"。

   Added in version 3.14.

--tls-password-file

   为密码保护的私有密钥指定密码文件:

      python -m http.server \
             --tls-cert cert.pem \
             --tls-key key.pem \
             --tls-password-file password.txt

   该选项要求已指定 *--tls-cert`*。

   Added in version 3.14.


安全考量
========

当处理请求时，"SimpleHTTPRequestHandler" 会解析符号链接，这有可能使得
指定文件夹以外的文件被暴露。

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

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