Various updates and fixes:

Separate endpoint initialization from opening.
Allow remote to name the link.
Fix examples to index using container+link name.
Clean up connection processing state.
This commit is contained in:
Kenneth Giusti 2014-01-09 15:55:34 -05:00
parent 560de5b097
commit eb3013f512
5 changed files with 472 additions and 250 deletions

@ -37,16 +37,34 @@ to a server, and waits for a response. The method call is a map of the form:
class MyConnection(fusion.ConnectionEventHandler): class MyConnection(fusion.ConnectionEventHandler):
def __init__(self, name, socket, container, properties): def __init__(self, name, container, properties):
self.name = name self.name = name
self.socket = socket self.container = container
self.connection = container.create_connection(name, self, self.properties = properties
properties) self.socket = None
self.connection.user_context = self self.caller = None
self.connection = None
def reset(self):
if self.caller:
self.caller.reset()
if self.connection:
self.connection.user_context = None
self.connection.destroy()
self.connection = None
if self.socket:
self.socket.close()
self.socket = None
def connect(self, socket):
self.reset()
self.socket = socket
self.connection = self.container.create_connection(self.name,
self,
self.properties)
self.connection.user_context = self
self.connection.sasl.mechanisms("ANONYMOUS") self.connection.sasl.mechanisms("ANONYMOUS")
self.connection.sasl.client() self.connection.sasl.client()
self.connection.open() self.connection.open()
def process(self): def process(self):
@ -84,11 +102,20 @@ class MyConnection(fusion.ConnectionEventHandler):
return self.connection.closed return self.connection.closed
def destroy(self, error=None): def destroy(self, error=None):
self.connection.user_context = None self.reset()
self.connection.destroy() self.caller.destroy()
self.connection = None self.caller = None
self.socket.close() self.container = None
self.socket = None
def create_caller(self, method_map, source_addr, target_addr,
receiver_properties, sender_properties):
""" Caller factory
"""
if self.caller:
self.caller.destroy()
self.caller = MyCaller(method_map, self, source_addr, target_addr,
receiver_properties, sender_properties)
return self.caller
# Connection callbacks: # Connection callbacks:
@ -96,7 +123,10 @@ class MyConnection(fusion.ConnectionEventHandler):
"""connection handshake completed""" """connection handshake completed"""
LOG.debug("Connection active callback") LOG.debug("Connection active callback")
def connection_closed(self, connection, reason): def connection_remote_closed(self, connection, reason):
LOG.debug("Connection remote closed callback")
def connection_closed(self, connection):
LOG.debug("Connection closed callback") LOG.debug("Connection closed callback")
def sender_requested(self, connection, link_handle, def sender_requested(self, connection, link_handle,
@ -123,20 +153,48 @@ class MyCaller(fusion.SenderEventHandler,
def __init__(self, method_map, my_connection, def __init__(self, method_map, my_connection,
my_source, my_target, my_source, my_target,
receiver_properties, sender_properties): receiver_properties, sender_properties):
conn = my_connection.connection #self._name = uuid.uuid4().hex
self._sender = conn.create_sender(my_source, target_address=None, self._my_connection = my_connection
eventHandler=self, name=my_source, self._source_addr = my_source
properties=sender_properties) self._target_addr = my_target
self._receiver = conn.create_receiver(my_target, source_address=None, self._receiver_properties = receiver_properties
eventHandler=self, name=my_target, self._sender_properties = sender_properties
properties=receiver_properties)
self._method = method_map self._method = method_map
self._sender = None
self._receiver = None
self.reset()
def reset(self):
LOG.debug("Resetting my-caller")
# @todo: for now, use new name as engine isn't cleaning up link state properly...
self._name = uuid.uuid4().hex
self._reply_to = None self._reply_to = None
self._to = None self._to = None
self._send_completed = False self._send_completed = False
self._response_received = False self._response_received = False
if self._sender:
self._sender.destroy()
self._sender = None
if self._receiver:
self._receiver.destroy()
self._receiver = None
def connect(self):
self.reset()
LOG.debug("Connecting my-caller")
conn = self._my_connection.connection
self._sender = conn.create_sender(self._source_addr, target_address=None,
eventHandler=self,
#name=self._source_addr,
name=self._name,
properties=self._sender_properties)
self._receiver = conn.create_receiver(self._target_addr, source_address=None,
eventHandler=self,
#name=self._target_addr,
name=self._name,
properties=self._receiver_properties)
self._sender.open() self._sender.open()
self._receiver.add_capacity(1) self._receiver.add_capacity(1)
self._receiver.open() self._receiver.open()
@ -145,14 +203,17 @@ class MyCaller(fusion.SenderEventHandler,
return self._send_completed and self._response_received return self._send_completed and self._response_received
def close(self): def close(self):
LOG.debug("Closing my-caller")
self._sender.close(None) self._sender.close(None)
self._receiver.close(None) self._receiver.close(None)
def closed(self):
return self._sender.closed and self._receiver.closed
def destroy(self): def destroy(self):
if self._sender: LOG.debug("Destroying my-caller")
self._sender.destroy() self.reset()
if self._receiver: self._my_connection = None
self._receiver.destroy()
def _send_request(self): def _send_request(self):
"""Send a message containing the RPC method call """Send a message containing the RPC method call
@ -179,7 +240,12 @@ class MyCaller(fusion.SenderEventHandler,
if self._reply_to: if self._reply_to:
self._send_request() self._send_request()
def sender_closed(self, sender_link, error): def sender_remote_closed(self, sender_link, error):
LOG.debug("Sender remote closed callback")
assert sender_link is self._sender
self._sender.close()
def sender_closed(self, sender_link):
LOG.debug("Sender closed callback") LOG.debug("Sender closed callback")
assert sender_link is self._sender assert sender_link is self._sender
self._send_completed = True self._send_completed = True
@ -200,7 +266,12 @@ class MyCaller(fusion.SenderEventHandler,
if self._to: if self._to:
self._send_request() self._send_request()
def receiver_closed(self, receiver_link, error): def receiver_remote_closed(self, receiver_link, error):
LOG.debug("receiver remote closed callback")
assert receiver_link is self._receiver
self._receiver.close()
def receiver_closed(self, receiver_link):
LOG.debug("Receiver closed callback") LOG.debug("Receiver closed callback")
assert receiver_link is self._receiver assert receiver_link is self._receiver
self._response_received = True self._response_received = True
@ -222,6 +293,9 @@ def main(argv=None):
help="The address of the server [amqp://0.0.0.0:5672]") help="The address of the server [amqp://0.0.0.0:5672]")
parser.add_option("-t", "--timeout", dest="timeout", type="int", parser.add_option("-t", "--timeout", dest="timeout", type="int",
help="timeout used when waiting for reply, in seconds") help="timeout used when waiting for reply, in seconds")
parser.add_option("--repeat", dest="repeat", type="int",
default=1,
help="Repeat the RPC call REPEAT times (0 == forever)")
parser.add_option("--trace", dest="trace", action="store_true", parser.add_option("--trace", dest="trace", action="store_true",
help="enable protocol tracing") help="enable protocol tracing")
parser.add_option("--debug", dest="debug", action="store_true", parser.add_option("--debug", dest="debug", action="store_true",
@ -263,26 +337,42 @@ def main(argv=None):
conn_properties = {} conn_properties = {}
if opts.trace: if opts.trace:
conn_properties["trace"] = True conn_properties["trace"] = True
my_connection = MyConnection( "to-server", my_socket,
container, conn_properties) my_connection = MyConnection( "to-server", container, conn_properties)
# Create the RPC caller # Create the RPC caller
method = {'method': method_info[0], method = {'method': method_info[0],
'args': dict([(method_info[i], method_info[i+1]) 'args': dict([(method_info[i], method_info[i+1])
for i in range(1, len(method_info), 2)])} for i in range(1, len(method_info), 2)])}
my_caller = MyCaller( method, my_caller = my_connection.create_caller( method,
my_connection, "my-source-address",
"my-source-address", "my-target-address",
"my-target-address", receiver_properties={},
receiver_properties={"capacity": 1}, sender_properties={})
sender_properties={}) my_connection.connect(my_socket)
while not my_caller.done(): repeat = 0
my_connection.process() while opts.repeat == 0 or repeat < opts.repeat:
LOG.debug("RPC completed, closing connections") LOG.debug("Requesting RPC...")
my_caller.close() my_caller.connect()
while not my_caller.done():
my_connection.process()
LOG.debug("RPC completed! Closing caller...")
my_caller.close()
while not my_caller.closed():
my_connection.process()
LOG.debug("Caller closed cleanly!")
repeat += 1
print("Closing connections")
my_connection.close() my_connection.close()
while not my_connection.closed: while not my_connection.closed:
my_connection.process() my_connection.process()

@ -38,9 +38,16 @@ map sent in the request.
""" """
# Maps of outgoing and incoming links # Maps of outgoing and incoming links. These are indexed by
sender_links = {} # indexed by Source address # (remote-container-name, link-name)
receiver_links = {} # indexed by Target address sender_links = {}
receiver_links = {}
# Map reply-to address to the proper sending link (indexed by address)
reply_senders = {}
# database of all active SocketConnections
socket_connections = {} # indexed by name
class SocketConnection(fusion.ConnectionEventHandler): class SocketConnection(fusion.ConnectionEventHandler):
@ -55,6 +62,16 @@ class SocketConnection(fusion.ConnectionEventHandler):
self.connection.sasl.mechanisms("ANONYMOUS") self.connection.sasl.mechanisms("ANONYMOUS")
self.connection.sasl.server() self.connection.sasl.server()
self.connection.open() self.connection.open()
self.done = False
def destroy(self):
self.done = True
if self.connection:
self.connection.destroy()
self.connection = None
if self.socket:
self.socket.close()
self.socket = None
def fileno(self): def fileno(self):
"""Allows use of a SocketConnection in a select() call. """Allows use of a SocketConnection in a select() call.
@ -80,43 +97,58 @@ class SocketConnection(fusion.ConnectionEventHandler):
def connection_active(self, connection): def connection_active(self, connection):
LOG.debug("Connection active callback") LOG.debug("Connection active callback")
def connection_closed(self, connection, reason): def connection_remote_closed(self, connection, reason):
LOG.debug("Connection closed callback") LOG.debug("Connection remote closed callback")
assert self.connection is connection
self.connection.close()
def connection_closed(self, connection):
LOG.debug("connection closed.")
# main loop will destroy
self.done = True
def sender_requested(self, connection, link_handle, def sender_requested(self, connection, link_handle,
requested_source, properties): name, requested_source, properties):
LOG.debug("sender requested callback") LOG.debug("sender requested callback")
global sender_links global sender_links
global reply_senders
# reject if name conflict
remote_container = connection.remote_container
ident = (remote_container, name)
if ident in sender_links:
connection.reject_sender(link_handle, "link name in use")
return
name = uuid.uuid4().hex
# allow for requested_source address if it doesn't conflict with an # allow for requested_source address if it doesn't conflict with an
# existing address # existing address, otherwise override
if not requested_source or requested_source in sender_links: if not requested_source or requested_source in reply_senders:
requested_source = "/%s/%s" % (connection.container.name, requested_source = uuid.uuid4().hex
name) assert requested_source not in reply_senders
assert requested_source not in sender_links
sender = MySenderLink(connection, link_handle, requested_source, sender = MySenderLink(ident, connection, link_handle, requested_source)
name, {}) sender_links[ident] = sender
sender_links[requested_source] = sender reply_senders[requested_source] = sender
print("New Sender link created, source=%s" % requested_source) print("New Sender link created, source=%s" % requested_source)
def receiver_requested(self, connection, link_handle, def receiver_requested(self, connection, link_handle,
requested_target, properties): name, requested_target, properties):
LOG.debug("receiver requested callback") LOG.debug("receiver requested callback")
# allow for requested_source address if it doesn't conflict with an
# existing address
global receiver_links global receiver_links
name = uuid.uuid4().hex # reject if name conflict
if not requested_target or requested_target in receiver_links: remote_container = connection.remote_container
requested_target = "/%s/%s" % (connection.container.name, ident = (remote_container, name)
name) if ident in receiver_links:
assert requested_target not in receiver_links connection.reject_sender(link_handle, "link name in use")
return
receiver = MyReceiverLink(connection, link_handle, # I don't use the target address, but supply one if necessary
requested_target, name) if not requested_target:
receiver_links[requested_target] = receiver requested_target = uuid.uuid4().hex
receiver = MyReceiverLink(ident, connection, link_handle, requested_target)
receiver_links[ident] = receiver
print("New Receiver link created, target=%s" % requested_target) print("New Receiver link created, target=%s" % requested_target)
# SASL callbacks: # SASL callbacks:
@ -132,13 +164,14 @@ class SocketConnection(fusion.ConnectionEventHandler):
class MySenderLink(fusion.SenderEventHandler): class MySenderLink(fusion.SenderEventHandler):
""" """
""" """
def __init__(self, connection, link_handle, source_address, def __init__(self, ident, connection, link_handle,
name, properties): source_address, properties={}):
self._ident = ident
self._source_address = source_address
self.sender_link = connection.accept_sender(link_handle, self.sender_link = connection.accept_sender(link_handle,
source_address, source_address,
self, self,
name,
properties) properties)
self.sender_link.open() self.sender_link.open()
@ -147,8 +180,21 @@ class MySenderLink(fusion.SenderEventHandler):
def sender_active(self, sender_link): def sender_active(self, sender_link):
LOG.debug("sender active callback") LOG.debug("sender active callback")
def sender_closed(self, sender_link, error): def sender_remote_closed(self, sender_link, error):
LOG.debug("sender remote closed callback")
self.sender_link.close()
def sender_closed(self, sender_link):
LOG.debug("sender closed callback") LOG.debug("sender closed callback")
global sender_links
global reply_senders
if self._ident in sender_links:
del sender_links[self._ident]
if self._source_address in reply_senders:
del reply_senders[self._source_address]
self.sender_link.destroy()
self.sender_link = None
# 'message sent' callback: # 'message sent' callback:
@ -159,12 +205,13 @@ class MySenderLink(fusion.SenderEventHandler):
class MyReceiverLink(fusion.ReceiverEventHandler): class MyReceiverLink(fusion.ReceiverEventHandler):
""" """
""" """
def __init__(self, connection, link_handle, target_address, def __init__(self, ident, connection, link_handle, target_address,
name, properties={}): properties={}):
self._ident = ident
self._target_address = target_address
self._link = connection.accept_receiver(link_handle, self._link = connection.accept_receiver(link_handle,
target_address, target_address,
self, self,
name,
properties) properties)
self._link.open() self._link.open()
@ -173,22 +220,32 @@ class MyReceiverLink(fusion.ReceiverEventHandler):
LOG.debug("receiver active callback") LOG.debug("receiver active callback")
self._link.add_capacity(5) self._link.add_capacity(5)
def receiver_closed(self, receiver_link, error): def receiver_remote_closed(self, receiver_link, error):
LOG.debug("receiver remote closed callback")
self._link.close()
def receiver_closed(self, receiver_link):
LOG.debug("receiver closed callback") LOG.debug("receiver closed callback")
global receiver_links
if self._ident in receiver_links:
del receiver_links[self._ident]
self._link.destroy()
self._link = None
def message_received(self, receiver_link, message, handle): def message_received(self, receiver_link, message, handle):
LOG.debug("message received callback") LOG.debug("message received callback")
global sender_links global reply_senders
# extract to reply-to, correlation id # extract to reply-to, correlation id
reply_to = message.reply_to reply_to = message.reply_to
if not reply_to or reply_to not in sender_links: if not reply_to or reply_to not in reply_senders:
LOG.error("sender for reply-to not found, reply-to=%s", LOG.error("sender for reply-to not found, reply-to=%s",
str(reply_to)) str(reply_to))
self._link.message_rejected(handle, "Bad reply-to address") self._link.message_rejected(handle, "Bad reply-to address")
else: else:
my_sender = sender_links[reply_to] my_sender = reply_senders[reply_to]
correlation_id = message.correlation_id correlation_id = message.correlation_id
method_map = message.body method_map = message.body
if (not isinstance(method_map, dict) or if (not isinstance(method_map, dict) or
@ -260,7 +317,7 @@ def main(argv=None):
# create an AMQP container that will 'provide' the RPC service # create an AMQP container that will 'provide' the RPC service
# #
container = fusion.Container("example RPC service") container = fusion.Container("example RPC service")
socket_connections = {} # indexed by name (uuid) global socket_connections
while True: while True:
@ -292,6 +349,7 @@ def main(argv=None):
readable,writable,ignore = select.select(readfd,writefd,[],timeout) readable,writable,ignore = select.select(readfd,writefd,[],timeout)
LOG.debug("select() returned") LOG.debug("select() returned")
worked = []
for r in readable: for r in readable:
if r is my_socket: if r is my_socket:
# new inbound connection request # new inbound connection request
@ -305,19 +363,38 @@ def main(argv=None):
client_socket, client_socket,
container, container,
conn_properties) conn_properties)
LOG.debug("new connection created name=%s" % name)
else: else:
assert isinstance(r, SocketConnection) assert isinstance(r, SocketConnection)
rc = r.process_input() rc = r.process_input()
worked.append(r)
for t in timers: for t in timers:
now = time.time() now = time.time()
if t.next_tick > now: if t.next_tick > now:
break break
t.process(now) t.process(now)
sc = t.user_context
assert isinstance(sc, SocketConnection)
worked.append(sc)
for w in writable: for w in writable:
assert isinstance(w, SocketConnection) assert isinstance(w, SocketConnection)
rc = w.send_output() rc = w.send_output()
worked.append(w)
# nuke any completed connections:
closed = False
while worked:
sc = worked.pop()
if sc.done:
if sc.name in socket_connections:
del socket_connections[sc.name]
sc.destroy()
closed = True
if closed:
LOG.debug("%d active connections present" % len(socket_connections))
return 0 return 0

@ -35,17 +35,22 @@ class ConnectionEventHandler(object):
"""connection handshake completed""" """connection handshake completed"""
LOG.debug("connection_active (ignored)") LOG.debug("connection_active (ignored)")
def connection_closed(self, connection, reason): def connection_remote_closed(self, connection, error=None):
LOG.debug("connection_remote_closed (ignored)")
def connection_closed(self, connection):
LOG.debug("connection_closed (ignored)") LOG.debug("connection_closed (ignored)")
def sender_requested(self, connection, link_handle, def sender_requested(self, connection, link_handle,
requested_source, properties={}): name, requested_source,
properties={}):
# call accept_sender to accept new link, # call accept_sender to accept new link,
# reject_sender to reject it. # reject_sender to reject it.
LOG.debug("sender_requested (ignored)") LOG.debug("sender_requested (ignored)")
def receiver_requested(self, connection, link_handle, def receiver_requested(self, connection, link_handle,
requested_target, properties={}): name, requested_target,
properties={}):
# call accept_sender to accept new link, # call accept_sender to accept new link,
# reject_sender to reject it. # reject_sender to reject it.
LOG.debug("receiver_requested (ignored)") LOG.debug("receiver_requested (ignored)")
@ -81,10 +86,10 @@ class Connection(object):
if properties.get("trace"): if properties.get("trace"):
self._pn_transport.trace(proton.Transport.TRACE_FRM) self._pn_transport.trace(proton.Transport.TRACE_FRM)
self._sender_links = {} # indexed by link-name
self._receiver_links = {} self._sender_links = {} # SenderLink or pn_link if pending
self._pending_links = {} self._receiver_links = {} # ReceiverLink or pn_link if pending
self._pending_link_id = 0
self._read_done = False self._read_done = False
self._write_done = False self._write_done = False
self._next_tick = 0 self._next_tick = 0
@ -101,18 +106,25 @@ class Connection(object):
@property @property
# @todo - hopefully remove # @todo - hopefully remove
def transport(self): def pn_transport(self):
return self._pn_transport return self._pn_transport
@property @property
# @todo - hopefully remove # @todo - hopefully remove
def connection(self): def pn_connection(self):
return self._pn_connection return self._pn_connection
@property @property
def name(self): def name(self):
return self._name return self._name
@property
def remote_container(self):
"""Return the name of the remote container. Should be present once the
connection is active.
"""
return self._pn_connection.remote_container
@property @property
# @todo - think about server side use of sasl! # @todo - think about server side use of sasl!
def sasl(self): def sasl(self):
@ -131,9 +143,73 @@ class Connection(object):
Associate an arbitrary user object with this Connection. Associate an arbitrary user object with this Connection.
""") """)
_NEED_INIT = proton.Endpoint.LOCAL_UNINIT def open(self):
_NEED_CLOSE = (proton.Endpoint.LOCAL_ACTIVE|proton.Endpoint.REMOTE_CLOSED) """
"""
self._pn_connection.open()
self._pn_session = self._pn_connection.session()
self._pn_session.open()
def close(self, error=None):
"""
"""
for l in self._sender_links.itervalues():
l.close(error)
for l in self._receiver_links.itervalues():
l.close(error)
self._pn_session.close()
self._pn_connection.close()
@property
def closed(self):
#return self._write_done and self._read_done
state = self._pn_connection.state
return state == (proton.Endpoint.LOCAL_CLOSED
| proton.Endpoint.REMOTE_CLOSED)
def destroy(self):
"""
"""
self._sender_links.clear()
self._receiver_links.clear()
self._container._remove_connection(self._name)
self._container = None
self._pn_connection = None
self._pn_transport = None
self._user_context = None
def _link_requested(self, pn_link):
if pn_link.is_sender and pn_link.name not in self._sender_links:
LOG.debug("Remotely initiated Sender needs init")
self._sender_links[pn_link.name] = pn_link
pn_link.context = None # @todo: update proton.py
req_source = ""
if pn_link.remote_source.dynamic:
req_source = None
elif pn_link.remote_source.address:
req_source = pn_link.remote_source.address
self._handler.sender_requested(self, pn_link.name,
pn_link.name, req_source,
{"target-address":
pn_link.remote_target.address})
elif pn_link.is_receiver and pn_link.name not in self._receiver_links:
LOG.debug("Remotely initiated Receiver needs init")
self._receiver_links[pn_link.name] = pn_link
pn_link.context = None # @todo: update proton.py
req_target = ""
if pn_link.remote_target.dynamic:
req_target = None
elif pn_link.remote_target.address:
req_target = pn_link.remote_target.address
self._handler.receiver_requested(self, pn_link.name,
pn_link.name, req_target,
{"source-address":
pn_link.remote_source.address})
_REMOTE_REQ = (proton.Endpoint.LOCAL_UNINIT|proton.Endpoint.REMOTE_ACTIVE)
_REMOTE_CLOSE = (proton.Endpoint.LOCAL_ACTIVE|proton.Endpoint.REMOTE_CLOSED)
_ACTIVE = (proton.Endpoint.LOCAL_ACTIVE|proton.Endpoint.REMOTE_ACTIVE) _ACTIVE = (proton.Endpoint.LOCAL_ACTIVE|proton.Endpoint.REMOTE_ACTIVE)
_CLOSED = (proton.Endpoint.LOCAL_CLOSED|proton.Endpoint.REMOTE_CLOSED)
def process(self, now): def process(self, now):
""" """
@ -152,103 +228,112 @@ Associate an arbitrary user object with this Connection.
self._handler.sasl_done(self, self._pn_sasl.outcome) self._handler.sasl_done(self, self._pn_sasl.outcome)
self._pn_sasl = None self._pn_sasl = None
if self._pn_connection.state & self._NEED_INIT: # do endpoint up handling:
assert False, "Connection always opened() on create"
if not self._active: if self._pn_connection.state == self._ACTIVE:
if self._pn_connection.state == self._ACTIVE: if not self._active:
self._active = True self._active = True
self._handler.connection_active(self) self._handler.connection_active(self)
ssn = self._pn_connection.session_head(self._NEED_INIT) pn_session = self._pn_connection.session_head(proton.Endpoint.LOCAL_UNINIT)
while ssn: while pn_session:
LOG.debug("Opening remotely initiated session") LOG.debug("Opening remotely initiated session")
ssn.open() pn_session.open()
ssn = ssn.next(self._NEED_INIT) pn_session = pn_session.next(proton.Endpoint.LOCAL_UNINIT)
link = self._pn_connection.link_head(self._NEED_INIT) pn_link = self._pn_connection.link_head(self._REMOTE_REQ)
while link: while pn_link:
LOG.debug("Remotely initiated Link needs init") next_link = pn_link.next(proton.Endpoint.LOCAL_UNINIT)
index = self._pending_link_id
self._pending_link_id += 1
assert index not in self._pending_links
self._pending_links[index] = link
if link.is_sender:
req_source = ""
if link.remote_source.dynamic:
req_source = None
elif link.remote_source.address:
req_source = link.remote_source.address
self._handler.sender_requested(self, index, req_source,
{"target-address":
link.remote_target.address})
else:
req_target = ""
if link.remote_target.dynamic:
req_target = None
elif link.remote_target.address:
req_target = link.remote_target.address
self._handler.receiver_requested(self, index, req_target,
{"source-address":
link.remote_source.address})
link = link.next(self._NEED_INIT)
# @todo: won't scale if pn_link.state == self._REMOTE_REQ:
link = self._pn_connection.link_head(self._ACTIVE) self._link_requested(pn_link)
while link:
if link.context and not link.context._active: pn_link = next_link
if link.is_sender:
sender_link = link.context # @todo: won't scale?
pn_link = self._pn_connection.link_head(self._ACTIVE)
while pn_link:
next_link = pn_link.next(self._ACTIVE)
if pn_link.context and not pn_link.context._active:
LOG.debug("Link is up")
pn_link.context._active = True
if pn_link.is_sender:
sender_link = pn_link.context
assert isinstance(sender_link, SenderLink)
sender_link._handler.sender_active(sender_link) sender_link._handler.sender_active(sender_link)
else: else:
receiver_link = link.context receiver_link = pn_link.context
assert isinstance(receiver_link, ReceiverLink)
receiver_link._handler.receiver_active(receiver_link) receiver_link._handler.receiver_active(receiver_link)
link.context._active = True pn_link = next_link
link = link.next(self._ACTIVE)
# process the work queue # process the work queue
delivery = self._pn_connection.work_head pn_delivery = self._pn_connection.work_head
while delivery: while pn_delivery:
LOG.debug("Delivery updated!") LOG.debug("Delivery updated!")
if delivery.link.context: next_delivery = pn_delivery.work_next
if delivery.link.is_sender: if pn_delivery.link.context:
sender_link = delivery.link.context if pn_delivery.link.is_sender:
sender_link._delivery_updated(delivery) sender_link = pn_delivery.link.context
sender_link._delivery_updated(pn_delivery)
else: else:
receiver_link = delivery.link.context receiver_link = pn_delivery.link.context
receiver_link._delivery_updated(delivery) receiver_link._delivery_updated(pn_delivery)
delivery = delivery.work_next pn_delivery = next_delivery
# close all endpoints closed by remotes # do endpoint down handling:
link = self._pn_connection.link_head(self._NEED_CLOSE) pn_link = self._pn_connection.link_head(self._REMOTE_CLOSE)
while link: while pn_link:
LOG.debug("Link closed remotely") LOG.debug("Link closed remotely")
link.close() next_link = pn_link.next(self._REMOTE_CLOSE)
# @todo: error reporting # @todo: error reporting
if link.context: if pn_link.context:
if link.is_sender: if pn_link.is_sender:
sender_link = link.context sender_link = pn_link.context
sender_link._handler.sender_closed(sender_link, None) sender_link._handler.sender_remote_closed(sender_link, None)
else: else:
receiver_link = link.context receiver_link = pn_link.context
receiver_link._handler.receiver_closed(receiver_link, receiver_link._handler.receiver_remote_closed(receiver_link,
None) None)
link = link.next(self._NEED_CLOSE) pn_link = next_link
ssn = self._pn_connection.session_head(self._NEED_CLOSE) pn_link = self._pn_connection.link_head(self._CLOSED)
while ssn: while pn_link:
next_link = pn_link.next(self._CLOSED)
if pn_link.context and pn_link.context._active:
LOG.debug("Link close completed")
pn_link.context._active = False
if pn_link.is_sender:
sender_link = pn_link.context
sender_link._handler.sender_closed(sender_link)
else:
receiver_link = pn_link.context
receiver_link._handler.receiver_closed(receiver_link)
pn_link = next_link
pn_session = self._pn_connection.session_head(self._REMOTE_CLOSE)
while pn_session:
LOG.debug("Session closed remotely") LOG.debug("Session closed remotely")
ssn.close() pn_session.close()
ssn = ssn.next(self._NEED_CLOSE) pn_session = pn_session.next(self._REMOTE_CLOSE)
if self._pn_connection.state == (self._NEED_CLOSE): if self._pn_connection.state == self._REMOTE_CLOSE:
LOG.debug("Connection remotely closed") LOG.debug("Connection remotely closed")
# @todo - think about handling this wrt links! self._handler.connection_remote_closed(self, None)
cond = self._pn_connection.remote_condition elif self._pn_connection.state == self._CLOSED:
self._pn_connection.close() LOG.debug("Connection close complete")
self._handler.connection_closed(self, cond) self._handler.connection_closed(self)
# DEBUG LINK "LEAK"
# count = 0
# link = self._pn_connection.link_head(0)
# while link:
# count += 1
# link = link.next(0)
# print "Link Count %d" % count
return self._next_tick return self._next_tick
@ -328,7 +413,7 @@ Associate an arbitrary user object with this Connection.
pn_link = self._pn_session.sender(ident) pn_link = self._pn_session.sender(ident)
if pn_link: if pn_link:
s = SenderLink(self, pn_link, ident, s = SenderLink(self, pn_link,
source_address, target_address, source_address, target_address,
eventHandler, properties) eventHandler, properties)
self._sender_links[ident] = s self._sender_links[ident] = s
@ -336,30 +421,29 @@ Associate an arbitrary user object with this Connection.
return None return None
def accept_sender(self, link_handle, source_override=None, def accept_sender(self, link_handle, source_override=None,
event_handler=None, name=None, properties={}): event_handler=None, properties={}):
pn_link = self._pending_links.pop(link_handle) pn_link = self._sender_links.get(link_handle)
if not pn_link: if not pn_link or not isinstance(pn_link, proton.Sender):
raise Exception("Invalid link_handle: %s" % link_handle) raise Exception("Invalid link_handle: %s" % link_handle)
if pn_link.remote_source.dynamic and not source_override: if pn_link.remote_source.dynamic and not source_override:
raise Exception("A source address must be supplied!") raise Exception("A source address must be supplied!")
source_addr = source_override or pn_link.remote_source.address source_addr = source_override or pn_link.remote_source.address
name = name or source_addr self._sender_links[link_handle] = SenderLink(self, pn_link,
if name in self._sender_links: source_addr,
raise KeyError("Sender %s already exists!" % name) pn_link.remote_target.address,
self._sender_links[name] = SenderLink(self, pn_link, name, event_handler, properties)
source_addr, return self._sender_links[link_handle]
pn_link.remote_target.address,
event_handler, properties)
return self._sender_links[name]
def reject_sender(self, link_handle, reason): def reject_sender(self, link_handle, reason):
pn_link = self._pending_links.pop(link_handle) pn_link = self._sender_links.get(link_handle)
if pn_link: if not pn_link or not isinstance(pn_link, proton.Sender):
# @todo support reason for close raise Exception("Invalid link_handle: %s" % link_handle)
pn_link.close() del self._sender_links[link_handle]
# @todo support reason for close
pn_link.close()
def create_receiver(self, target_address, source_address, def create_receiver(self, target_address, source_address=None,
eventHandler, name=None, properties={}): eventHandler=None, name=None, properties={}):
"""Factory for Receive links""" """Factory for Receive links"""
ident = name or str(target_address) ident = name or str(target_address)
if ident in self._receiver_links: if ident in self._receiver_links:
@ -367,73 +451,34 @@ Associate an arbitrary user object with this Connection.
pn_link = self._pn_session.receiver(ident) pn_link = self._pn_session.receiver(ident)
if pn_link: if pn_link:
r = ReceiverLink(self, pn_link, ident, target_address, r = ReceiverLink(self, pn_link, target_address,
source_address, eventHandler, properties) source_address, eventHandler, properties)
if r: self._receiver_links[ident] = r
self._receiver_links[ident] = r return r
return r
return None return None
def accept_receiver(self, link_handle, target_override=None, def accept_receiver(self, link_handle, target_override=None,
event_handler=None, name=None, properties={}): event_handler=None, properties={}):
pn_link = self._pending_links.pop(link_handle) pn_link = self._receiver_links.get(link_handle)
if not pn_link: if not pn_link or not isinstance(pn_link, proton.Receiver):
raise Exception("Invalid link_handle: %s" % link_handle) raise Exception("Invalid link_handle: %s" % link_handle)
if pn_link.remote_target.dynamic and not target_override: if pn_link.remote_target.dynamic and not target_override:
raise Exception("A target address must be supplied!") raise Exception("A target address must be supplied!")
target_addr = target_override or pn_link.remote_target.address target_addr = target_override or pn_link.remote_target.address
name = name or target_addr self._receiver_links[link_handle] = ReceiverLink(self, pn_link,
if name in self._receiver_links: target_addr,
raise KeyError("Receiver %s already exists!" % name) pn_link.remote_source.address,
self._receiver_links[name] = ReceiverLink(self, pn_link, name, event_handler, properties)
target_addr, return self._receiver_links[link_handle]
pn_link.remote_source.address,
event_handler, properties)
return self._receiver_links[name]
pass
def reject_receiver(self, link_handle, reason): def reject_receiver(self, link_handle, reason):
pn_link = self._pending_links.pop(link_handle) pn_link = self._receiver_links.get(link_handle)
if pn_link: if not pn_link or not isinstance(pn_link, proton.Receiver):
# @todo support reason for close raise Exception("Invalid link_handle: %s" % link_handle)
pn_link.close() del self._receiver_links[link_handle]
# @todo support reason for close
pn_link.close()
def open(self):
"""
"""
self._pn_connection.open()
self._pn_session = self._pn_connection.session()
self._pn_session.open()
def close(self, error=None):
"""
"""
for l in self._sender_links.itervalues():
l.close(error)
for l in self._receiver_links.itervalues():
l.close(error)
self._pn_session.close()
self._pn_connection.close()
@property
def closed(self):
#return self._write_done and self._read_done
state = self._pn_connection.state
return state == (proton.Endpoint.LOCAL_CLOSED
| proton.Endpoint.REMOTE_CLOSED)
def destroy(self):
"""
"""
self._pending_links.clear()
self._sender_links.clear()
self._receiver_links.clear()
self._container._remove_connection(self._name)
self._container = None
self._pn_connection = None
self._pn_transport = None
self._user_context = None
def _remove_sender(self, name): def _remove_sender(self, name):
if name in self._sender_links: if name in self._sender_links:

@ -24,15 +24,17 @@ LOG = logging.getLogger(__name__)
class _Link(object): class _Link(object):
"""Generic Link base class""" """Generic Link base class"""
def __init__(self, connection, pn_link, name, def __init__(self, connection, pn_link,
target_address, source_address, target_address, source_address,
handler, properties): handler, properties):
self._connection = connection self._connection = connection
self._name = name self._name = pn_link.name
self._handler = handler self._handler = handler
self._properties = properties self._properties = properties
self._user_context = None
self._active = False
# @todo: raise jira to add 'context' attr to api
self._pn_link = pn_link self._pn_link = pn_link
# @todo: raise jira to add 'context' to api
pn_link.context = self pn_link.context = self
if target_address is None: if target_address is None:
@ -58,8 +60,10 @@ class _Link(object):
else: else:
raise Exception("Unknown distribution mode: %s" % raise Exception("Unknown distribution mode: %s" %
str(desired_mode)) str(desired_mode))
self._user_context = None
self._active = False @property
def name(self):
return self._name
def open(self): def open(self):
""" """
@ -107,18 +111,21 @@ Associate an arbitrary application object with this link.
| proton.Endpoint.REMOTE_CLOSED) | proton.Endpoint.REMOTE_CLOSED)
def destroy(self): def destroy(self):
LOG.debug("link destroyed %s" % str(self._pn_link))
self._user_context = None self._user_context = None
self._pn_link.context = None self._pn_link.context = None
self._pn_link = None self._pn_link = None
class SenderEventHandler(object): class SenderEventHandler(object):
""" """
""" """
def sender_active(self, sender_link): def sender_active(self, sender_link):
LOG.debug("sender_active (ignored)") LOG.debug("sender_active (ignored)")
def sender_closed(self, sender_link, error=None): def sender_remote_closed(self, sender_link, error=None):
LOG.debug("sender_remote_closed (ignored)")
def sender_closed(self, sender_link):
LOG.debug("sender_closed (ignored)") LOG.debug("sender_closed (ignored)")
@ -134,9 +141,9 @@ class SenderLink(_Link):
RELEASED = 3 RELEASED = 3
MODIFIED = 4 MODIFIED = 4
def __init__(self, connection, pn_link, name, source_address, def __init__(self, connection, pn_link, source_address,
target_address, eventHandler, properties): target_address, eventHandler, properties):
super(SenderLink, self).__init__(connection, pn_link, name, super(SenderLink, self).__init__(connection, pn_link,
target_address, source_address, target_address, source_address,
eventHandler, properties) eventHandler, properties)
self._pending_sends = collections.deque() self._pending_sends = collections.deque()
@ -242,7 +249,10 @@ class ReceiverEventHandler(object):
def receiver_active(self, receiver_link): def receiver_active(self, receiver_link):
LOG.debug("receiver_active (ignored)") LOG.debug("receiver_active (ignored)")
def receiver_closed(self, receiver_link, error=None): def receiver_remote_closed(self, receiver_link, error=None):
LOG.debug("receiver_remote_closed (ignored)")
def receiver_closed(self, receiver_link):
LOG.debug("receiver_closed (ignored)") LOG.debug("receiver_closed (ignored)")
def message_received(self, receiver_link, message, handle): def message_received(self, receiver_link, message, handle):
@ -250,9 +260,9 @@ class ReceiverEventHandler(object):
class ReceiverLink(_Link): class ReceiverLink(_Link):
def __init__(self, connection, pn_link, name, target_address, def __init__(self, connection, pn_link, target_address,
source_address, eventHandler, properties): source_address, eventHandler, properties):
super(ReceiverLink, self).__init__(connection, pn_link, name, super(ReceiverLink, self).__init__(connection, pn_link,
target_address, source_address, target_address, source_address,
eventHandler, properties) eventHandler, properties)
self._next_handle = 0 self._next_handle = 0

@ -26,7 +26,7 @@ LOG = logging.getLogger(__name__)
processing. processing.
""" """
def read_socket_input(connection, socket): def read_socket_input(connection, socket_obj):
"""Read from the network layer and processes all data read. Can """Read from the network layer and processes all data read. Can
support both blocking and non-blocking sockets. support both blocking and non-blocking sockets.
Returns the number of input bytes processed, or EOS if input processing Returns the number of input bytes processed, or EOS if input processing
@ -37,7 +37,7 @@ def read_socket_input(connection, socket):
return count # 0 or EOS return count # 0 or EOS
try: try:
sock_data = socket.recv(count) sock_data = socket_obj.recv(count)
except socket.timeout, e: except socket.timeout, e:
LOG.debug("Socket timeout exception %s", str(e)) LOG.debug("Socket timeout exception %s", str(e))
raise # caller must handle raise # caller must handle
@ -64,7 +64,7 @@ def read_socket_input(connection, socket):
connection.close_input() connection.close_input()
return count return count
def write_socket_output(connection, socket): def write_socket_output(connection, socket_obj):
"""Write data to the network layer. Can support both blocking and """Write data to the network layer. Can support both blocking and
non-blocking sockets. non-blocking sockets.
""" """
@ -74,7 +74,7 @@ def write_socket_output(connection, socket):
data = connection.output_data() data = connection.output_data()
try: try:
count = socket.send(data) count = socket_obj.send(data)
except socket.timeout, e: except socket.timeout, e:
LOG.debug("Socket timeout exception %s", str(e)) LOG.debug("Socket timeout exception %s", str(e))
raise # caller must handle raise # caller must handle