13

使用以下代码Cannot connect to host ...:443 ssl:True时,我使用异步aiohttp. 当我使用 synchronousrequests时,它成功了。

whitehouse.gov链接失败,但异步google.com和同步情况都成功。

出了什么问题?这是 FreeBSD8 上的 python 3.4.2,aiohttp 0.14.4,请求 2.5.3

import asyncio
import aiohttp
import requests

urls = [
    'http://www.whitehouse.gov/cea/', 
    'http://www.whitehouse.gov/omb', 
    'http://www.google.com']


def test_sync():
    for url in urls:
        r = requests.get(url)
        print(r.status_code)


def test_async():
    for url in urls:
        try:
            r = yield from aiohttp.request('get', url)
        except aiohttp.errors.ClientOSError as e:
            print('bad eternal link %s: %s' % (url, e))
        else:
            print(r.status)


if __name__ == '__main__':
    print('async')
    asyncio.get_event_loop().run_until_complete(test_async())
    print('sync')
    test_sync()

输出是:

async
bad eternal link http://www.whitehouse.gov/cea: Cannot connect to host www.whitehouse.gov:443 ssl:True
bad eternal link http://www.whitehouse.gov/omb: Cannot connect to host www.whitehouse.gov:443 ssl:True
200
sync
200
200
200
4

4 回答 4

17

我怀疑您的机器上的证书验证链已损坏。正如@dano 提到的,在 Ubuntu 上一切正常。

Connector无论如何,您可以通过创建自定义实例来禁用 ssl 验证:

import asyncio
import aiohttp

urls = [
    'http://www.whitehouse.gov/cea/',
    'http://www.whitehouse.gov/omb',
    'http://www.google.com']


def test_async():
    connector = aiohttp.TCPConnector(verify_ssl=False)
    for url in urls:
        try:
            r = yield from aiohttp.request('get', url, connector=connector)
        except aiohttp.errors.ClientOSError as e:
            print('bad eternal link %s: %s' % (url, e))
        else:
            print(r.status)


if __name__ == '__main__':
    print('async')
    asyncio.get_event_loop().run_until_complete(test_async())

顺便说一句,requests库附带了自己的证书包。也许我们需要为 aiohttp 做同样的事情?

UPD。另见https://github.com/aio-libs/aiohttp/issues/341

于 2015-04-25T10:10:36.370 回答
4

我在具有过期 CA 根证书的旧 Linux 服务器上遇到了同样的问题,并且在修复了该问题时加载了 certifi CA 证书包。SSLContext

import aiohttp
import ssl
import certifi

ssl_context = ssl.create_default_context(cafile=certifi.where())
async with aiohttp.ClientSession() as session:
    async with session.get('https://some.foo/bar/', ssl=ssl_context) as response:
        print(await response.text())
于 2019-03-07T14:55:54.443 回答
0

这对我有用:

if sys.version_info[0] == 3 and sys.version_info[1] >= 8 and sys.platform.startswith('win'):
    asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
于 2021-01-18T18:12:29.203 回答
0

不要使用这个答案,因为它可能等于禁用证书检查。

正如 Le Hibou 在评论中指出的那样,ssl.Purpose.CLIENT_AUTH用于在服务器端对客户端进行身份验证。

该值表示上下文可用于验证 Web 客户端(因此,它将用于创建服务器端套接字)。

查看代码create_default_context()表明证书检查在这种情况下可能被禁用或至少是可选的。


我有相同的错误消息(在 Windows 上)并用以下方法解决了它:

import aiohttp
import ssl


client = aiohttp.ClientSession()
client.post(
    'https://some.foo/bar/',
    json={"foo": "bar"},
    ssl=ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH))

这是使用request()ssl的参数来设置与默认值不同的 SSL 上下文。默认值为ssl.create_default_context()

问题是默认值为ssl.create_default_context()

ssl.create_default_context(purpose=Purpose.SERVER_AUTH, cafile=None, capath=None, cadata=None)

Purpose.SERVER_AUTH客户端验证服务器证书时似乎不起作用。而是Purpose.CLIENT_AUTH在作为客户端验证时使用。

于 2018-11-01T16:13:03.667 回答