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

源码: Lib/http/client.py

本模块定义了HTTP和HTTPS协议的客户端实现。通常来说，该模块不能直接使用，需要模块`urllib.request`调用该模块来处理使用HTTP和HTTPS的URL。

参考

推荐使用Requests package包处理高级别HTTP客户端接口。

注意

仅当Python通过SSL支持（通过`ssl`模块）进行编译时，HTTPS支持才可用。

本模块提供以下类：

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

  一个 `HTTPConnection`实例表示与HTTP服务器端的一次事务。通过传参host地址和端口号进行实例化。没有端口号传参，也可以通过host传参，形式同`host:port`，都没有的话，默认端口号为80。如果可选参数 timeout给出传参，阻塞操作（如连接）将在给出的秒数后超时（如果未给出该参数，默认使用全局默认超时设置值）。可选参数source_address可以是一个元组，形式同 (host, port)，作为HTTP链接的源地址使用。

  示例，下面几个调用都创建了实例，都是指向同个地址同个端口的服务器端的链接。见下：

      >>> 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中已更改：已删除严格参数。不再支持HTTP 0.9样式的“简单响应”。

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

  该类是 `HTTPConnection`的子类，使用SSL，用于与服务器端的安全通信。默认端口是 `443`。如果需要指定上下文context，必须使用 `ssl.SSLContext`实例化，它用于描述不同的SSL选项。

  key_file和cert_file已被弃用，请改用`ssl.SSLContext.load_cert_chain()`或让`ssl.create_default_context()`为您选择系统的受信任的CA证书。check_hostname参数也已弃用；应使用上下文的`ssl.SSLContext.check_hostname`属性。

  有关最佳做法的详情，请参阅Security considerations。

  在版本3.2中更改： 源地址，上下文和check_hostname已添加。

  在版本3.2中更改：此类现在支持HTTPS虚拟主机（如果可能的话）（即，如果`ssl.HAS_SNI`为true）。

  在版本3.4中已更改：已删除严格参数。不再支持HTTP 0.9样式的“简单响应”。

  在版本3.4.3中更改：此类现在默认执行所有必需的证书和主机名检查。要还原到上一个未验证的行为，`ssl._create_unverified_context()`可以传递到上下文参数。

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

  该类是在连接成功后返回的实例。不需要用户直接实例化。

  在版本3.4中已更改：已删除严格参数。不再支持HTTP 0.9样式“简单响应”。

可能会涉及到的异常类，见下：

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`

  该字典类匹配HTTP1.1状态码在W3C中的命名。

  Example: `http.client.responses[http.client.NOT_FOUND]` 就是 `'Not Found'`.

本模块中HTTP状态码的常量值列表可参考HTTP status codes。

## 21.12.1. HTTPConnection对象

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

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

  该方法会向服务器端发送一个请求，入参为HTTP请求方法method和选择器url。

  如果指定了入参body ，那么在 headers发送完成后就发送body的数据。它可能是一个字符串、 一个 类似字节对象、 打开的 文件对象，或可迭代的 类似字节对象 。如果 body 是一个字符串，它按 ISO-8859-1进行编码，默认 HTTP 。如果body是一个类似字节对象，按字节发送。如果body是一个 文件对象，发送的是文件的内容；这个文件对象至少可支持 `read()` 方法。If the file object has a `mode` attribute, the data returned by the `read()` method will be encoded as ISO-8859-1 unless the `mode` attribute contains the substring `b`, otherwise the data returned by `read()` is sent as is. 如果body是可迭代的，则可迭代的元素按照原样发送，直到可迭代被耗尽。

  参数 headers 应该是附带的HTTP报文头的映射，与请求一起发送。

  如果参数 headers 不包括内容长度项，那么就自动添加一个。如果body为`None`，那么对于需要body的方法（`PUT`），Content-Length标头设置为`0` ，`POST`和`PATCH`）。如果body是字符串或字节对象，则将Content-Length头设置为其长度。如果body是一个file object，并且它的作用是对`fileno()`的结果调用`fstat()`方法，那么Content-Length头部被设置为由`fstat`调用报告的`st_size`。否则不添加Content-Length头。

  版本3.2中的新功能： 正文现在可以是可迭代的。

`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 Connect隧道的主机和端口。这允许通过代理服务器运行连接。

  主机和端口参数指定隧道连接的端点（即，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`()

  关闭与服务器的连接。

作为使用上述`request()`方法的替代方法，您还可以使用以下四个函数逐步发送请求。

`HTTPConnection.``putrequest`(request, selector, skip_host=False, skip_accept_encoding=False)

  这应该是在连接到服务器之后的第一个调用。它向由请求字符串，选择器字符串和HTTP版本（`HTTP/1.1`）组成的服务器发送一行。要禁止自动发送`Host:`或`Accept-Encoding:`标头（例如接受附加内容编码），请指定skip_host或skip_accept_encoding与非False值。

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

  向服务器发送 RFC 822类型头文件。它发送一行到服务器，包括头，冒号和空格，以及第一个参数。如果给出更多的参数，则发送连续行，每行由一个制表符和一个参数组成。

`HTTPConnection.``endheaders`(message_body=None)

  发送空白行到服务器，用信号通知头的结束。可选的message_body参数可用于传递与请求相关联的消息体。如果消息头是字符串，消息体将与消息头在相同的分组中发送，否则它将在单独的分组中发送。

`HTTPConnection.``send`(data)

  将数据发送到服务器。只有在调用`endheaders()`方法和调用`getresponse()`之前，才应该直接使用此方法。

## 21.12.2. 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'是除了单个字符串之外的任何可迭代，它的元素类似地返回用逗号连接。

`HTTPResponse.``getheaders`()

  返回（标题，值）元组的列表。

`HTTPResponse.``fileno`()

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

`HTTPResponse.``msg`

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

`HTTPResponse.``version`

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

`HTTPResponse.``status`

  服务器返回的状态代码。

`HTTPResponse.``reason`

  服务器返回的原因短语。

`HTTPResponse.``debuglevel`

  调试挂钩。如果`debuglevel`大于零，则会在读取和解析响应时将消息打印到stdout。

`HTTPResponse.``closed`

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

## 21.12.3. 示例

使用 `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 not r1.closed:
    ...     print(r1.read(200))  # 200 bytes
    b'<!doctype html>\n<!--[if"...
    ...
    >>> # Example of an invalid request
    >>> 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`中处理。这是一个示例会话，显示如何做`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

## 21.12.4. HTTPMessage对象

`http.client.HTTPMessage`实例保存来自HTTP响应的标头。它使用`email.message.Message`类实现。