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

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

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

这个模块定义了实现 HTTP 服务器（ Web 服务器）的类。

警告:

  不推荐在生产环境中使用 "http.server" 。它只实现了基本的安全检查功能
  。

"HTTPServer" 是 "socketserver.TCPServer" 的一个子类。它会创建和侦听
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" 实例变
   量访问。

class http.server.ThreadingHTTPServer(server_address, RequestHandlerClass)

   这个类与 HTTPServer 相似，但会通过使用 "ThreadingMixIn" 来以线程处
   理请求。 这适用于处理 Web 浏览器预开启套接字，以便 "HTTPServer" 无
   限期地执行等待。

   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 2822**
      风格的标头。

   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()

      When a HTTP/1.1 compliant server receives an "Expect:
      100-continue" request header it responds back with a "100
      Continue" followed by "200 OK" headers. This method can be
      overridden to raise an error if the server does not want the
      client to continue.  For e.g. server can chose to send "417
      Expectation Failed" as a response header and "return False".

      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()" 以便完成操作。

      3.2 版更變: 标头将被存入内部缓冲区。

   send_response_only(code, message=None)

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

      3.2 版新加入.

   end_headers()

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

      3.2 版更變: 已缓冲的标头会被写入到输出流。

   flush_headers()

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

      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)

   这个类为当前目录及以下的文件提供服务，直接映射目录结构到HTTP请求

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

   以下是 "SimpleHTTPRequestHandler" 的类属性。

   server_version

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

   extensions_map

      A dictionary mapping suffixes into MIME types. The default is
      signified by an empty string, and is considered to be
      "application/octet-stream". The mapping is used case-
      insensitively, and so should contain only lower-cased keys.

   directory

      如果没有指定，要服务 目录是当前工作目录

   "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:'" 头部信息，带有文件的修改时间。

      后面是一个空行，标志着头部信息的结束，然后输出文件的内容。如果文
      件的 MIME 类型以 "text/" 开头，文件将以文本模式打开；否则将使用
      二进制模式。

      用法示例请参阅 "http.server" 模块中的 "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()

"http.server" 也可使用解释器的 "-m" 开关加上 "port number" 参数直接发
起调用。 与前面的例子类似，可支持相对于当前目录的文件:

   python -m http.server 8000

默认情况下，HTTP 服务器将绑定所有的网络接口。参数 "-b/--bind" 可指定需
绑定的地址。IPv4 和 IPv6 均可。比如以下命令可让 HTTP 服务器只与
localhost 绑定：

   python -m http.server 8000 --bind 127.0.0.1

3.4 版新加入: 引入了 "--bind" 参数。

3.8 版新加入: 为了支持 IPv6 改进了 "--bind" 参数。

HTTP 服务器默认使用当前目录。参数 "-d/--directory" 可指定文件所在的目
录。比如以下命令就指定了一个目录：

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

3.7 版新加入: "--directory" 指定备用目录

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。

通过在命令行传入 "--cgi" 参数，可以启用  "CGIHTTPRequestHandler" ：

   python -m http.server --cgi 8000
