ssl/asyncio:即使处理错误也可追溯 [英] ssl/asyncio: traceback even when error is handled

查看:579
本文介绍了ssl/asyncio:即使处理错误也可追溯的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

尝试从URL下载和处理jpeg.我的问题不是不是某些URL的证书验证失败,因为这些URL很旧并且可能不再值得信任,而是当我try...except... SSLCertVerificationError时,仍然获得了追溯.

Trying to download and process jpegs from URLs. My issue isn't that certificate verification fails for some URLs, as these URLs are old and may no longer be trustworthy, but that when I try...except... the SSLCertVerificationError, I still get the traceback.

系统: Linux 4.17.14-arch1-1-ARCH,python 3.7.0-3,aiohttp 3.3.2

System: 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/']))

输出:

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

推荐答案

回溯由asyncio的SSL协议实现生成,该协议调用事件循环的

The traceback is generated by asyncio's implementation of the SSL protocol, which invokes the event loop's exception handler. Through a maze of interactions between the transport/protocol and the streaming layers, this exception gets logged by the event loop and propagated to the API user. The way that happens is as follows:

  • SSL握手期间发生异常.
  • SSLProtocol._on_handshake_complete 收到非-无handshake_exc并将其视为致命错误"(在握手上下文中),即调用self._fatal_error并返回.
  • _fatal_error 调用事件循环的异常处理程序记录错误.通常,对于在队列回调中发生的异常(通常不再有调用者将其传播到该异常)调用该处理程序,因此它仅将回溯记录到标准错误中,以确保异常不会以静默方式传递.但是...
  • _fatal_error继续调用 ,它会在协议上回叫connection_lost.
  • 流读取器协议的connection_lost 实现将异常设置为流阅读器未来的结果,从而将异常传播给等待它的流API用户.
  • An exception occurs during the SSL handshake.
  • SSLProtocol._on_handshake_complete receives non-None handshake_exc and treats it as a "fatal error" (in the handshake context), i.e. invokes self._fatal_error and returns.
  • _fatal_error calls the event loop's exception handler to log the error. The handler is normally invoked for exceptions that occur in queued callbacks where there is no longer a caller to propagate them to, so it just logs the traceback to standard error to ensure that the exception doesn't pass silently. However...
  • _fatal_error goes on to call transport._force_close, which calls connection_lost back on the protocol.
  • The stream reader protocol's connection_lost implementation sets the exception as the result of the stream reader's future, thus propagating it to the users of the stream API that await it.

如果是错误或功能,事件循环将相同的异常都记录下来并传递给connection_lost,这不是很明显.对于BaseProtocol.connection_lost定义了一个变通方法no-op ,因此额外的日志可确保仅从BaseProtocol继承的协议不会使SSL握手期间发生的可能敏感的异常保持沉默.无论哪种原因,当前行为都会导致OP遇到问题:捕获异常不足以抑制异常,仍将记录回溯.

It is not obvious if it is a bug or a feature that the same exception is both logged by the event loop and passed to connection_lost. It might be a workaround for BaseProtocol.connection_lost being defined a no-op, so the extra log ensures that a protocol that simply inherits from BaseProtocol doesn't silence the possibly sensitive exceptions occurring during SSL handshake. Whichever the reason, the current behavior leads to the problem experienced by the OP: catching the exception is not enough to suppress it, a traceback will still be logged.

要变通解决此问题,可以将异常处理程序临时设置为不报告SSLCertVerificationError的异常处理程序:

To work around the issue, one can temporarily set the exception handler to one that doesn't report 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)

fetch_url中的代码周围添加with suppress_ssl_exception_report()可以抑制不必要的回溯.

Adding with suppress_ssl_exception_report() around the code in fetch_url suppresses the unwanted traceback.

以上方法有效,但强烈感觉像是针对基本问题的解决方法,而不是正确使用API​​,因此我提交了

The above works, but it strongly feels like a workaround for an underlying issue and not like correct API usage, so I filed a bug report in the tracker. the issue was fixed in the meantime and the code from the question no longer prints the spurious backtrace.

这篇关于ssl/asyncio:即使处理错误也可追溯的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆