dea*_*nha 11 python ssl openssl python-asyncio aiohttp
尝试从URL下载和处理jpeg.我的问题是不适合的网址有些证书验证失败,因为这些网址都是老可能不再是值得信赖的,但是,当我try...except...
的SSLCertVerificationError
,我仍然得到回溯.
系统:Linux 4.17.14-arch1-1-ARCH,python 3.7.0-3,aiohttp 3.3.2
最小的例子:
import asyncio
import aiohttp
from ssl import SSLCertVerificationError
async def fetch_url(url, client):
try:
async with client.get(url) as resp:
print(resp.status)
print(await resp.read())
except SSLCertVerificationError as e:
print('Error handled')
async def main(urls):
tasks = []
async with aiohttp.ClientSession(loop=loop) as client:
for url in urls:
task = asyncio.ensure_future(fetch_url(url, client))
tasks.append(task)
return await asyncio.gather(*tasks)
loop = asyncio.get_event_loop()
loop.run_until_complete(main(['https://images.photos.com/']))
Run Code Online (Sandbox Code Playgroud)
输出:
SSL handshake failed on verifying the certificate
protocol: <asyncio.sslproto.SSLProtocol object at 0x7ffbecad8ac8>
transport: <_SelectorSocketTransport fd=6 read=polling write=<idle, bufsize=0>>
Traceback (most recent call last):
File "/usr/lib/python3.7/asyncio/sslproto.py", line 625, in _on_handshake_complete
raise handshake_exc
File "/usr/lib/python3.7/asyncio/sslproto.py", line 189, in feed_ssldata
self._sslobj.do_handshake()
File "/usr/lib/python3.7/ssl.py", line 763, in do_handshake
self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Hostname mismatch, certificate is not valid for 'images.photos.com'. (_ssl.c:1045)
SSL error in data received
protocol: <asyncio.sslproto.SSLProtocol object at 0x7ffbecad8ac8>
transport: <_SelectorSocketTransport closing fd=6 read=idle write=<idle, bufsize=0>>
Traceback (most recent call last):
File "/usr/lib/python3.7/asyncio/sslproto.py", line 526, in data_received
ssldata, appdata = self._sslpipe.feed_ssldata(data)
File "/usr/lib/python3.7/asyncio/sslproto.py", line 189, in feed_ssldata
self._sslobj.do_handshake()
File "/usr/lib/python3.7/ssl.py", line 763, in do_handshake
self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Hostname mismatch, certificate is not valid for 'images.photos.com'. (_ssl.c:1045)
Error handled
Run Code Online (Sandbox Code Playgroud)
use*_*342 14
追溯由asyncio的SSL协议实现生成,该协议调用事件循环的异常处理程序。通过传输和流接口之间的交互迷宫,碰巧该异常既被事件循环记录,又传播到API用户。发生的方式如下:
SSLProtocol._on_handshake_complete
接收non-None handshake_exc
并将其视为“致命错误”(在握手上下文中),即调用self._fatal_error
并返回。_fatal_error
调用事件循环的异常处理程序以记录错误。通常,对于在队列回调中发生的异常(通常不再有调用者将其传播到该异常)调用处理程序,因此它仅将回溯记录为标准错误,以确保异常不会静默传递。然而..._fatal_error
继续调用transport._force_close
,该connection_lost
协议回叫该协议。connection_lost
实现将异常设置为流读取器未来的结果,从而将其传播给等待它的流API用户。如果是bug或功能,事件循环记录并传递到的相同异常是不明显的connection_lost
。将BaseProtocol.connection_lost
其定义为no-op可能是一种解决方法,因此额外的日志可确保仅继承自的协议BaseProtocol
不会使SSL握手期间发生的可能敏感的异常保持沉默。无论哪种原因,当前行为都会导致OP遇到问题:捕获异常不足以抑制异常,仍将记录回溯。
要解决此问题,可以将异常处理程序临时设置为不报告的异常处理程序SSLCertVerificationError
:
@contextlib.contextmanager
def suppress_ssl_exception_report():
loop = asyncio.get_event_loop()
old_handler = loop.get_exception_handler()
old_handler_fn = old_handler or lambda _loop, ctx: loop.default_exception_handler(ctx)
def ignore_exc(_loop, ctx):
exc = ctx.get('exception')
if isinstance(exc, SSLCertVerificationError):
return
old_handler_fn(loop, ctx)
loop.set_exception_handler(ignore_exc)
try:
yield
finally:
loop.set_exception_handler(old_handler)
Run Code Online (Sandbox Code Playgroud)
在with suppress_ssl_exception_report()
周围添加代码fetch_url
可抑制不必要的回溯。
上面的方法可以工作,但是强烈感觉像是解决潜在问题的方法,而不是正确使用API,因此我在跟踪器中提交了错误报告。
归档时间: |
|
查看次数: |
2269 次 |
最近记录: |