这个问题的简短回答是:请求会在任何异常情况下关闭连接,包括KeyboardInterrupt和SystemExit。
请求源代码中的little digging 显示requests.get 最终调用HTTPAdapter.send 方法(这是所有魔法发生的地方)。
在send 方法中可以通过两种方式发出请求:分块或不分块。我们执行哪个send 取决于request.body 和Content-Length 标头的值:
chunked = not (request.body is None or 'Content-Length' in request.headers)
在请求体为None或设置了Content-Length的情况下,requests将urlopen的高级方法make use的urlopenurllib3:
if not chunked:
resp = conn.urlopen(
method=request.method,
url=url,
body=request.body,
# ...
)
urllib3.PoolManager.urlopen 方法的finally 块具有处理在try 块未成功执行的情况下关闭连接的代码:
clean_exit = False
# ...
try:
# ...
# Everything went great!
clean_exit = True
finally:
if not clean_exit:
# We hit some kind of exception, handled or otherwise. We need
# to throw the connection away unless explicitly told not to.
# Close the connection, set the variable to None, and make sure
# we put the None back in the pool to avoid leaking it.
conn = conn and conn.close()
release_this_conn = True
在响应可以分块的情况下,请求会降低一点,并使用urllib3 提供的底层低级连接。在这种情况下,请求仍然处理异常,它使用try / except 块来处理,该块在获取连接后立即开始,并以:
low_conn = conn._get_conn(timeout=DEFAULT_POOL_TIMEOUT)
try:
# ...
except:
# If we hit any problems here, clean up the connection.
# Then, reraise so that we can handle the actual exception.
low_conn.close()
raise
有趣的是,如果没有错误,连接可能不会关闭,具体取决于您为urllib3 配置连接池的方式。在成功执行的情况下,连接将被放回连接池(尽管我在 requests 源中找不到分块 send 的 _put_conn 调用,这可能是分块工作中的错误 -流)。