update
This commit is contained in:
679
frontEnd/venv/lib/python3.12/site-packages/amqp/transport.py
Normal file
679
frontEnd/venv/lib/python3.12/site-packages/amqp/transport.py
Normal file
@@ -0,0 +1,679 @@
|
||||
"""Transport implementation."""
|
||||
# Copyright (C) 2009 Barry Pederson <bp@barryp.org>
|
||||
|
||||
import errno
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import ssl
|
||||
from contextlib import contextmanager
|
||||
from ssl import SSLError
|
||||
from struct import pack, unpack
|
||||
|
||||
from .exceptions import UnexpectedFrame
|
||||
from .platform import KNOWN_TCP_OPTS, SOL_TCP
|
||||
from .utils import set_cloexec
|
||||
|
||||
_UNAVAIL = {errno.EAGAIN, errno.EINTR, errno.ENOENT, errno.EWOULDBLOCK}
|
||||
|
||||
AMQP_PORT = 5672
|
||||
|
||||
EMPTY_BUFFER = bytes()
|
||||
|
||||
SIGNED_INT_MAX = 0x7FFFFFFF
|
||||
|
||||
# Yes, Advanced Message Queuing Protocol Protocol is redundant
|
||||
AMQP_PROTOCOL_HEADER = b'AMQP\x00\x00\x09\x01'
|
||||
|
||||
# Match things like: [fe80::1]:5432, from RFC 2732
|
||||
IPV6_LITERAL = re.compile(r'\[([\.0-9a-f:]+)\](?::(\d+))?')
|
||||
|
||||
DEFAULT_SOCKET_SETTINGS = {
|
||||
'TCP_NODELAY': 1,
|
||||
'TCP_USER_TIMEOUT': 1000,
|
||||
'TCP_KEEPIDLE': 60,
|
||||
'TCP_KEEPINTVL': 10,
|
||||
'TCP_KEEPCNT': 9,
|
||||
}
|
||||
|
||||
|
||||
def to_host_port(host, default=AMQP_PORT):
|
||||
"""Convert hostname:port string to host, port tuple."""
|
||||
port = default
|
||||
m = IPV6_LITERAL.match(host)
|
||||
if m:
|
||||
host = m.group(1)
|
||||
if m.group(2):
|
||||
port = int(m.group(2))
|
||||
else:
|
||||
if ':' in host:
|
||||
host, port = host.rsplit(':', 1)
|
||||
port = int(port)
|
||||
return host, port
|
||||
|
||||
|
||||
class _AbstractTransport:
|
||||
"""Common superclass for TCP and SSL transports.
|
||||
|
||||
PARAMETERS:
|
||||
host: str
|
||||
|
||||
Broker address in format ``HOSTNAME:PORT``.
|
||||
|
||||
connect_timeout: int
|
||||
|
||||
Timeout of creating new connection.
|
||||
|
||||
read_timeout: int
|
||||
|
||||
sets ``SO_RCVTIMEO`` parameter of socket.
|
||||
|
||||
write_timeout: int
|
||||
|
||||
sets ``SO_SNDTIMEO`` parameter of socket.
|
||||
|
||||
socket_settings: dict
|
||||
|
||||
dictionary containing `optname` and ``optval`` passed to
|
||||
``setsockopt(2)``.
|
||||
|
||||
raise_on_initial_eintr: bool
|
||||
|
||||
when True, ``socket.timeout`` is raised
|
||||
when exception is received during first read. See ``_read()`` for
|
||||
details.
|
||||
"""
|
||||
|
||||
def __init__(self, host, connect_timeout=None,
|
||||
read_timeout=None, write_timeout=None,
|
||||
socket_settings=None, raise_on_initial_eintr=True, **kwargs):
|
||||
self.connected = False
|
||||
self.sock = None
|
||||
self.raise_on_initial_eintr = raise_on_initial_eintr
|
||||
self._read_buffer = EMPTY_BUFFER
|
||||
self.host, self.port = to_host_port(host)
|
||||
self.connect_timeout = connect_timeout
|
||||
self.read_timeout = read_timeout
|
||||
self.write_timeout = write_timeout
|
||||
self.socket_settings = socket_settings
|
||||
|
||||
__slots__ = (
|
||||
"connection",
|
||||
"sock",
|
||||
"raise_on_initial_eintr",
|
||||
"_read_buffer",
|
||||
"host",
|
||||
"port",
|
||||
"connect_timeout",
|
||||
"read_timeout",
|
||||
"write_timeout",
|
||||
"socket_settings",
|
||||
# adding '__dict__' to get dynamic assignment
|
||||
"__dict__",
|
||||
"__weakref__",
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
if self.sock:
|
||||
src = f'{self.sock.getsockname()[0]}:{self.sock.getsockname()[1]}'
|
||||
try:
|
||||
dst = f'{self.sock.getpeername()[0]}:{self.sock.getpeername()[1]}'
|
||||
except (socket.error) as e:
|
||||
dst = f'ERROR: {e}'
|
||||
return f'<{type(self).__name__}: {src} -> {dst} at {id(self):#x}>'
|
||||
else:
|
||||
return f'<{type(self).__name__}: (disconnected) at {id(self):#x}>'
|
||||
|
||||
def connect(self):
|
||||
try:
|
||||
# are we already connected?
|
||||
if self.connected:
|
||||
return
|
||||
self._connect(self.host, self.port, self.connect_timeout)
|
||||
self._init_socket(
|
||||
self.socket_settings, self.read_timeout, self.write_timeout,
|
||||
)
|
||||
# we've sent the banner; signal connect
|
||||
# EINTR, EAGAIN, EWOULDBLOCK would signal that the banner
|
||||
# has _not_ been sent
|
||||
self.connected = True
|
||||
except (OSError, SSLError):
|
||||
# if not fully connected, close socket, and reraise error
|
||||
if self.sock and not self.connected:
|
||||
self.sock.close()
|
||||
self.sock = None
|
||||
raise
|
||||
|
||||
@contextmanager
|
||||
def having_timeout(self, timeout):
|
||||
if timeout is None:
|
||||
yield self.sock
|
||||
else:
|
||||
sock = self.sock
|
||||
prev = sock.gettimeout()
|
||||
if prev != timeout:
|
||||
sock.settimeout(timeout)
|
||||
try:
|
||||
yield self.sock
|
||||
except SSLError as exc:
|
||||
if 'timed out' in str(exc):
|
||||
# http://bugs.python.org/issue10272
|
||||
raise socket.timeout()
|
||||
elif 'The operation did not complete' in str(exc):
|
||||
# Non-blocking SSL sockets can throw SSLError
|
||||
raise socket.timeout()
|
||||
raise
|
||||
except OSError as exc:
|
||||
if exc.errno == errno.EWOULDBLOCK:
|
||||
raise socket.timeout()
|
||||
raise
|
||||
finally:
|
||||
if timeout != prev:
|
||||
sock.settimeout(prev)
|
||||
|
||||
def _connect(self, host, port, timeout):
|
||||
entries = socket.getaddrinfo(
|
||||
host, port, socket.AF_UNSPEC, socket.SOCK_STREAM, SOL_TCP,
|
||||
)
|
||||
for i, res in enumerate(entries):
|
||||
af, socktype, proto, canonname, sa = res
|
||||
try:
|
||||
self.sock = socket.socket(af, socktype, proto)
|
||||
try:
|
||||
set_cloexec(self.sock, True)
|
||||
except NotImplementedError:
|
||||
pass
|
||||
self.sock.settimeout(timeout)
|
||||
self.sock.connect(sa)
|
||||
except socket.error:
|
||||
if self.sock:
|
||||
self.sock.close()
|
||||
self.sock = None
|
||||
if i + 1 >= len(entries):
|
||||
raise
|
||||
else:
|
||||
break
|
||||
|
||||
def _init_socket(self, socket_settings, read_timeout, write_timeout):
|
||||
self.sock.settimeout(None) # set socket back to blocking mode
|
||||
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
||||
self._set_socket_options(socket_settings)
|
||||
|
||||
# set socket timeouts
|
||||
for timeout, interval in ((socket.SO_SNDTIMEO, write_timeout),
|
||||
(socket.SO_RCVTIMEO, read_timeout)):
|
||||
if interval is not None:
|
||||
sec = int(interval)
|
||||
usec = int((interval - sec) * 1000000)
|
||||
self.sock.setsockopt(
|
||||
socket.SOL_SOCKET, timeout,
|
||||
pack('ll', sec, usec),
|
||||
)
|
||||
self._setup_transport()
|
||||
|
||||
self._write(AMQP_PROTOCOL_HEADER)
|
||||
|
||||
def _get_tcp_socket_defaults(self, sock):
|
||||
tcp_opts = {}
|
||||
for opt in KNOWN_TCP_OPTS:
|
||||
enum = None
|
||||
if opt == 'TCP_USER_TIMEOUT':
|
||||
try:
|
||||
from socket import TCP_USER_TIMEOUT as enum
|
||||
except ImportError:
|
||||
# should be in Python 3.6+ on Linux.
|
||||
enum = 18
|
||||
elif hasattr(socket, opt):
|
||||
enum = getattr(socket, opt)
|
||||
|
||||
if enum:
|
||||
if opt in DEFAULT_SOCKET_SETTINGS:
|
||||
tcp_opts[enum] = DEFAULT_SOCKET_SETTINGS[opt]
|
||||
elif hasattr(socket, opt):
|
||||
tcp_opts[enum] = sock.getsockopt(
|
||||
SOL_TCP, getattr(socket, opt))
|
||||
return tcp_opts
|
||||
|
||||
def _set_socket_options(self, socket_settings):
|
||||
tcp_opts = self._get_tcp_socket_defaults(self.sock)
|
||||
if socket_settings:
|
||||
tcp_opts.update(socket_settings)
|
||||
for opt, val in tcp_opts.items():
|
||||
self.sock.setsockopt(SOL_TCP, opt, val)
|
||||
|
||||
def _read(self, n, initial=False):
|
||||
"""Read exactly n bytes from the peer."""
|
||||
raise NotImplementedError('Must be overridden in subclass')
|
||||
|
||||
def _setup_transport(self):
|
||||
"""Do any additional initialization of the class."""
|
||||
pass
|
||||
|
||||
def _shutdown_transport(self):
|
||||
"""Do any preliminary work in shutting down the connection."""
|
||||
pass
|
||||
|
||||
def _write(self, s):
|
||||
"""Completely write a string to the peer."""
|
||||
raise NotImplementedError('Must be overridden in subclass')
|
||||
|
||||
def close(self):
|
||||
if self.sock is not None:
|
||||
try:
|
||||
self._shutdown_transport()
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
# Call shutdown first to make sure that pending messages
|
||||
# reach the AMQP broker if the program exits after
|
||||
# calling this method.
|
||||
try:
|
||||
self.sock.shutdown(socket.SHUT_RDWR)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.sock.close()
|
||||
except OSError:
|
||||
pass
|
||||
self.sock = None
|
||||
self.connected = False
|
||||
|
||||
def read_frame(self, unpack=unpack):
|
||||
"""Parse AMQP frame.
|
||||
|
||||
Frame has following format::
|
||||
|
||||
0 1 3 7 size+7 size+8
|
||||
+------+---------+---------+ +-------------+ +-----------+
|
||||
| type | channel | size | | payload | | frame-end |
|
||||
+------+---------+---------+ +-------------+ +-----------+
|
||||
octet short long 'size' octets octet
|
||||
|
||||
"""
|
||||
read = self._read
|
||||
read_frame_buffer = EMPTY_BUFFER
|
||||
try:
|
||||
frame_header = read(7, True)
|
||||
read_frame_buffer += frame_header
|
||||
frame_type, channel, size = unpack('>BHI', frame_header)
|
||||
# >I is an unsigned int, but the argument to sock.recv is signed,
|
||||
# so we know the size can be at most 2 * SIGNED_INT_MAX
|
||||
if size > SIGNED_INT_MAX:
|
||||
part1 = read(SIGNED_INT_MAX)
|
||||
|
||||
try:
|
||||
part2 = read(size - SIGNED_INT_MAX)
|
||||
except (socket.timeout, OSError, SSLError):
|
||||
# In case this read times out, we need to make sure to not
|
||||
# lose part1 when we retry the read
|
||||
read_frame_buffer += part1
|
||||
raise
|
||||
|
||||
payload = b''.join([part1, part2])
|
||||
else:
|
||||
payload = read(size)
|
||||
read_frame_buffer += payload
|
||||
frame_end = ord(read(1))
|
||||
except socket.timeout:
|
||||
self._read_buffer = read_frame_buffer + self._read_buffer
|
||||
raise
|
||||
except (OSError, SSLError) as exc:
|
||||
if (
|
||||
isinstance(exc, socket.error) and os.name == 'nt'
|
||||
and exc.errno == errno.EWOULDBLOCK # noqa
|
||||
):
|
||||
# On windows we can get a read timeout with a winsock error
|
||||
# code instead of a proper socket.timeout() error, see
|
||||
# https://github.com/celery/py-amqp/issues/320
|
||||
self._read_buffer = read_frame_buffer + self._read_buffer
|
||||
raise socket.timeout()
|
||||
|
||||
if isinstance(exc, SSLError) and 'timed out' in str(exc):
|
||||
# Don't disconnect for ssl read time outs
|
||||
# http://bugs.python.org/issue10272
|
||||
self._read_buffer = read_frame_buffer + self._read_buffer
|
||||
raise socket.timeout()
|
||||
|
||||
if exc.errno not in _UNAVAIL:
|
||||
self.connected = False
|
||||
raise
|
||||
# frame-end octet must contain '\xce' value
|
||||
if frame_end == 206:
|
||||
return frame_type, channel, payload
|
||||
else:
|
||||
raise UnexpectedFrame(
|
||||
f'Received frame_end {frame_end:#04x} while expecting 0xce')
|
||||
|
||||
def write(self, s):
|
||||
try:
|
||||
self._write(s)
|
||||
except socket.timeout:
|
||||
raise
|
||||
except OSError as exc:
|
||||
if exc.errno not in _UNAVAIL:
|
||||
self.connected = False
|
||||
raise
|
||||
|
||||
|
||||
class SSLTransport(_AbstractTransport):
|
||||
"""Transport that works over SSL.
|
||||
|
||||
PARAMETERS:
|
||||
host: str
|
||||
|
||||
Broker address in format ``HOSTNAME:PORT``.
|
||||
|
||||
connect_timeout: int
|
||||
|
||||
Timeout of creating new connection.
|
||||
|
||||
ssl: bool|dict
|
||||
|
||||
parameters of TLS subsystem.
|
||||
- when ``ssl`` is not dictionary, defaults of TLS are used
|
||||
- otherwise:
|
||||
- if ``ssl`` dictionary contains ``context`` key,
|
||||
:attr:`~SSLTransport._wrap_context` is used for wrapping
|
||||
socket. ``context`` is a dictionary passed to
|
||||
:attr:`~SSLTransport._wrap_context` as context parameter.
|
||||
All others items from ``ssl`` argument are passed as
|
||||
``sslopts``.
|
||||
- if ``ssl`` dictionary does not contain ``context`` key,
|
||||
:attr:`~SSLTransport._wrap_socket_sni` is used for
|
||||
wrapping socket. All items in ``ssl`` argument are
|
||||
passed to :attr:`~SSLTransport._wrap_socket_sni` as
|
||||
parameters.
|
||||
|
||||
kwargs:
|
||||
|
||||
additional arguments of
|
||||
:class:`~amqp.transport._AbstractTransport` class
|
||||
"""
|
||||
|
||||
def __init__(self, host, connect_timeout=None, ssl=None, **kwargs):
|
||||
self.sslopts = ssl if isinstance(ssl, dict) else {}
|
||||
self._read_buffer = EMPTY_BUFFER
|
||||
super().__init__(
|
||||
host, connect_timeout=connect_timeout, **kwargs)
|
||||
|
||||
__slots__ = (
|
||||
"sslopts",
|
||||
)
|
||||
|
||||
def _setup_transport(self):
|
||||
"""Wrap the socket in an SSL object."""
|
||||
self.sock = self._wrap_socket(self.sock, **self.sslopts)
|
||||
# Explicitly set a timeout here to stop any hangs on handshake.
|
||||
self.sock.settimeout(self.connect_timeout)
|
||||
self.sock.do_handshake()
|
||||
self._quick_recv = self.sock.read
|
||||
|
||||
def _wrap_socket(self, sock, context=None, **sslopts):
|
||||
if context:
|
||||
return self._wrap_context(sock, sslopts, **context)
|
||||
return self._wrap_socket_sni(sock, **sslopts)
|
||||
|
||||
def _wrap_context(self, sock, sslopts, check_hostname=None, **ctx_options):
|
||||
"""Wrap socket without SNI headers.
|
||||
|
||||
PARAMETERS:
|
||||
sock: socket.socket
|
||||
|
||||
Socket to be wrapped.
|
||||
|
||||
sslopts: dict
|
||||
|
||||
Parameters of :attr:`ssl.SSLContext.wrap_socket`.
|
||||
|
||||
check_hostname
|
||||
|
||||
Whether to match the peer cert’s hostname. See
|
||||
:attr:`ssl.SSLContext.check_hostname` for details.
|
||||
|
||||
ctx_options
|
||||
|
||||
Parameters of :attr:`ssl.create_default_context`.
|
||||
"""
|
||||
ctx = ssl.create_default_context(**ctx_options)
|
||||
ctx.check_hostname = check_hostname
|
||||
return ctx.wrap_socket(sock, **sslopts)
|
||||
|
||||
def _wrap_socket_sni(self, sock, keyfile=None, certfile=None,
|
||||
server_side=False, cert_reqs=None,
|
||||
ca_certs=None, do_handshake_on_connect=False,
|
||||
suppress_ragged_eofs=True, server_hostname=None,
|
||||
ciphers=None, ssl_version=None):
|
||||
"""Socket wrap with SNI headers.
|
||||
|
||||
stdlib :attr:`ssl.SSLContext.wrap_socket` method augmented with support
|
||||
for setting the server_hostname field required for SNI hostname header.
|
||||
|
||||
PARAMETERS:
|
||||
sock: socket.socket
|
||||
|
||||
Socket to be wrapped.
|
||||
|
||||
keyfile: str
|
||||
|
||||
Path to the private key
|
||||
|
||||
certfile: str
|
||||
|
||||
Path to the certificate
|
||||
|
||||
server_side: bool
|
||||
|
||||
Identifies whether server-side or client-side
|
||||
behavior is desired from this socket. See
|
||||
:attr:`~ssl.SSLContext.wrap_socket` for details.
|
||||
|
||||
cert_reqs: ssl.VerifyMode
|
||||
|
||||
When set to other than :attr:`ssl.CERT_NONE`, peers certificate
|
||||
is checked. Possible values are :attr:`ssl.CERT_NONE`,
|
||||
:attr:`ssl.CERT_OPTIONAL` and :attr:`ssl.CERT_REQUIRED`.
|
||||
|
||||
ca_certs: str
|
||||
|
||||
Path to “certification authority” (CA) certificates
|
||||
used to validate other peers’ certificates when ``cert_reqs``
|
||||
is other than :attr:`ssl.CERT_NONE`.
|
||||
|
||||
do_handshake_on_connect: bool
|
||||
|
||||
Specifies whether to do the SSL
|
||||
handshake automatically. See
|
||||
:attr:`~ssl.SSLContext.wrap_socket` for details.
|
||||
|
||||
suppress_ragged_eofs (bool):
|
||||
|
||||
See :attr:`~ssl.SSLContext.wrap_socket` for details.
|
||||
|
||||
server_hostname: str
|
||||
|
||||
Specifies the hostname of the service which
|
||||
we are connecting to. See :attr:`~ssl.SSLContext.wrap_socket`
|
||||
for details.
|
||||
|
||||
ciphers: str
|
||||
|
||||
Available ciphers for sockets created with this
|
||||
context. See :attr:`ssl.SSLContext.set_ciphers`
|
||||
|
||||
ssl_version:
|
||||
|
||||
Protocol of the SSL Context. The value is one of
|
||||
``ssl.PROTOCOL_*`` constants.
|
||||
"""
|
||||
opts = {
|
||||
'sock': sock,
|
||||
'server_side': server_side,
|
||||
'do_handshake_on_connect': do_handshake_on_connect,
|
||||
'suppress_ragged_eofs': suppress_ragged_eofs,
|
||||
'server_hostname': server_hostname,
|
||||
}
|
||||
|
||||
if ssl_version is None:
|
||||
ssl_version = (
|
||||
ssl.PROTOCOL_TLS_SERVER
|
||||
if server_side
|
||||
else ssl.PROTOCOL_TLS_CLIENT
|
||||
)
|
||||
|
||||
context = ssl.SSLContext(ssl_version)
|
||||
|
||||
if certfile is not None:
|
||||
context.load_cert_chain(certfile, keyfile)
|
||||
if ca_certs is not None:
|
||||
context.load_verify_locations(ca_certs)
|
||||
if ciphers is not None:
|
||||
context.set_ciphers(ciphers)
|
||||
# Set SNI headers if supported.
|
||||
# Must set context.check_hostname before setting context.verify_mode
|
||||
# to avoid setting context.verify_mode=ssl.CERT_NONE while
|
||||
# context.check_hostname is still True (the default value in context
|
||||
# if client-side) which results in the following exception:
|
||||
# ValueError: Cannot set verify_mode to CERT_NONE when check_hostname
|
||||
# is enabled.
|
||||
try:
|
||||
context.check_hostname = (
|
||||
ssl.HAS_SNI and server_hostname is not None
|
||||
)
|
||||
except AttributeError:
|
||||
pass # ask forgiveness not permission
|
||||
|
||||
# See note above re: ordering for context.check_hostname and
|
||||
# context.verify_mode assignments.
|
||||
if cert_reqs is not None:
|
||||
context.verify_mode = cert_reqs
|
||||
|
||||
if ca_certs is None and context.verify_mode != ssl.CERT_NONE:
|
||||
purpose = (
|
||||
ssl.Purpose.CLIENT_AUTH
|
||||
if server_side
|
||||
else ssl.Purpose.SERVER_AUTH
|
||||
)
|
||||
context.load_default_certs(purpose)
|
||||
|
||||
sock = context.wrap_socket(**opts)
|
||||
return sock
|
||||
|
||||
def _shutdown_transport(self):
|
||||
"""Unwrap a SSL socket, so we can call shutdown()."""
|
||||
if self.sock is not None:
|
||||
self.sock = self.sock.unwrap()
|
||||
|
||||
def _read(self, n, initial=False,
|
||||
_errnos=(errno.ENOENT, errno.EAGAIN, errno.EINTR)):
|
||||
# According to SSL_read(3), it can at most return 16kb of data.
|
||||
# Thus, we use an internal read buffer like TCPTransport._read
|
||||
# to get the exact number of bytes wanted.
|
||||
recv = self._quick_recv
|
||||
rbuf = self._read_buffer
|
||||
try:
|
||||
while len(rbuf) < n:
|
||||
try:
|
||||
s = recv(n - len(rbuf)) # see note above
|
||||
except OSError as exc:
|
||||
# ssl.sock.read may cause ENOENT if the
|
||||
# operation couldn't be performed (Issue celery#1414).
|
||||
if exc.errno in _errnos:
|
||||
if initial and self.raise_on_initial_eintr:
|
||||
raise socket.timeout()
|
||||
continue
|
||||
raise
|
||||
if not s:
|
||||
raise OSError('Server unexpectedly closed connection')
|
||||
rbuf += s
|
||||
except: # noqa
|
||||
self._read_buffer = rbuf
|
||||
raise
|
||||
result, self._read_buffer = rbuf[:n], rbuf[n:]
|
||||
return result
|
||||
|
||||
def _write(self, s):
|
||||
"""Write a string out to the SSL socket fully."""
|
||||
write = self.sock.write
|
||||
while s:
|
||||
try:
|
||||
n = write(s)
|
||||
except ValueError:
|
||||
# AG: sock._sslobj might become null in the meantime if the
|
||||
# remote connection has hung up.
|
||||
# In python 3.4, a ValueError is raised is self._sslobj is
|
||||
# None.
|
||||
n = 0
|
||||
if not n:
|
||||
raise OSError('Socket closed')
|
||||
s = s[n:]
|
||||
|
||||
|
||||
class TCPTransport(_AbstractTransport):
|
||||
"""Transport that deals directly with TCP socket.
|
||||
|
||||
All parameters are :class:`~amqp.transport._AbstractTransport` class.
|
||||
"""
|
||||
|
||||
def _setup_transport(self):
|
||||
# Setup to _write() directly to the socket, and
|
||||
# do our own buffered reads.
|
||||
self._write = self.sock.sendall
|
||||
self._read_buffer = EMPTY_BUFFER
|
||||
self._quick_recv = self.sock.recv
|
||||
|
||||
def _read(self, n, initial=False, _errnos=(errno.EAGAIN, errno.EINTR)):
|
||||
"""Read exactly n bytes from the socket."""
|
||||
recv = self._quick_recv
|
||||
rbuf = self._read_buffer
|
||||
try:
|
||||
while len(rbuf) < n:
|
||||
try:
|
||||
s = recv(n - len(rbuf))
|
||||
except OSError as exc:
|
||||
if exc.errno in _errnos:
|
||||
if initial and self.raise_on_initial_eintr:
|
||||
raise socket.timeout()
|
||||
continue
|
||||
raise
|
||||
if not s:
|
||||
raise OSError('Server unexpectedly closed connection')
|
||||
rbuf += s
|
||||
except: # noqa
|
||||
self._read_buffer = rbuf
|
||||
raise
|
||||
|
||||
result, self._read_buffer = rbuf[:n], rbuf[n:]
|
||||
return result
|
||||
|
||||
|
||||
def Transport(host, connect_timeout=None, ssl=False, **kwargs):
|
||||
"""Create transport.
|
||||
|
||||
Given a few parameters from the Connection constructor,
|
||||
select and create a subclass of
|
||||
:class:`~amqp.transport._AbstractTransport`.
|
||||
|
||||
PARAMETERS:
|
||||
|
||||
host: str
|
||||
|
||||
Broker address in format ``HOSTNAME:PORT``.
|
||||
|
||||
connect_timeout: int
|
||||
|
||||
Timeout of creating new connection.
|
||||
|
||||
ssl: bool|dict
|
||||
|
||||
If set, :class:`~amqp.transport.SSLTransport` is used
|
||||
and ``ssl`` parameter is passed to it. Otherwise
|
||||
:class:`~amqp.transport.TCPTransport` is used.
|
||||
|
||||
kwargs:
|
||||
|
||||
additional arguments of :class:`~amqp.transport._AbstractTransport`
|
||||
class
|
||||
"""
|
||||
transport = SSLTransport if ssl else TCPTransport
|
||||
return transport(host, connect_timeout=connect_timeout, ssl=ssl, **kwargs)
|
||||
Reference in New Issue
Block a user