gpt4 book ai didi

python - SSL ConnectionResetError 从哪里来?

转载 作者:太空狗 更新时间:2023-10-29 17:08:16 27 4
gpt4 key购买 nike

长话短说

我的问题很简单 - 在调用 self._sslobj.read(len, buffer)< 后,负责在 cpython3 上引发 ConnectionResetError 的代码在哪里ssl.py 上?

背景

我有时会收到 ConnectionResetError尝试使用 ssl 连接到 S3 时。此错误很少发生,因此很难重现。

# trimmed stacktrace
File "/MYPROJECT/MY_FUNC.py", line 123, in <genexpr>
rows = (row for row in reader)
File "/XXX/lib/python3.6/csv.py", line 112, in _next_
row = next(self.reader)
File "/XXX/lib/python3.6/tarfile.py", line 706, in readinto
buf = self.read(len(b))
File "/XXX/lib/python3.6/tarfile.py", line 695, in read
b = self.fileobj.read(length)
File "/XXX/lib/python3.6/gzip.py", line 276, in read
return self._buffer.read(size)
File "/XXX/lib/python3.6/_compression.py", line 68, in readinto
data = self.read(len(byte_view))
File "/XXX/lib/python3.6/gzip.py", line 469, in read
buf = self._fp.read(io.DEFAULT_BUFFER_SIZE)
File "/XXX/lib/python3.6/gzip.py", line 91, in read
self.file.read(size-self._length+read)
File "/XXX/lib/python3.6/site-packages/s3fs/core.py", line 1311, in read
self._fetch(self.loc, self.loc + length)
File "/XXX/lib/python3.6/site-packages/s3fs/core.py", line 1292, in _fetch
req_kw=self.s3.req_kw)
File "/XXX/lib/python3.6/site-packages/s3fs/core.py", line 1496, in _fetch_range
return resp['Body'].read()
File "/XXX/lib/python3.6/site-packages/botocore/response.py", line 74, in read
chunk = self._raw_stream.read(amt)
File "/XXX/lib/python3.6/site-packages/botocore/vendored/requests/packages/urllib3/response.py", line 239, in read
data = self._fp.read()
File "/XXX/lib/python3.6/http/client.py", line 462, in read
s = self._safe_read(self.length)
File "/XXX/lib/python3.6/http/client.py", line 612, in _safe_read
chunk = self.fp.read(min(amt, MAXAMOUNT))
File "/XXX/lib/python3.6/socket.py", line 586, in readinto
return self._sock.recv_into(b)
File "/XXX/lib/python3.6/ssl.py", line 1009, in recv_into
return self.read(nbytes, buffer)
File "/XXX/lib/python3.6/ssl.py", line 871, in read
return self._sslobj.read(len, buffer)
File "/XXX/lib/python3.6/ssl.py", line 631, in read
v = self._sslobj.read(len, buffer)
ConnectionResetError: [Errno 104] Connection reset by peer

我试过的

查看 ssl.py:631 没有给我更多线索 - 我们必须更深入!

    def read(self, len=1024, buffer=None):
"""Read up to 'len' bytes from the SSL object and return them.

If 'buffer' is provided, read into this buffer and return the number of
bytes read.
"""
if buffer is not None:
v = self._sslobj.read(len, buffer) # <--- exception here
else:
v = self._sslobj.read(len)
return v

我试过搜索它 on CPython repo但是 AFAICS 似乎没有什么可以引发它,我怀疑它隐藏在 SSL 实现中或 OSErrorConnectionError 子类之间的某些映射中。

我的最终目标是通过比较引发此错误的模块的 py2 和 py3 版本来编写 py2 和 py3 兼容代码来处理此异常(ConnectionError 是 py3 上的新功能)。


更新 - py2 和 py3 捕获 ConnectionError 子类

我的问题起源是找到一种方法来捕获 ConnectionError 及其在 python2 和 python3 上的子类,所以这里是:

import errno

# ref: https://docs.python.org/3/library/exceptions.html#ConnectionError
_CONNECTION_ERRORS = frozenset({
errno.ECONNRESET, # ConnectionResetError
errno.EPIPE, errno.ESHUTDOWN, # BrokenPipeError
errno.ECONNABORTED, # ConnectionAbortedError
errno.ECONNREFUSED, # ConnectionRefusedError
})

try:
...
except OSError as e:
if e.errno not in _CONNECTION_ERRORS:
raise
print('got ConnectionError - %e' % e)

最佳答案

ConnectionResetErrorerrno 时引发是 ECONNRESETerrno 是 libc 指示系统调用是否发生错误的方式。

您可以在 Objects/exceptions.c 中搜索 ConnectionResetError 以了解此异常类型是如何初始化并添加到 errnomap 字典中的。

self._sslobj.read引发ConnectionResetError的情况下,_sslobj.read是用_ssl__SSLSocket_read_impl实现的,实际的 ssl 读取是用 openssl 的 SSL_read 完成的:

count = SSL_read(self->ssl, mem, len);
_PySSL_UPDATE_ERRNO_IF(count <= 0, self, count);

当错误发生时,_PySSL_UPDATE_ERRNO_IF 将设置 (sock)->ssl_errno = SSL_ERROR_SYSCALL(sock)->c_errno = ECONNRESET

稍后,在 PySSL_SetError 中:

    err = obj->ssl_errno;
switch (err) {
...
case SSL_ERROR_SYSCALL:

if (obj->c_errno) {
errno = obj->c_errno;
return PyErr_SetFromErrno(PyExc_OSError);
}

PyErr_SetFromErrno(PyExc_OSError) 等于:

OSError(errno.ECONNRESET, 'Connection reset by peer', ...)

OSError 使用 errno 构造时,it will lookup a more specified subclass ,通过在上述errnomap dict中查找errno值:

newtype = PyDict_GetItem(errnomap, myerrno);
if (newtype) {
assert(PyType_Check(newtype));
type = (PyTypeObject *) newtype;
}

它实际上返回并引发了一个 ConnectionResetError 异常。

关于python - SSL ConnectionResetError 从哪里来?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52312606/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com