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

Commit

Permalink
Merge pull request #1 from gotcha/master
Browse files Browse the repository at this point in the history
support IPv6
  • Loading branch information
jimfulton authored Aug 31, 2017
2 parents fd449b6 + 5b81722 commit c20f873
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 100 deletions.
69 changes: 22 additions & 47 deletions README.txt → CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,54 +1,41 @@
Network Gateway Interface
*************************
Changelog
=========

The Network Gateway Interface provides:

- the ability to test application networking code without use of
sockets, threads or subprocesses
2.1.0.dev0 (unreleased)
-----------------------

- clean separation of application code and low-level networking code

- a fairly simple inheritence free set of networking APIs

- an event-based framework that makes it easy to handle many
simultaneous connections while still supporting an imperative
programming style.
New features:

To learn more, see http://packages.python.org/zc.ngi/
- support IPv6

Changes
*******

====================
2.0.1 (2012-04-06)
====================
------------------

Bugs Fixed

- Sending data faster than a socket could transmit it wasn't handled
correctly.

====================
2.0.0 (2011-12-10)
====================
------------------

Bugs Fixed

- zc.ngi.async listeners didn't provide the real address when binding
to port 0.

====================
2.0.0a6 (2011-05-26)
====================
--------------------

Bugs Fixed

- If application code made many small writes, each write was sent
individually, which could trigger Nagle's algorithm.

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

New Features:

Expand All @@ -68,9 +55,8 @@ Bugs Fixed:
- The built-in connection adapters and handy adapter base class
didn't implement __nonzero__.

====================
2.0.0a4 (2010-07-27)
====================
--------------------

Bugs Fixed:

Expand All @@ -86,17 +72,15 @@ Bugs Fixed:
(This means the undocumented practive of sending zc.ngi.END_OF_DATA
to write is now deprecated.)

====================
2.0.0a3 (2010-07-22)
====================
--------------------

Bugs Fixed:

- Fixed a packaging bug.

====================
2.0.0a2 (2010-07-22)
====================
--------------------

New Features:

Expand All @@ -111,9 +95,8 @@ Bugs Fixed:
caused printing handlers to be used when they shouldn't have been.


====================
2.0.0a1 (2010-07-08)
====================
--------------------

New Features:

Expand Down Expand Up @@ -143,19 +126,17 @@ Bugs Fixed:
- There we a number of problems with error handling in the ``async``
implementation.

==================
1.1.6 (2010-03-01)
==================
------------------

Bug fixed:

- Fixed bad logging of ``listening on ...``. The message was emitted
before the actual operation was successful. Emits now a warning
``unable to listen on...`` if binding to the given address fails.

==================
1.1.5 (2010-01-19)
==================
------------------

Bug fixed:

Expand All @@ -167,45 +148,40 @@ Bug fixed:
algorithm).


==================
1.1.4 (2009-10-28)
==================
------------------

Bug fixed:

- Spurious warnings sometimes occurred due to a race condition in
setting up servers.
- Added missing "writelines" method to zc.ngi.adapters.Lines.

==================
1.1.3 (2009-07-30)
==================
------------------

Bug fixed:

- zc.ngi.async bind failures weren't handled properly, causing lots of
annoying log messages to get spewed, which tesnded to fill up log
files.

==================
1.1.2 (2009-07-02)
==================
------------------

Bugs fixed:

- The zc.ngi.async thread wasn't named. All threads should be named.

==================
1.1.1 (2009-06-29)
==================
------------------

Bugs fixed:

- zc.ngi.blocking didn't properly handle connection failures.

==================
1.1.0 (2009-05-26)
==================
------------------

Bugs fixed:

Expand All @@ -230,9 +206,8 @@ New features:
connections in response to connection-handler errors. (Otherwise,
handlers, and especially handler adapters, would have to do this.)

==================
1.0.1 (2007-05-30)
==================
------------------

Bugs fixed:

Expand Down
17 changes: 17 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Network Gateway Interface
-------------------------

The Network Gateway Interface provides:

- the ability to test application networking code without use of
sockets, threads or subprocesses

- clean separation of application code and low-level networking code

- a fairly simple inheritence free set of networking APIs

- an event-based framework that makes it easy to handle many
simultaneous connections while still supporting an imperative
programming style.

To learn more, see http://packages.python.org/zc.ngi/
9 changes: 7 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,16 @@
#
##############################################################################

name, version = 'zc.ngi', '0'
name = 'zc.ngi'
version = "2.1.0.dev0"

from setuptools import setup, find_packages

readme = open('README.txt').read()
readme = (
open('README.rst').read()
+ '\n' +
open('CHANGES.rst').read()
+ '\n')

tests_require = ['zope.testing', 'manuel']

Expand Down
105 changes: 55 additions & 50 deletions src/zc/ngi/async.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,21 @@

BUFFER_SIZE = 8*1024


def get_family_from_address(addr):
if addr is None:
# keep backward compatibility
return socket.AF_INET
elif isinstance(addr, str):
return socket.AF_UNIX
elif isinstance(addr, tuple):
if ":" in addr[0]:
return socket.AF_INET6
else:
return socket.AF_INET
raise ValueError("addr should be string or tuple of ip address, port")


class Implementation:
zc.ngi.interfaces.implements(zc.ngi.interfaces.IImplementation)

Expand Down Expand Up @@ -82,10 +97,7 @@ def listener(self, addr, handler, thready=False):
return result

def udp(self, address, message):
if isinstance(address, str):
family = socket.AF_UNIX
else:
family = socket.AF_INET
family = get_family_from_address(address)
try:
sock = _udp_socks[family].pop()
except IndexError:
Expand Down Expand Up @@ -493,10 +505,8 @@ class _Connector(dispatcher):

def __init__(self, addr, handler, implementation):
self.__handler = handler
if isinstance(addr, str):
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
else:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
family = get_family_from_address(addr)
sock = socket.socket(family, socket.SOCK_STREAM)

dispatcher.__init__(self, sock, addr, implementation)

Expand Down Expand Up @@ -601,10 +611,8 @@ def __init__(self, addr, handler, implementation, thready):
self.__connections = set()
self.address = addr
BaseListener.__init__(self, implementation)
if isinstance(addr, str):
family = socket.AF_UNIX
else:
family = socket.AF_INET
family = get_family_from_address(addr)

self.create_socket(family, socket.SOCK_STREAM)
try:
if not is_win32:
Expand All @@ -627,7 +635,7 @@ def __init__(self, addr, handler, implementation, thready):
break
else:
self.bind(addr)
if family is socket.AF_INET and addr[1] == 0:
if family in (socket.AF_INET, socket.AF_INET6) and addr[1] == 0:
self.addr = addr = addr[0], self.socket.getsockname()[1]

self.logger.info("listening on %r", addr)
Expand Down Expand Up @@ -724,10 +732,7 @@ def __init__(self, addr, handler, buffer_size, implementation):
self.__handler = handler
self.__buffer_size = buffer_size
BaseListener.__init__(self, implementation)
if isinstance(addr, str):
family = socket.AF_UNIX
else:
family = socket.AF_INET
family = get_family_from_address(addr)
try:
self.create_socket(family, socket.SOCK_DGRAM)
if not is_win32:
Expand Down Expand Up @@ -831,39 +836,39 @@ def __init__(self, map):

count = 0
while 1:
count += 1
# Bind to a local port; for efficiency, let the OS pick
# a free port for us.
# Unfortunately, stress tests showed that we may not
# be able to connect to that port ("Address already in
# use") despite that the OS picked it. This appears
# to be a race bug in the Windows socket implementation.
# So we loop until a connect() succeeds (almost always
# on the first try). See the long thread at
# http://mail.zope.org/pipermail/zope/2005-July/160433.html
# for hideous details.
a = socket.socket()
a.bind(("127.0.0.1", 0))
connect_address = a.getsockname() # assigned (host, port) pair
a.listen(1)
try:
w.connect(connect_address)
break # success
except socket.error, detail:
if detail[0] != errno.WSAEADDRINUSE:
# "Address already in use" is the only error
# I've seen on two WinXP Pro SP2 boxes, under
# Pythons 2.3.5 and 2.4.1.
raise
# (10048, 'Address already in use')
# assert count <= 2 # never triggered in Tim's tests
if count >= 10: # I've never seen it go above 2
a.close()
w.close()
raise BindError("Cannot bind trigger!")
# Close `a` and try again. Note: I originally put a short
# sleep() here, but it didn't appear to help or hurt.
a.close()
count += 1
# Bind to a local port; for efficiency, let the OS pick
# a free port for us.
# Unfortunately, stress tests showed that we may not
# be able to connect to that port ("Address already in
# use") despite that the OS picked it. This appears
# to be a race bug in the Windows socket implementation.
# So we loop until a connect() succeeds (almost always
# on the first try). See the long thread at
# http://mail.zope.org/pipermail/zope/2005-July/160433.html
# for hideous details.
a = socket.socket()
a.bind(("127.0.0.1", 0))
connect_address = a.getsockname() # assigned (host, port) pair
a.listen(1)
try:
w.connect(connect_address)
break # success
except socket.error, detail:
if detail[0] != errno.WSAEADDRINUSE:
# "Address already in use" is the only error
# I've seen on two WinXP Pro SP2 boxes, under
# Pythons 2.3.5 and 2.4.1.
raise
# (10048, 'Address already in use')
# assert count <= 2 # never triggered in Tim's tests
if count >= 10: # I've never seen it go above 2
a.close()
w.close()
raise BindError("Cannot bind trigger!")
# Close `a` and try again. Note: I originally put a short
# sleep() here, but it didn't appear to help or hurt.
a.close()

r, addr = a.accept() # r becomes asyncore's (self.)socket
a.close()
Expand Down
Loading

0 comments on commit c20f873

Please sign in to comment.