# `http.client` --- HTTP 协议客户端

源代码： Lib/http/client.py

这个模块定义了实现 HTTP 和 HTTPS 协议客户端的类。 它通常不直接使用 --- 模块 `urllib.request` 用它来处理使用 HTTP 和 HTTPS 的 URL。

也參考

对于更高级别的 HTTP 客户端接口，建议使用 Requests 。

備註

HTTPS 支持仅在编译 Python 时启用了 SSL 支持的情况下（通过 `ssl` 模块）可用。

该模块支持以下类：

class `http.client.``HTTPConnection`(host, port=None, [timeout, ]source_address=None, blocksize=8192)

  `HTTPConnection` 的实例代表与 HTTP 的一个连接事务。 它的实例化应当传入一个主机和可选的端口号。 如果没有传入端口号，如果主机字符串的形式为 `主机:端口` 则会从中提取端口，否则将使用默认的 HTTP 端口（80）。 如果给出了可选的 timeout 参数，则阻塞操作（例如连接尝试）将在指定的秒数之后超时（如果未给出，则使用全局默认超时设置）。 可选的 source_address 参数可以为一个 (主机, 端口) 元组，用作进行 HTTP 连接的源地址。 可选的 blocksize 参数可以字节为单位设置缓冲区的大小，用来发送文件类消息体。

  举个例子，以下调用都是创建连接到同一主机和端口的服务器的实例：

      >>> h1 = http.client.HTTPConnection('www.python.org')
      >>> h2 = http.client.HTTPConnection('www.python.org:80')
      >>> h3 = http.client.HTTPConnection('www.python.org', 80)
      >>> h4 = http.client.HTTPConnection('www.python.org', 80, timeout=10)

  3.2 版更變: 添加了*source_address* 参数

  3.4 版更變: 删除了 strict 参数，不再支持 HTTP 0.9 风格的“简单响应”。

  3.7 版更變: 添加了 blocksize 参数。

class `http.client.``HTTPSConnection`(host, port=None, key_file=None, cert_file=None, [timeout, ]source_address=None, *, context=None, check_hostname=None, blocksize=8192)

  `HTTPConnection` 的子类，使用 SSL 与安全服务器进行通信。 默认端口为 `443`。 如果指定了 context，它必须为一个描述 SSL 各选项的 `ssl.SSLContext` 实例。

  请参阅 安全考量 了解有关最佳实践的更多信息。

  3.2 版更變: 添加了 source_address, context 和 check_hostname。

  3.2 版更變: 这个类目前会在可能的情况下（即如果 `ssl.HAS_SNI` 为真值）支持 HTTPS 虚拟主机。

  3.4 版更變: 删除了 strict 参数，不再支持 HTTP 0.9 风格的“简单响应”。

  3.4.3 版更變: 目前这个类在默认情况下会执行所有必要的证书和主机检查。 要回复到先前的非验证行为，可以将 `ssl._create_unverified_context()` 传递给 context 参数。

  3.8 版更變: 该类现在对于默认的 context 或在传入 cert_file 并附带自定义 context 时会启用 TLS 1.3 `ssl.SSLContext.post_handshake_auth`。

  3.6 版後已棄用: key_file 和 cert_file 已弃用并转而推荐 context。 请改用 `ssl.SSLContext.load_cert_chain()` 或让 `ssl.create_default_context()` 为你选择系统所信任的 CA 证书。

  check_hostname 参数也已弃用；应当改用 context 的 `ssl.SSLContext.check_hostname` 属性。

class `http.client.``HTTPResponse`(sock, debuglevel=0, method=None, url=None)

  在成功连接后返回类的实例，而不是由用户直接实例化。

  3.4 版更變: 删除了 strict 参数，不再支持HTTP 0.9 风格的“简单响应”。

这个模块定义了以下函数：

`http.client.``parse_headers`(fp)

  从文件指针 fp 中解析头部信息，该文件代表 HTTP 请求/响应。该文件必须是 `BufferedIOBase` reader 对象（即不是文本），并且必须提供符合 RFC 2822 格式的头部。

  该函数返回 `http.client.HTTPMessage` 的实例，带有头部各个字段，但不带正文数据（与 `HTTPResponse.msg` 和 `http.server.BaseHTTPRequestHandler.headers` 一样）。返回之后，文件指针 fp 已为读取 HTTP 正文做好准备了。

  備註

  `parse_headers()` 不会解析 HTTP 消息的开始行；只会解析各 `Name: value` 行。文件必须为读取这些字段做好准备，所以在调用该函数之前，第一行应该已经被读取过了。

下列异常可以适当地被引发:

exception `http.client.``HTTPException`

  此模块中其他异常的基类。 它是 `Exception` 的一个子类。

exception `http.client.``NotConnected`

  `HTTPException` 的一个子类。

exception `http.client.``InvalidURL`

  `HTTPException` 的一个子类，如果给出了一个非数字或为空值的端口就会被引发。

exception `http.client.``UnknownProtocol`

  `HTTPException` 的一个子类。

exception `http.client.``UnknownTransferEncoding`

  `HTTPException` 的一个子类。

exception `http.client.``UnimplementedFileMode`

  `HTTPException` 的一个子类。

exception `http.client.``IncompleteRead`

  `HTTPException` 的一个子类。

exception `http.client.``ImproperConnectionState`

  `HTTPException` 的一个子类。

exception `http.client.``CannotSendRequest`

  `ImproperConnectionState` 的一个子类。

exception `http.client.``CannotSendHeader`

  `ImproperConnectionState` 的一个子类。

exception `http.client.``ResponseNotReady`

  `ImproperConnectionState` 的一个子类。

exception `http.client.``BadStatusLine`

  `HTTPException` 的一个子类。 如果服务器反馈了一个我们不理解的 HTTP 状态码就会被引发。

exception `http.client.``LineTooLong`

  `HTTPException` 的一个子类。 如果在 HTTP 协议中从服务器接收到过长的行就会被引发。

exception `http.client.``RemoteDisconnected`

  `ConnectionResetError` 和 `BadStatusLine` 的一个子类。 当尝试读取响应时的结果是未从连接读取到数据时由 `HTTPConnection.getresponse()` 引发，表明远端已关闭连接。

  3.5 版新加入: 在此之前引发的异常为 `BadStatusLine``('')`。

此模块中定义的常量为：

`http.client.``HTTP_PORT`

  HTTP 协议默认的端口号 (总是 `80`)。

`http.client.``HTTPS_PORT`

  HTTPS 协议默认的端口号 (总是 `443`)。

`http.client.``responses`

  这个字典把 HTTP 1.1 状态码映射到 W3C 名称。

  例如：`http.client.responses[http.client.NOT_FOUND]` 是 `'NOT FOUND` （未发现）。

本模块中可用的 HTTP 状态码常量可以参见 HTTP 状态码 。

## HTTPConnection 对象

`HTTPConnection` 实例拥有以下方法：

`HTTPConnection.``request`(method, url, body=None, headers={}, *, encode_chunked=False)

  这会使用 HTTP 请求方法 method 和选择器 url 向服务器发送请求。

  如果给定 body，那么给定的数据会在信息头完成之后发送。它可能是一个 `str` 、一个 bytes-like object 、一个打开的 file object，或者 `bytes` 迭代器。如果 body 是字符串，它会按 HTTP 默认的 ISO-8859-1 编码；如果是一个字节类对象，它会按原样发送；如果是 file object ，文件的内容会被发送，这个文件对象应该支持 `read()` 方法。如果这个文件对象是一个 `io.TextIOBase` 实例， `read()` 方法返回的数据会按 ISO-8859-1 编码，否则 `read()` 方法返回的数据会按原样发送；如果 body 是一个迭代器，迭代器中的元素会被发送，直到迭代器耗尽。

  headers 参数应是额外的随请求发送的 HTTP 信息头的字典。

  如果 headers 既不包含 Content-Length 也没有 Transfer-Encoding，但存在请求正文，那么这些头字段中的一个会自动设定。如果 body 是 `None`，那么对于要求正文的方法 (`PUT`，`POST`，和 `PATCH`)，Content-Length 头会被设为 `0`。如果 body 是字符串或者类似字节的对象，并且也不是 文件，Content-Length 头会设为正文的长度。任何其他类型的 body （一般是文件或迭代器）会按块编码，这时会自动设定 Transfer-Encoding 头以代替 Content-Length。

  在 headers 中指定 Transfer-Encoding 时， encode_chunked 是唯一相关的参数。如果 encode_chunked 为 `False`，HTTPConnection 对象会假定所有的编码都由调用代码处理。如果为 `True`，正文会按块编码。

  備註

  HTTP 协议在 1.1 版中添加了块传输编码。除非明确知道 HTTP 服务器可以处理 HTTP 1.1，调用者要么必须指定 Content-Length，要么必须传入 `str` 或字节类对象，注意该对象不能是表达 body 的文件。

  3.2 版新加入: body 现在可以是可迭代对象了。

  3.6 版更變: 如果 Content-Length 和 Transfer-Encoding 都没有在 headers 中设置，文件和可迭代的 body 对象现在会按块编码。添加了 encode_chunked 参数。不会尝试去确定文件对象的 Content-Length。

`HTTPConnection.``getresponse`()

  应当在发送一个请求从服务器获取响应时被调用。 返回一个 `HTTPResponse` 的实例。

  備註

  请注意你必须在读取了整个响应之后才能向服务器发送新的请求。

  3.5 版更變: 如果引发了 `ConnectionError` 或其子类， `HTTPConnection` 对象将在发送新的请求时准备好重新连接。

`HTTPConnection.``set_debuglevel`(level)

  设置调试等级。 默认的调试等级为 `0`，意味着不会打印调试输出。 任何大于 `0` 的值将使得所有当前定义的调试输出被打印到 stdout。 `debuglevel` 会被传给任何新创建的 `HTTPResponse` 对象。

  3.1 版新加入.

`HTTPConnection.``set_tunnel`(host, port=None, headers=None)

  为 HTTP 连接隧道设置主机和端口。 这将允许通过代理服务器运行连接。

  host 和 port 参数指明隧道连接的位置（即 CONNECT 请求所包含的地址，而 不是 代理服务器的地址）。

  headers 参数应为一个随 CONNECT 请求发送的额外 HTTP 标头的映射。

  例如，要通过一个运行于本机 8080 端口的 HTTPS 代理服务器隧道，我们应当向 `HTTPSConnection` 构造器传入代理的地址，并将我们最终想要访问的主机地址传给 `set_tunnel()` 方法:

      >>> import http.client
      >>> conn = http.client.HTTPSConnection("localhost", 8080)
      >>> conn.set_tunnel("www.python.org")
      >>> conn.request("HEAD","/index.html")

  3.2 版新加入.

`HTTPConnection.``connect`()

  当对象被创建后连接到指定的服务器。 默认情况下，如果客户端还未建立连接，此函数会在发送请求时自动被调用。

`HTTPConnection.``close`()

  关闭到服务器的连接。

`HTTPConnection.``blocksize`

  用于发送文件类消息体的缓冲区大小。

  3.7 版新加入.

作为对使用上述 `request()` 方法的替代同，你也可以通过使用下面的四个函数，分步骤发送请的请求。

`HTTPConnection.``putrequest`(method, url, skip_host=False, skip_accept_encoding=False)

  应为连接服务器之后首先调用的函数。将向服务器发送一行数据，包含 method 字符串、url 字符串和 HTTP 版本（`HTTP/1.1`）。若要禁止自动发送 `Host:` 或 `Accept-Encoding:` 头部信息（比如需要接受其他编码格式的内容），请将 skip_host 或 skip_accept_encoding 设为非 False 值。

`HTTPConnection.``putheader`(header, argument[, ...])

  向服务器发送一个 RFC 822 格式的头部。将向服务器发送一行由头、冒号和空格以及第一个参数组成的数据。 如果还给出了其他参数，将在后续行中发送，每行由一个制表符和一个参数组成。

`HTTPConnection.``endheaders`(message_body=None, *, encode_chunked=False)

  向服务器发送一个空行，表示头部文件结束。可选的 message_body 参数可用于传入一个与请求相关的消息体。

  如果 encode_chunked 为 `True`，则对 message_body 的每次迭代结果将依照 RFC 7230 3.3.1 节的规范进行分块编码。数据如何编码取决于 message_body 的类型。 如果 message_body 实现了 buffer 接口，编码将生成一个数据块。如果 message_body 是 `collections.abc.Iterable`，则 message_body 的每次迭代都会产生一个块。 如果 message_body 为 file object，那么每次调用 `.read()` 都会产生一个数据块。在 message_body 结束后，本方法立即会自动标记块编码数据的结束。

  備註

  由于分块编码的规范要求，迭代器本身产生的空块将被分块编码器忽略。这是为了避免目标服务器因错误编码而过早终止对请求的读取。

  3.6 版新加入: 支持分块编码。加入了 encode_chunked 参数。

`HTTPConnection.``send`(data)

  发送数据到服务器。本函数只应在调用 `endheaders()` 方法之后且调用 `getresponse()` 之前直接调用。

## HTTPResponse 对象

`HTTPResponse` 实例封装了来自服务器的 HTTP 响应。通过它可以访问请求头和响应体。响应是可迭代对象，可在 with 语句中使用。

3.5 版更變: 现在已实现了 `io.BufferedIOBase` 接口，并且支持所有的读取操作。

`HTTPResponse.``read`([amt])

  读取并返回响应体，或后续 amt 个字节。

`HTTPResponse.``readinto`(b)

  读取响应体的后续 len(b) 个字节到缓冲区 b。返回读取的字节数。

  3.3 版新加入.

`HTTPResponse.``getheader`(name, default=None)

  返回头部信息中的 name 值，如果没有与 name 匹配的字段，则返回*default*。如果名为 name 的字段不止一个，则返回所有字段，中间用 ', ' 连接。 如果 'default' 不是单个字符串，而是其他可迭代对象，则其元素同样以逗号连接并返回。

`HTTPResponse.``getheaders`()

  返回 (header, value) 元组构成的列表。

`HTTPResponse.``fileno`()

  返回底层套接字的 `fileno`。

`HTTPResponse.``msg`

  包含响应头的 `http.client.HTTPMessage` 实例。`http.client.HTTPMessage` 是 `email.message` 的子类。

`HTTPResponse.``version`

  服务器使用的 HTTP 协议版本。10 代表 HTTP/1.0，11 代表 HTTP/1.1。

`HTTPResponse.``status`

  由服务器返回的状态码。

`HTTPResponse.``reason`

  服务器返回的原因短语。

`HTTPResponse.``debuglevel`

  一个调试钩子。如果 `debuglevel` 大于零，状态信息将在读取和解析响应数据时打印输出到 stdout。

`HTTPResponse.``closed`

  如果流被关闭，则为``True``。

## 例子

下面是使用 `GET` 方法的会话示例：

    >>> import http.client
    >>> conn = http.client.HTTPSConnection("www.python.org")
    >>> conn.request("GET", "/")
    >>> r1 = conn.getresponse()
    >>> print(r1.status, r1.reason)
    200 OK
    >>> data1 = r1.read()  # This will return entire content.
    >>> # The following example demonstrates reading data in chunks.
    >>> conn.request("GET", "/")
    >>> r1 = conn.getresponse()
    >>> while chunk := r1.read(200):
    ...     print(repr(chunk))
    b'<!doctype html>\n<!--[if"...
    ...
    >>> # Example of an invalid request
    >>> conn = http.client.HTTPSConnection("docs.python.org")
    >>> conn.request("GET", "/parrot.spam")
    >>> r2 = conn.getresponse()
    >>> print(r2.status, r2.reason)
    404 Not Found
    >>> data2 = r2.read()
    >>> conn.close()

以下是使用 `HEAD` 方法的会话示例。 请注意，`HEAD` 方法从不返回任何数据。

    >>> import http.client
    >>> conn = http.client.HTTPSConnection("www.python.org")
    >>> conn.request("HEAD", "/")
    >>> res = conn.getresponse()
    >>> print(res.status, res.reason)
    200 OK
    >>> data = res.read()
    >>> print(len(data))
    0
    >>> data == b''
    True

下面是用 `POST` 发送请求的会话示例：

    >>> import http.client, urllib.parse
    >>> params = urllib.parse.urlencode({'@number': 12524, '@type': 'issue', '@action': 'show'})
    >>> headers = {"Content-type": "application/x-www-form-urlencoded",
    ...            "Accept": "text/plain"}
    >>> conn = http.client.HTTPConnection("bugs.python.org")
    >>> conn.request("POST", "", params, headers)
    >>> response = conn.getresponse()
    >>> print(response.status, response.reason)
    302 Found
    >>> data = response.read()
    >>> data
    b'Redirecting to <a href="http://bugs.python.org/issue12524">http://bugs.python.org/issue12524</a>'
    >>> conn.close()

在客户端， `HTTP PUT` 请求与 `POST` 请求非常相似。区别只在于服务器端，HTTP 服务器将允许通过 `PUT` 请求创建资源。应该注意的是，自定义的 HTTP 方法也可以在 `urllib.request.Request` 中通过设置适当的方法属性来进行处理。下面是一个会话示例，演示了如何利用 http.client 发送 `PUT` 请求。

    >>> # This creates an HTTP message
    >>> # with the content of BODY as the enclosed representation
    >>> # for the resource http://localhost:8080/file
    ...
    >>> import http.client
    >>> BODY = "***filecontents***"
    >>> conn = http.client.HTTPConnection("localhost", 8080)
    >>> conn.request("PUT", "/file", BODY)
    >>> response = conn.getresponse()
    >>> print(response.status, response.reason)
    200, OK

## HTTPMessage 对象

`http.client.HTTPMessage` 的实例存有 HTTP 响应的头部信息。利用 `email.message.Message` 类实现。