扩展功能
请求和响应扩展提供了一个无类型的空间,可以用于添加额外信息。
扩展功能应当用于那些可能并非所有传输方式都支持的特性,以及不适合直接放入底层 httpcore
包作为 API 使用的简化请求/响应模型中的特性。
请求支持以下几种扩展:
# 请求超时实际上是作为请求的一个扩展实现的,
# 确保它们能在整个调用栈中传递。
client = httpx.Client()
response = client.get(
"https://www.example.com",
extensions={"timeout": {"connect": 5.0}}
)
response.request.extensions["timeout"]
{"connect": 5.0}
响应也支持扩展:
client = httpx.Client()
response = client.get("https://www.example.com")
print(response.extensions["http_version"]) # b"HTTP/1.1"
# 其他服务器响应可能是
# b"HTTP/0.9", b"HTTP/1.0" 或 b"HTTP/1.1"
请求扩展
"trace"
trace 扩展允许安装一个回调处理器,用于监控底层 httpcore
传输内部的事件流。
最简单的解释方式是通过示例:
import httpx
def log(event_name, info):
print(event_name, info)
client = httpx.Client()
response = client.get("https://www.example.com/", extensions={"trace": log})
# connection.connect_tcp.started {'host': 'www.example.com', 'port': 443, 'local_address': None, 'timeout': None}
# connection.connect_tcp.complete {'return_value': <httpcore.backends.sync.SyncStream object at 0x1093f94d0>}
# connection.start_tls.started {'ssl_context': <ssl.SSLContext object at 0x1093ee750>, 'server_hostname': b'www.example.com', 'timeout': None}
# connection.start_tls.complete {'return_value': <httpcore.backends.sync.SyncStream object at 0x1093f9450>}
# http11.send_request_headers.started {'request': <Request [b'GET']>}
# http11.send_request_headers.complete {'return_value': None}
# http11.send_request_body.started {'request': <Request [b'GET']>}
# http11.send_request_body.complete {'return_value': None}
# http11.receive_response_headers.started {'request': <Request [b'GET']>}
# http11.receive_response_headers.complete {'return_value': (b'HTTP/1.1', 200, b'OK', [(b'Age', b'553715'), (b'Cache-Control', b'max-age=604800'), (b'Content-Type', b'text/html; charset=UTF-8'), (b'Date', b'Thu, 21 Oct 2021 17:08:42 GMT'), (b'Etag', b'"3147526947+ident"'), (b'Expires', b'Thu, 28 Oct 2021 17:08:42 GMT'), (b'Last-Modified', b'Thu, 17 Oct 2019 07:18:26 GMT'), (b'Server', b'ECS (nyb/1DCD)'), (b'Vary', b'Accept-Encoding'), (b'X-Cache', b'HIT'), (b'Content-Length', b'1256')])}
# http11.receive_response_body.started {'request': <Request [b'GET']>}
# http11.receive_response_body.complete {'return_value': None}
# http11.response_closed.started {}
# http11.response_closed.complete {'return_value': None}
这里的 event_name
和 info
参数会是以下形式之一:
{event_type}.{event_name}.started
,<关键字参数字典>
{event_type}.{event_name}.complete
,{"return_value": <...>}
{event_type}.{event_name}.failed
,{"exception": <...>}
请注意,在使用异步代码时,传递给 "trace"
的处理函数必须是 async def ...
函数。
当前暴露的以下事件类型...
建立连接
"connection.connect_tcp"
"connection.connect_unix_socket"
"connection.start_tls"
HTTP/1.1 事件
"http11.send_request_headers"
"http11.send_request_body"
"http11.receive_response"
"http11.receive_response_body"
"http11.response_closed"
HTTP/2 事件
"http2.send_connection_init"
"http2.send_request_headers"
"http2.send_request_body"
"http2.receive_response_headers"
"http2.receive_response_body"
"http2.response_closed"
httpcore
不同版本中追踪事件的具体集合可能会有所变化。如果您需要依赖特定的事件集合,建议将该包的安装固定到特定版本。
"sni_hostname"
服务器的主机名,用于验证 SSL 证书提供的主机名。
如果您想连接到明确的 IP 地址而非使用标准的 DNS 主机名查找,则需要使用此请求扩展。
例如:
# 连接到 '185.199.108.153' 但 Host 头中使用 'www.encode.io',
# 并在 SSL 验证服务器主机名时使用 'www.encode.io'。
client = httpx.Client()
headers = {"Host": "www.encode.io"}
extensions = {"sni_hostname": "www.encode.io"}
response = client.get(
"https://185.199.108.153/path",
headers=headers,
extensions=extensions
)
"timeout"
一个包含 str: Optional[float]
超时值的字典。
可以包含 'connect'
、'read'
、'write'
或 'pool'
的值。
例如:
# 如果连接建立耗时超过 5 秒,或
# 等待连接池阻塞超过 10 秒则超时。
client = httpx.Client()
response = client.get(
"https://www.example.com",
extensions={"timeout": {"connect": 5.0, "pool": 10.0}}
)
该扩展是 httpx
超时功能的实现方式,确保超时值与请求实例相关联并传递至整个堆栈。通常您不应直接使用此扩展,而应使用更高层的 timeout
API。
"target"
用作 HTTP 目标而非 URL 路径 的目标参数。
这使得可以构造原本不受支持的请求:
- 应用了非标准转义的 URL 路径
- 使用绝对 URI 的转发代理请求
- 使用
CONNECT
并以主机名作为目标的隧道代理请求 - 服务器范围的
OPTIONS *
请求
一些示例:
使用 'target' 扩展发送不遵循标准路径转义规则的请求...
# 通常请求 "https://www.example.com/test^path" 会
# 连接到 "www.example.com" 并发送如下 HTTP/1.1 请求...
#
# GET /test%5Epath HTTP/1.1
#
# 使用 target 扩展我们可以包含字面的 '^'...
#
# GET /test^path HTTP/1.1
#
# 注意请求仍需是有效的 HTTP 请求
# 例如在 target 中包含空格会引发 `LocalProtocolError`
extensions = {"target": b"/test^path"}
response = httpx.get("https://www.example.com", extensions=extensions)
target
扩展还允许构造服务器范围的 OPTIONS *
请求...
# 这会发送如下请求...
#
# CONNECT * HTTP/1.1
extensions = {"target": b"*"}
response = httpx.request("CONNECT", "https://www.example.com", extensions=extensions)
响应扩展
"http_version"
HTTP 版本,以字节形式表示。例如 b"HTTP/1.1"
。
使用 HTTP/1.1 时,响应行包含显式版本号,此键的值可能是 b"HTTP/0.9"
、b"HTTP/1.0"
或 b"HTTP/1.1"
之一。
使用 HTTP/2 时,协议中不再包含进一步的响应版本信息,此键的值将始终为 b"HTTP/2"
。
"reason_phrase"
HTTP 响应的原因短语(reason-phrase),以字节形式返回。例如 b"OK"
。某些服务器可能会包含自定义的原因短语,尽管这种做法不被推荐。
从 HTTP/2 开始,协议中不再包含原因短语。
当响应头中未包含该字段时,可能会基于状态码使用默认值。
"stream_id"
当使用 HTTP/2 协议时,可以通过 "stream_id"
响应扩展来获取该响应所对应的数据流 ID。
"network_stream"
"network_stream"
扩展允许开发者通过提供一套脱离标准请求/响应模型的 API 来处理 HTTP CONNECT
和 Upgrade
请求,并可直接读写网络流。
网络流提供的接口包括:
read(max_bytes, timeout = None) -> bytes
write(buffer, timeout = None)
close()
start_tls(ssl_context, server_hostname = None, timeout = None) -> NetworkStream
get_extra_info(info) -> Any
此 API 可作为处理 HTTP 代理、WebSocket 升级及其他高级用例的基础。
有关直接操作网络流的更多信息,请参阅网络后端文档。
额外网络信息
网络流抽象层还允许访问底层套接字可能暴露的各种低级信息:
response = httpx.get("https://www.example.com")
network_stream = response.extensions["network_stream"]
client_addr = network_stream.get_extra_info("client_addr")
server_addr = network_stream.get_extra_info("server_addr")
print("客户端地址", client_addr)
print("服务端地址", server_addr)
套接字的 SSL 信息也可以通过此接口获取,不过需要确保底层连接仍然处于打开状态才能访问...
with httpx.stream("GET", "https://www.example.com") as response:
network_stream = response.extensions["network_stream"]
ssl_object = network_stream.get_extra_info("ssl_object")
print("TLS 版本", ssl_object.version())