Merge pull request #501 from exarkun/1595.address-already-in-use-test_introducer-create_tub

Fix "Address already in use" errors from test_introducer on POSIX
This commit is contained in:
Jean-Paul Calderone 2018-06-08 16:21:34 -04:00 committed by GitHub
commit b31b78e6cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -2,11 +2,15 @@
import os, re, itertools
from base64 import b32decode
import json
from socket import socket, AF_INET
from twisted.trial import unittest
from twisted.internet import defer, address
from twisted.python import log
from twisted.python.filepath import FilePath
from twisted.python.reflect import requireModule
from twisted.internet.endpoints import AdoptedStreamServerEndpoint
from twisted.internet.interfaces import IReactorSocket
from foolscap.api import Tub, Referenceable, fireEventually, flushEventualQueue
from twisted.application import service
@ -23,6 +27,8 @@ from allmydata.client import create_client
from allmydata.util import pollmixin, keyutil, idlib, fileutil, iputil, yamlutil
import allmydata.test.common_util as testutil
fcntl = requireModule("fcntl")
class LoggingMultiService(service.MultiService):
def log(self, msg, **kw):
log.msg(msg, **kw)
@ -314,6 +320,64 @@ class Server(unittest.TestCase):
NICKNAME = u"n\u00EDickname-%s" # LATIN SMALL LETTER I WITH ACUTE
def foolscapEndpointForPortNumber(portnum):
"""
Create an endpoint that can be passed to ``Tub.listen``.
:param portnum: Either an integer port number indicating which TCP/IPv4
port number the endpoint should bind or ``None`` to automatically
allocate such a port number.
:return: A two-tuple of the integer port number allocated and a
Foolscap-compatible endpoint object.
"""
if portnum is None:
# Bury this reactor import here to minimize the chances of it having
# the effect of installing the default reactor.
from twisted.internet import reactor
if fcntl is not None and IReactorSocket.providedBy(reactor):
# On POSIX we can take this very safe approach of binding the
# actual socket to an address. Once the bind succeeds here, we're
# no longer subject to any future EADDRINUSE problems.
s = socket()
try:
s.bind(('', 0))
portnum = s.getsockname()[1]
s.listen(1)
fd = os.dup(s.fileno())
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
flags = flags | os.O_NONBLOCK | fcntl.FD_CLOEXEC
fcntl.fcntl(fd, fcntl.F_SETFD, flags)
return (
portnum,
AdoptedStreamServerEndpoint(reactor, fd, AF_INET),
)
finally:
s.close()
else:
# Get a random port number and fall through. This is necessary on
# Windows where Twisted doesn't offer IReactorSocket. This
# approach is error prone for the reasons described on
# https://tahoe-lafs.org/trac/tahoe-lafs/ticket/2787
portnum = iputil.allocate_tcp_port()
return (portnum, "tcp:%d" % (portnum,))
def listenOnUnused(tub, portnum=None):
"""
Start listening on an unused TCP port number with the given tub.
:param portnum: Either an integer port number indicating which TCP/IPv4
port number the endpoint should bind or ``None`` to automatically
allocate such a port number.
:return: An integer indicating the TCP port number on which the tub is now
listening.
"""
portnum, endpoint = foolscapEndpointForPortNumber(portnum)
tub.listenOn(endpoint)
tub.setLocation("localhost:%d" % (portnum,))
return portnum
class SystemTestMixin(ServiceMixin, pollmixin.PollMixin):
def create_tub(self, portnum=None):
@ -323,11 +387,7 @@ class SystemTestMixin(ServiceMixin, pollmixin.PollMixin):
#tub.setOption("logRemoteFailures", True)
tub.setOption("expose-remote-exception-types", False)
tub.setServiceParent(self.parent)
if portnum is None:
portnum = iputil.allocate_tcp_port()
tub.listenOn("tcp:%d" % portnum)
self.central_portnum = portnum
tub.setLocation("localhost:%d" % self.central_portnum)
self.central_portnum = listenOnUnused(tub, portnum)
class Queue(SystemTestMixin, unittest.TestCase):
def test_queue_until_connected(self):
@ -414,10 +474,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
#tub.setOption("logRemoteFailures", True)
tub.setOption("expose-remote-exception-types", False)
tub.setServiceParent(self.parent)
portnum = iputil.allocate_tcp_port()
tub.listenOn("tcp:%d" % portnum)
tub.setLocation("localhost:%d" % portnum)
listenOnUnused(tub)
log.msg("creating client %d: %s" % (i, tub.getShortTubID()))
c = IntroducerClient(tub, self.introducer_furl,
NICKNAME % str(i),
@ -895,10 +952,7 @@ class NonV1Server(SystemTestMixin, unittest.TestCase):
tub = Tub()
tub.setOption("expose-remote-exception-types", False)
tub.setServiceParent(self.parent)
portnum = iputil.allocate_tcp_port()
tub.listenOn("tcp:%d" % portnum)
tub.setLocation("localhost:%d" % portnum)
listenOnUnused(tub)
c = IntroducerClient(tub, self.introducer_furl,
u"nickname-client", "version", "oldest", {},
fakeseq, FilePath(self.mktemp()))