checkpoint
This commit is contained in:
parent
ac6d1c7fd3
commit
623b1af3ab
4
.gitignore
vendored
4
.gitignore
vendored
@ -34,3 +34,7 @@ nosetests.xml
|
||||
.mr.developer.cfg
|
||||
.project
|
||||
.pydevproject
|
||||
|
||||
# emacs crap
|
||||
*~
|
||||
|
||||
|
@ -36,44 +36,196 @@ to a server, and waits for a response. The method call is a map of the form:
|
||||
"""
|
||||
|
||||
|
||||
class MyConnectionEventHandler(fusion_connection.ConnectionEventHandler):
|
||||
class MyConnection(fusion_connection.ConnectionEventHandler):
|
||||
|
||||
def __init__(self, name, socket, container, properties):
|
||||
self.name = name
|
||||
self.socket = socket
|
||||
self.connection = container.create_connection(name, self,
|
||||
properties)
|
||||
self.connection.user_context = self
|
||||
|
||||
def process(self):
|
||||
""" Do connection-based processing (I/O and timers) """
|
||||
readfd = []
|
||||
writefd = []
|
||||
if self.connection.needs_input > 0:
|
||||
readfd = [self.socket]
|
||||
if self.connection.has_output > 0:
|
||||
writefd = [self.socket]
|
||||
|
||||
timeout = None
|
||||
deadline = self.connection.next_tick
|
||||
if deadline:
|
||||
now = time.time()
|
||||
timeout = 0 if deadline <= now else deadline - now
|
||||
|
||||
print("select start (t=%s)" % str(timeout))
|
||||
readable,writable,ignore = select.select(readfd,writefd,[],timeout)
|
||||
print("select return")
|
||||
|
||||
if readable:
|
||||
count = self.connection.needs_input
|
||||
if count > 0:
|
||||
try:
|
||||
sock_data = self.socket.recv(count)
|
||||
if sock_data:
|
||||
self.connection.process_input( sock_data )
|
||||
else:
|
||||
# closed?
|
||||
self.connection.close_input()
|
||||
except socket.timeout, e:
|
||||
raise # I don't expect this
|
||||
except socket.error, e:
|
||||
err = e.args[0]
|
||||
# ignore non-fatal errors
|
||||
if (err != errno.EAGAIN and
|
||||
err != errno.EWOULDBLOCK and
|
||||
err != errno.EINTR):
|
||||
# otherwise, unrecoverable:
|
||||
self.connection.close_input()
|
||||
raise
|
||||
except: # beats me...
|
||||
self.connection.close_input()
|
||||
raise
|
||||
|
||||
if writable:
|
||||
data = self.connection.output_data()
|
||||
if data:
|
||||
try:
|
||||
rc = self.socket.send(data)
|
||||
if rc > 0:
|
||||
self.connection.output_written(rc)
|
||||
else:
|
||||
# else socket closed
|
||||
self.connection.close_output()
|
||||
except socket.timeout, e:
|
||||
raise # I don't expect this
|
||||
except socket.error, e:
|
||||
err = e.args[0]
|
||||
# ignore non-fatal errors
|
||||
if (err != errno.EAGAIN and
|
||||
err != errno.EWOULDBLOCK and
|
||||
err != errno.EINTR):
|
||||
# otherwise, unrecoverable
|
||||
self.connection.close_output()
|
||||
raise
|
||||
except: # beats me...
|
||||
self.connection.close_output()
|
||||
raise
|
||||
|
||||
self.connection.process(time.time())
|
||||
|
||||
def destroy(self, error=None):
|
||||
self.connection.user_context = None
|
||||
self.connection.destroy()
|
||||
self.connection = None
|
||||
self.socket.close()
|
||||
self.socket = None
|
||||
|
||||
# Connection callbacks:
|
||||
|
||||
def connection_active(self, connection):
|
||||
"""connection handshake completed"""
|
||||
print "APP: CONN ACTIVE"
|
||||
|
||||
def connection_closed(self, connection, reason):
|
||||
print "APP: CONN CLOSED"
|
||||
|
||||
def sender_requested(self, connection, link_handle,
|
||||
requested_source, properties={}):
|
||||
# call accept_sender to accept new link,
|
||||
# reject_sender to reject it.
|
||||
assert False, "Not expected"
|
||||
|
||||
def receiver_requested(self, connection, link_handle,
|
||||
requested_target, properties={}):
|
||||
# call accept_sender to accept new link,
|
||||
# reject_sender to reject it.
|
||||
assert False, "Not expected"
|
||||
|
||||
def sasl_done(self, connection, result):
|
||||
print "APP: SASL DONE"
|
||||
print result
|
||||
|
||||
def connection_closed(self, connection, reason):
|
||||
print "APP: CONN CLOSED"
|
||||
|
||||
def link_pending(self, connection, link):
|
||||
print "APP: LINK PENDING"
|
||||
return True
|
||||
|
||||
|
||||
class MySenderEventHandler(fusion_link.SenderEventHandler):
|
||||
class MyCaller(fusion_link.SenderEventHandler,
|
||||
fusion_link.ReceiverEventHandler):
|
||||
"""
|
||||
"""
|
||||
|
||||
def __init__(self, method_map, my_connection,
|
||||
my_source, my_target,
|
||||
receiver_properties, sender_properties):
|
||||
conn = my_connection.connection
|
||||
self._sender = conn.create_sender(my_source, target_address=None,
|
||||
eventHandler=self, name=my_source,
|
||||
properties=sender_properties)
|
||||
self._receiver = conn.create_receiver(my_target, source_address=None,
|
||||
eventHandler=self, name=my_target,
|
||||
properties=receiver_properties)
|
||||
self._method = method_map
|
||||
self._reply_to = None
|
||||
self._to = None
|
||||
self._receiver_closed = False
|
||||
self._sender_closed = False
|
||||
|
||||
def done(self):
|
||||
return self._receiver_closed and self._sender_closed
|
||||
|
||||
def _send_request(self):
|
||||
"""Send a message containing the RPC method call
|
||||
"""
|
||||
msg = Message()
|
||||
msg.subject = "An RPC call!"
|
||||
msg.address = self._to
|
||||
msg.reply_to = self._reply_to
|
||||
msg.body = self._method
|
||||
msg.correlation_id = 5 # whatever...
|
||||
|
||||
self._sender.send(msg, self, None, time.time() + 10)
|
||||
|
||||
# SenderEventHandler callbacks:
|
||||
|
||||
def sender_active(self, sender_link):
|
||||
print "APP: SENDER ACTIVE"
|
||||
assert sender_link is self._sender
|
||||
self._to = sender_link.target_address
|
||||
assert self._to, "Expected a target address!!!"
|
||||
if self._reply_to:
|
||||
self._send_request()
|
||||
|
||||
def sender_closed(self, sender_link, error):
|
||||
print "APP: SENDER CLOSED"
|
||||
self._sender_closed = True
|
||||
|
||||
# send complete callback:
|
||||
|
||||
class MyReceiverEventHandler(fusion_link.ReceiverEventHandler):
|
||||
def __call__(self, sender_link, handle, status, error):
|
||||
print "APP: MESSAGE SENT CALLBACK %s" % status
|
||||
|
||||
# ReceiverEventHandler callbacks:
|
||||
|
||||
def receiver_active(self, receiver_link):
|
||||
print "APP: RECEIVER ACTIVE"
|
||||
assert receiver_link is self._receiver
|
||||
self._reply_to = receiver_link.source_address
|
||||
assert self._reply_to, "Expected a source address!!!"
|
||||
if self._to:
|
||||
self._send_request()
|
||||
|
||||
def receiver_closed(self, receiver_link, error):
|
||||
print "APP: RECEIVER CLOSED"
|
||||
self._receiver_closed = True
|
||||
|
||||
def message_received(self, receiver_link, message, handle):
|
||||
print "APP: MESSAGE RECEIVED"
|
||||
print "Response received: %s" % str(message)
|
||||
|
||||
|
||||
def send_callback(sender, handle, status, error=None):
|
||||
print "APP: MESSAGE SENT CALLBACK"
|
||||
self._sender.destroy()
|
||||
del self._sender
|
||||
self._receiver.destroy()
|
||||
del self._receiver
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
@ -90,15 +242,7 @@ def main(argv=None):
|
||||
if not method_info:
|
||||
assert False, "No method info specified!"
|
||||
if len(method_info) % 2 != 1:
|
||||
assert False, "An even number of method arguments is required!"
|
||||
|
||||
|
||||
method = {'method': method_info[0],
|
||||
'args': dict([(method_info[i], method_info[i+1])
|
||||
for i in range(1, len(method_info), 2)])}
|
||||
|
||||
print "method=%s" % str(method)
|
||||
|
||||
assert False, "An even number of method arguments are required!"
|
||||
|
||||
# Create a socket connection to the server
|
||||
#
|
||||
@ -123,109 +267,26 @@ def main(argv=None):
|
||||
|
||||
# create AMQP container, connection, sender and receiver
|
||||
#
|
||||
conn_handler = MyConnectionEventHandler()
|
||||
send_handler = MySenderEventHandler()
|
||||
recv_handler = MyReceiverEventHandler()
|
||||
|
||||
container = fusion_container.Container(uuid.uuid4().hex)
|
||||
connection = container.create_connection("server",
|
||||
conn_handler)
|
||||
my_connection = MyConnection( "to-server", my_socket,
|
||||
container, {})
|
||||
|
||||
# @todo: need better sasl + server
|
||||
connection.sasl.mechanisms("ANONYMOUS")
|
||||
connection.sasl.client()
|
||||
my_connection.connection.sasl.mechanisms("ANONYMOUS")
|
||||
my_connection.connection.sasl.client()
|
||||
|
||||
sender = connection.create_sender( "rpc-client-src",
|
||||
"rpc-server-tgt",
|
||||
send_handler )
|
||||
# Create the RPC caller
|
||||
method = {'method': method_info[0],
|
||||
'args': dict([(method_info[i], method_info[i+1])
|
||||
for i in range(1, len(method_info), 2)])}
|
||||
my_caller = MyCaller( method,
|
||||
my_connection,
|
||||
"my-source-address",
|
||||
"my-target-address",
|
||||
receiver_properties = {"capacity": 1})
|
||||
|
||||
receiver = connection.create_receiver( "rpc-client-tgt",
|
||||
"rpc-server-src",
|
||||
recv_handler )
|
||||
|
||||
# send a message containing the RPC method call
|
||||
#
|
||||
msg = Message()
|
||||
msg.address = "rpc-server-address"
|
||||
msg.subject = "An RPC call!"
|
||||
msg.reply_to = "rpc-client-tgt"
|
||||
msg.body = method
|
||||
|
||||
sender.send( msg, send_callback, "my-handle", opts.timeout )
|
||||
|
||||
while not connection.closed:
|
||||
|
||||
#
|
||||
# Poll for I/O & timers
|
||||
#
|
||||
|
||||
readfd = []
|
||||
writefd = []
|
||||
if connection.needs_input > 0:
|
||||
readfd = [my_socket]
|
||||
if connection.has_output > 0:
|
||||
writefd = [my_socket]
|
||||
|
||||
timeout = None
|
||||
deadline = connection.next_tick
|
||||
if deadline:
|
||||
now = time.time()
|
||||
timeout = 0 if deadline <= now else deadline - now
|
||||
|
||||
print("select start (t=%s)" % str(timeout))
|
||||
readable,writable,ignore = select.select(readfd,writefd,[],timeout)
|
||||
print("select return")
|
||||
|
||||
if readable:
|
||||
count = connection.needs_input
|
||||
if count > 0:
|
||||
try:
|
||||
sock_data = my_socket.recv(count)
|
||||
if sock_data:
|
||||
connection.process_input( sock_data )
|
||||
else:
|
||||
# closed?
|
||||
connection.close_input()
|
||||
except socket.timeout, e:
|
||||
raise # I don't expect this
|
||||
except socket.error, e:
|
||||
err = e.args[0]
|
||||
# ignore non-fatal errors
|
||||
if (err != errno.EAGAIN and
|
||||
err != errno.EWOULDBLOCK and
|
||||
err != errno.EINTR):
|
||||
# otherwise, unrecoverable:
|
||||
connection.close_input()
|
||||
raise
|
||||
except: # beats me...
|
||||
connection.close_input()
|
||||
raise
|
||||
|
||||
if writable:
|
||||
data = connection.output_data()
|
||||
if data:
|
||||
try:
|
||||
rc = my_socket.send(data)
|
||||
if rc > 0:
|
||||
connection.output_written(rc)
|
||||
else:
|
||||
# else socket closed
|
||||
connection.close_output()
|
||||
except socket.timeout, e:
|
||||
raise # I don't expect this
|
||||
except socket.error, e:
|
||||
err = e.args[0]
|
||||
# ignore non-fatal errors
|
||||
if (err != errno.EAGAIN and
|
||||
err != errno.EWOULDBLOCK and
|
||||
err != errno.EINTR):
|
||||
# otherwise, unrecoverable
|
||||
connection.close_output()
|
||||
raise
|
||||
except: # beats me...
|
||||
connection.close_output()
|
||||
raise
|
||||
|
||||
connection.process(time.time())
|
||||
while not my_caller.done():
|
||||
my_connection.process()
|
||||
|
||||
print "DONE"
|
||||
|
||||
|
@ -39,6 +39,11 @@ map sent in the request.
|
||||
"""
|
||||
|
||||
|
||||
# Maps of outgoing and incoming links
|
||||
sender_links = {} # indexed by Source address
|
||||
receiver_links = {} # indexed by Target address
|
||||
|
||||
|
||||
class SocketConnection(fusion_connection.ConnectionEventHandler):
|
||||
"""Associates a fusion Connection with a python network socket"""
|
||||
|
||||
@ -61,9 +66,43 @@ class SocketConnection(fusion_connection.ConnectionEventHandler):
|
||||
def connection_closed(self, connection, reason):
|
||||
print "APP: CONN CLOSED"
|
||||
|
||||
def link_pending(self, connection, link):
|
||||
print "APP: LINK PENDING"
|
||||
return True
|
||||
def sender_requested(self, connection, link_handle,
|
||||
requested_source, properties):
|
||||
print "APP: SENDER LINK REQUESTED"
|
||||
global sender_links
|
||||
|
||||
name = uuid.uuid4().hex
|
||||
# allow for requested_source address if it doesn't conflict with an
|
||||
# existing address
|
||||
if not requested_source or requested_source in sender_links:
|
||||
requested_source = "/%s/%s" % (connection.container.name,
|
||||
name)
|
||||
assert requested_source not in sender_links
|
||||
|
||||
sender = MySenderLink(connection, link_handle, requested_source,
|
||||
name, {})
|
||||
sender_links[requested_source] = sender
|
||||
print "APP: NEW SENDER LINK CREATED, source=%s" % requested_source
|
||||
|
||||
def receiver_requested(self, connection, link_handle,
|
||||
requested_target, properties):
|
||||
print "APP: RECEIVER LINK REQUESTED"
|
||||
# allow for requested_source address if it doesn't conflict with an
|
||||
# existing address
|
||||
global receiver_links
|
||||
|
||||
name = uuid.uuid4().hex
|
||||
if not requested_target or requested_target in receiver_links:
|
||||
requested_target = "/%s/%s" % (connection.container.name,
|
||||
name)
|
||||
assert requested_target not in receiver_links
|
||||
|
||||
receiver = MyReceiverLink(connection, link_handle,
|
||||
requested_target, name,
|
||||
{"capacity": 3})
|
||||
receiver_links[requested_target] = receiver
|
||||
print "APP: NEW RECEIVER LINK CREATED, target=%s" % requested_target
|
||||
|
||||
|
||||
def sasl_done(self, connection, result):
|
||||
print "APP: SASL DONE"
|
||||
@ -74,30 +113,38 @@ class MySenderLink(fusion_link.SenderEventHandler):
|
||||
"""
|
||||
"""
|
||||
def __init__(self, connection, link_handle, source_address,
|
||||
name):
|
||||
name, properties):
|
||||
|
||||
self.sender_link = connection.accept_sender(link_handle,
|
||||
source_address,
|
||||
self,
|
||||
name)
|
||||
name,
|
||||
properties)
|
||||
|
||||
# SenderEventHandler callbacks:
|
||||
|
||||
def sender_active(self, sender_link):
|
||||
print "APP: SENDER ACTIVE"
|
||||
|
||||
def sender_closed(self, sender_link, error):
|
||||
print "APP: SENDER CLOSED"
|
||||
|
||||
# 'message sent' callback:
|
||||
|
||||
def __call__(self, sender, handle, status, error=None):
|
||||
print "APP: MESSAGE SENT CALLBACK %s" % status
|
||||
|
||||
|
||||
class MyReceiverLink(fusion_link.ReceiverEventHandler):
|
||||
"""
|
||||
"""
|
||||
def __init__(self, connection, link_handle, target_address,
|
||||
name):
|
||||
self.receiver_link = connection.accept_receiver(link_handle,
|
||||
target_address,
|
||||
self,
|
||||
name)
|
||||
name, properties):
|
||||
self._link = connection.accept_receiver(link_handle,
|
||||
target_address,
|
||||
self,
|
||||
name,
|
||||
properties)
|
||||
|
||||
# ReceiverEventHandler callbacks:
|
||||
def receiver_active(self, receiver_link):
|
||||
@ -109,9 +156,33 @@ class MyReceiverLink(fusion_link.ReceiverEventHandler):
|
||||
def message_received(self, receiver_link, message, handle):
|
||||
print "APP: MESSAGE RECEIVED"
|
||||
|
||||
global sender_links
|
||||
|
||||
def send_callback(sender, handle, status, error=None):
|
||||
print "APP: MESSAGE SENT CALLBACK"
|
||||
# extract to reply-to, correlation id
|
||||
reply_to = message.reply_to
|
||||
if not reply_to or reply_to not in sender_links:
|
||||
self._link.message_rejected(handle, "Bad reply-to address")
|
||||
else:
|
||||
my_sender = sender_links[reply_to]
|
||||
correlation_id = message.correlation_id
|
||||
method_map = message.body
|
||||
if (not isinstance(method_map, dict) or
|
||||
'method' not in method_map):
|
||||
self._link.message_rejected(handle, "Bad format")
|
||||
else:
|
||||
print "Echoing back message map"
|
||||
response = Message()
|
||||
response.address = reply_to
|
||||
response.subject = message.subject
|
||||
response.correlation_id = correlation_id
|
||||
response.body = {"response": method_map}
|
||||
|
||||
link = my_sender.sender_link
|
||||
link.send( response, my_sender,
|
||||
message, time.time() + 5.0)
|
||||
|
||||
if self._link.capacity == 0:
|
||||
self._link.add_capacity( 5 )
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
@ -125,7 +196,7 @@ def main(argv=None):
|
||||
|
||||
opts, arguments = parser.parse_args(args=argv)
|
||||
|
||||
# Create a socket connection to the server
|
||||
# Create a socket for inbound connections
|
||||
#
|
||||
regex = re.compile(r"^amqp://([a-zA-Z0-9.]+)(:([\d]+))?$")
|
||||
print("Listening on %s" % opts.address)
|
||||
@ -162,7 +233,7 @@ def main(argv=None):
|
||||
writefd = []
|
||||
readers,writers,timers = container.need_processing()
|
||||
|
||||
# map fusion Connections to my SocketConnections
|
||||
# map fusion Connections back to my SocketConnections
|
||||
for c in readers:
|
||||
sc = c.user_context
|
||||
assert sc and isinstance(sc, SocketConnection)
|
||||
@ -174,7 +245,7 @@ def main(argv=None):
|
||||
|
||||
timeout = None
|
||||
if timers:
|
||||
deadline = timers[0].next_tick
|
||||
deadline = timers[0].next_tick # 0 == next expiring timer
|
||||
now = time.time()
|
||||
timeout = 0 if deadline <= now else deadline - now
|
||||
|
||||
@ -251,10 +322,6 @@ def main(argv=None):
|
||||
raise
|
||||
w.connection.process(time.time())
|
||||
|
||||
print "How to close connections that are finished???"
|
||||
|
||||
# Send replies
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
|
@ -34,20 +34,20 @@ class ConnectionEventHandler(object):
|
||||
def connection_closed(self, connection, reason):
|
||||
pass
|
||||
|
||||
def sender_pending(self, connection, link_handle,
|
||||
requested_source, properties={}):
|
||||
def sender_requested(self, connection, link_handle,
|
||||
requested_source, properties={}):
|
||||
# call accept_sender to accept new link,
|
||||
# reject_sender to reject it.
|
||||
pass
|
||||
|
||||
def receiver_pending(self, connection, link_handle,
|
||||
requested_target, properties={}):
|
||||
def receiver_requested(self, connection, link_handle,
|
||||
requested_target, properties={}):
|
||||
# call accept_sender to accept new link,
|
||||
# reject_sender to reject it.
|
||||
pass
|
||||
|
||||
# @todo cleaner sasl support, esp. server side
|
||||
def sasl_done(self, connection, result):
|
||||
# @todo sasl server support
|
||||
pass
|
||||
|
||||
|
||||
@ -91,6 +91,10 @@ class Connection(object):
|
||||
self._pn_session = self._pn_connection.session()
|
||||
self._pn_session.open()
|
||||
|
||||
@property
|
||||
def container(self):
|
||||
return self._container
|
||||
|
||||
@property
|
||||
# @todo - hopefully remove
|
||||
def transport(self):
|
||||
@ -148,8 +152,8 @@ Associate an arbitrary user object with this Connection.
|
||||
|
||||
if not self._active:
|
||||
if self._pn_connection.state == self._ACTIVE:
|
||||
self._active = False
|
||||
self._handler.connection_active(self, connection)
|
||||
self._active = True
|
||||
self._handler.connection_active(self)
|
||||
|
||||
ssn = self._pn_connection.session_head(self._NEED_INIT)
|
||||
while ssn:
|
||||
@ -328,7 +332,7 @@ Associate an arbitrary user object with this Connection.
|
||||
|
||||
def reject_sender(self, link_handle, reason):
|
||||
pn_link = self._pending_links.pop(link_handle)
|
||||
if link:
|
||||
if pn_link:
|
||||
# @todo support reason for close
|
||||
pn_link.close()
|
||||
|
||||
@ -369,7 +373,7 @@ Associate an arbitrary user object with this Connection.
|
||||
|
||||
def reject_receiver(self, link_handle, reason):
|
||||
pn_link = self._pending_links.pop(link_handle)
|
||||
if link:
|
||||
if pn_link:
|
||||
# @todo support reason for close
|
||||
pn_link.close()
|
||||
|
||||
|
@ -33,15 +33,17 @@ class _Link(object):
|
||||
# @todo: raise jira to add 'context' to api
|
||||
pn_link.context = self
|
||||
|
||||
if target_address:
|
||||
self._pn_link.target.address = target_address
|
||||
else:
|
||||
if target_address is None:
|
||||
assert pn_link.is_sender, "Dynamic target not allowed"
|
||||
self._pn_link.target.dynamic = True
|
||||
elif target_address:
|
||||
self._pn_link.target.address = target_address
|
||||
|
||||
if source_address:
|
||||
self._pn_link.source.address = source_address
|
||||
else:
|
||||
if source_address is None:
|
||||
assert pn_link.is_receiver, "Dynamic source not allowed"
|
||||
self._pn_link.source.dynamic = True
|
||||
elif source_address:
|
||||
self._pn_link.source.address = source_address
|
||||
|
||||
desired_mode = properties.get("distribution-mode")
|
||||
if desired_mode:
|
||||
@ -64,9 +66,29 @@ class _Link(object):
|
||||
|
||||
user_context = property(_get_user_context, _set_user_context,
|
||||
doc="""
|
||||
Associate an arbitrary applicaion object with this link.
|
||||
Associate an arbitrary application object with this link.
|
||||
""")
|
||||
|
||||
@property
|
||||
def source_address(self):
|
||||
"""If link is a sender, source is determined by the local value, else
|
||||
use the remote
|
||||
"""
|
||||
if self._pn_link.is_sender:
|
||||
return self._pn_link.source.address
|
||||
else:
|
||||
return self._pn_link.remote_source.address
|
||||
|
||||
@property
|
||||
def target_address(self):
|
||||
"""If link is a receiver, target is determined by the local value, else
|
||||
use the remote
|
||||
"""
|
||||
if self._pn_link.is_receiver:
|
||||
return self._pn_link.target.address
|
||||
else:
|
||||
return self._pn_link.remote_target.address
|
||||
|
||||
def _destroy(self):
|
||||
self._user_context = None
|
||||
self._pn_link.context = None
|
||||
|
Loading…
x
Reference in New Issue
Block a user