Skip to content
This repository has been archived by the owner on May 13, 2020. It is now read-only.

Commit

Permalink
New Features:
Browse files Browse the repository at this point in the history
- Connection objects have a new peer_address attribute, which is
  equivilent to calling ``getpeername()`` on sockets.

Bugs Fixed:

- Servers using unix-domain sockets didn't clean up socket files.

- When testing listeners were closed, handle_close, rather than close,
  was called on server connections.
  • Loading branch information
Jim Fulton committed Aug 18, 2010
1 parent 6cdb887 commit 4fcf78b
Show file tree
Hide file tree
Showing 10 changed files with 218 additions and 34 deletions.
16 changes: 16 additions & 0 deletions README.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@ To learn more, see http://packages.python.org/zc.ngi/
Changes
*******

====================
2.0.0a5 (2010-08-18)
====================

New Features:

- Connection objects have a new peer_address attribute, which is
equivilent to calling ``getpeername()`` on sockets.

Bugs Fixed:

- Servers using unix-domain sockets didn't clean up socket files.

- When testing listeners were closed, handle_close, rather than close,
was called on server connections.

====================
2.0.0a4 (2010-07-27)
====================
Expand Down
4 changes: 4 additions & 0 deletions src/zc/ngi/adapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ def handle_exception(self, connection, reason):
def handler(class_, func):
return zc.ngi.generator.handler(func, class_)

@property
def peer_address(self):
return self.connection.peer_address

class Lines(Base):

input = ''
Expand Down
7 changes: 7 additions & 0 deletions src/zc/ngi/async.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,9 @@ def writelines(self, data):
def close(self):
self._dispatcher.close_after_write()

@property
def peer_address(self):
return self._dispatcher.socket.getpeername()

class _ServerConnection(_Connection):
zc.ngi.interfaces.implements(zc.ngi.interfaces.IServerConnection)
Expand Down Expand Up @@ -569,6 +572,7 @@ def __init__(self, addr, handler, implementation, thready):
self.__close_handler = None
self._thready = thready
self.__connections = set()
self.address = addr
BaseListener.__init__(self, implementation)
if isinstance(addr, str):
family = socket.AF_UNIX
Expand Down Expand Up @@ -658,6 +662,9 @@ def closed(self, connection):

def _close(self, handler):
BaseListener.close(self)
if isinstance(self.address, str) and os.path.exists(self.address):
os.remove(self.address)

if handler is None:
for c in list(self.__connections):
c._dispatcher.handle_close("stopped")
Expand Down
2 changes: 2 additions & 0 deletions src/zc/ngi/doc/index.txt
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,7 @@ word-count server::
-> '2 3\n'

>>> listener.close()
-> CLOSE

We can also use adapters with generator-based handlers by passing an
adapter factory to ``zc.ngi.generator.handler`` using the
Expand All @@ -923,6 +924,7 @@ of the word count server using an adapter::
>>> connection.write('\nhello out\nthere')
-> '2 3\n'
>>> listener.close()
-> CLOSE

By separating the low-level protocol handling from the application
logic, we can reuse the low-level protocol in other applications, and
Expand Down
6 changes: 6 additions & 0 deletions src/zc/ngi/doc/reference.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ by applications.
.. autoclass:: IConnection
:members:

.. attribute:: peer_address

For server connections, the address of the client. For clients,
the server address. For socket-based implementations, this is
the result of calling ``getpeername()`` on the socket.

.. autoclass:: IImplementation
:members:

Expand Down
10 changes: 10 additions & 0 deletions src/zc/ngi/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,16 @@ def close():
any time.
"""

peer_address = Attribute(
"""The peer address
For socket-based connectionss, this is the result of calling
getpeername on the socket.
This is primarily interesting for servers that want to vary
behavior depending on where clients connect from.
""")

class IServerConnection(IConnection):
"""Server connection
Expand Down
8 changes: 4 additions & 4 deletions src/zc/ngi/old.test
Original file line number Diff line number Diff line change
Expand Up @@ -322,8 +322,8 @@ then all server connections are closed immediately and no more
connections are accepted:

>>> listener.close()
server closed: stopped
server closed: stopped
-> CLOSE
-> CLOSE

>>> connection = zc.ngi.testing.Connection()
>>> listener.connect(connection)
Expand Down Expand Up @@ -420,11 +420,11 @@ connect method that can be used to create connections to a server.
Let's connect our echo server and client. First, we'll create our
server using the listener constructor:

>>> listener = zc.ngi.testing.listener(EchoServer)
>>> listener = zc.ngi.testing.listener(('localhost', 42), EchoServer)

Then we'll use the connect method on the listener:

>>> client = EchoClient(listener.connect)
>>> client = EchoClient(zc.ngi.testing.connect)
>>> client.check(('localhost', 42), ['hello', 'world', 'how are you?'])
server connected
server got input: 'hello\n'
Expand Down
65 changes: 44 additions & 21 deletions src/zc/ngi/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,15 @@ class Connection:

zc.ngi.interfaces.implements(zc.ngi.interfaces.IConnection)

def __init__(self, peer=None, handler=PrintingHandler):
def __init__(self, peer=None, handler=PrintingHandler,
address=None, peer_address=None):
self.handler = None
self.address = address
self.handler_queue = []
self.control = None
self.closed = None
if peer is None:
peer = Connection(self)
peer = Connection(self, address=peer_address)
handler(peer)
self.peer = peer

Expand All @@ -74,15 +76,13 @@ def _callHandler(self, method, arg):

if self.queue is None:
self.queue = queue = [(method, arg)]
while queue:
while queue and not self.closed:
method, arg = queue.pop(0)

if method == 'handle_close':
if self.control is not None:
self.control.closed(self)
self.closed = arg
elif self.closed:
break

try:
try:
Expand All @@ -102,7 +102,7 @@ def _callHandler(self, method, arg):
raise

handler(self, arg)
except:
except Exception, v:
print "Error test connection calling connection handler:"
traceback.print_exc(file=sys.stdout)
if method != 'handle_close':
Expand All @@ -114,6 +114,8 @@ def _callHandler(self, method, arg):
self.queue.append((method, arg))

def close(self):
if self.closed:
return
self.peer.test_close('closed')
if self.control is not None:
self.control.closed(self)
Expand Down Expand Up @@ -161,9 +163,17 @@ def writelines(self, data):
except Exception, v:
self._exception(v)

@property
def peer_address(self):
return self.peer.address

class _ServerConnection(Connection):
zc.ngi.interfaces.implements(zc.ngi.interfaces.IServerConnection)

def __init__(self):
Connection.__init__(self, False) # False to avoid setting peer handler
self.peer = Connection(self)

class TextPrintingHandler(PrintingHandler):

def handle_input(self, connection, data):
Expand All @@ -174,13 +184,17 @@ def TextConnection(peer=None, handler=TextPrintingHandler):

_connectable = {}
_recursing = object()
def connect(addr, handler):
def connect(addr, handler, client_address=None):
connections = _connectable.get(addr)
if isinstance(connections, list):
if connections:
return handler.connected(connections.pop(0))
connection = connections.pop(0)
connection.peer.address = addr
connection.address = client_address
return handler.connected(connection)
elif isinstance(connections, listener):
return connections.connect(addr, handler=handler)
return connections.connect(handler=handler,
client_address=client_address)
elif connections is _recursing:
print (
"For address, %r, a connect handler called connect from a\n"
Expand All @@ -201,6 +215,7 @@ def connect(addr, handler):
def connectable(addr, connection):
_connectable.setdefault(addr, []).append(connection)


class listener:
zc.ngi.interfaces.implements(zc.ngi.interfaces.IListener)

Expand All @@ -215,22 +230,30 @@ def __init__(self, addr, handler=None):
self._close_handler = None
self._connections = []

def connect(self, connection=None, handler=None):
if handler is not None:
# connection is addr in this case and is ignored
handler.connected(Connection(None, self._handler))
return
def connect(self, connection=None, handler=None, client_address=None):
if self._handler is None:
raise TypeError("Listener closed")
if connection is None:

try: # Round about None (or legacy addr) test
connection.write
orig = connection
except AttributeError:
connection = _ServerConnection()
peer = connection.peer
else:
peer = None
self._connections.append(connection)
orig = None

connection.control = self
connection.peer.address = client_address
connection.address = self.address
self._connections.append(connection)
self._handler(connection)
return peer

if handler is not None:
handler.connected(connection.peer)
elif orig is None:
PrintingHandler(connection.peer)
return connection.peer

return None

connector = connect

Expand All @@ -243,7 +266,7 @@ def close(self, handler=None):
self._handler = None
if handler is None:
while self._connections:
self._connections[0].test_close('stopped')
self._connections[0].close()
elif not self._connections:
handler(self)
else:
Expand Down
1 change: 0 additions & 1 deletion src/zc/ngi/testing.test
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ simple example:
client i got: Hi
server h got: Hi
client h got: Bye
client closed b closed

If an exeption is raised by a handler, then the exception is logged
and the connection is closed (if not already closed).
Expand Down
Loading

0 comments on commit 4fcf78b

Please sign in to comment.