My question is simple - where is the code responsible to raise ConnectionResetError
on cpython3 following a call to self._sslobj.read(len, buffer)
on ssl.py
?
I'm getting sometimes ConnectionResetError
when trying to connect to S3 with ssl. this error occurs rarely so its tricky to reproduce it.
# 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
looking at ssl.py:631
gives me no further clues - we have to go deeper!:
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
i've tried searching it on CPython repo but AFAICS nothing seems to raise it, i suspect its hidden in SSL implementation or on some mapping between OSError
to ConnectionError
subclasses.
my final goal is to write py2 & py3 compatible code for handling this exceptions (ConnectionError
is new on py3) by comparing the module's py2 & py3 versions that raises this error.
ConnectionError
subclassesmy question origins was to find a way to catch ConnectionError
and its subclasses on python2 & python3, so here it is:
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)
ConnectionResetError
is raised when errno
is ECONNRESET
. errno
is how libc indicates whether or not an error occurred in a system call.
You could search ConnectionResetError
in Objects/exceptions.c
to find out how this exception type get initialized and added to errnomap
dict.
In the case of self._sslobj.read
raised ConnectionResetError
, _sslobj.read
is implemented with _ssl__SSLSocket_read_impl
, the actual ssl read is done with openssl's SSL_read
:
count = SSL_read(self->ssl, mem, len);
_PySSL_UPDATE_ERRNO_IF(count <= 0, self, count);
as the error occurred, _PySSL_UPDATE_ERRNO_IF
will set (sock)->ssl_errno = SSL_ERROR_SYSCALL
and (sock)->c_errno = ECONNRESET
.
later, in 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)
equals with:
OSError(errno.ECONNRESET, 'Connection reset by peer', ...)
when OSError
constructs with an errno
, it will lookup a more specified subclass, by lookup errno
value in the aforementioned errnomap
dict:
newtype = PyDict_GetItem(errnomap, myerrno);
if (newtype) {
assert(PyType_Check(newtype));
type = (PyTypeObject *) newtype;
}
it actually returns and raises out a ConnectionResetError
exception.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With