[CalendarServer-changes] [9055] CalendarServer/branches/users/glyph/ipv6-client
source_changes at macosforge.org
source_changes at macosforge.org
Fri Apr 13 11:43:15 PDT 2012
Revision: 9055
http://trac.macosforge.org/projects/calendarserver/changeset/9055
Author: glyph at apple.com
Date: 2012-04-13 11:43:14 -0700 (Fri, 13 Apr 2012)
Log Message:
-----------
Add a temporary backport of required Twisted functionality for IPv6 clients.
Modified Paths:
--------------
CalendarServer/branches/users/glyph/ipv6-client/calendarserver/__init__.py
CalendarServer/branches/users/glyph/ipv6-client/support/build.sh
CalendarServer/branches/users/glyph/ipv6-client/twext/patches.py
Added Paths:
-----------
CalendarServer/branches/users/glyph/ipv6-client/twext/backport/
CalendarServer/branches/users/glyph/ipv6-client/twext/backport/__init__.py
CalendarServer/branches/users/glyph/ipv6-client/twext/backport/internet/
CalendarServer/branches/users/glyph/ipv6-client/twext/backport/internet/__init__.py
CalendarServer/branches/users/glyph/ipv6-client/twext/backport/internet/address.py
CalendarServer/branches/users/glyph/ipv6-client/twext/backport/internet/endpoints.py
CalendarServer/branches/users/glyph/ipv6-client/twext/backport/internet/tcp.py
Modified: CalendarServer/branches/users/glyph/ipv6-client/calendarserver/__init__.py
===================================================================
--- CalendarServer/branches/users/glyph/ipv6-client/calendarserver/__init__.py 2012-04-13 18:40:22 UTC (rev 9054)
+++ CalendarServer/branches/users/glyph/ipv6-client/calendarserver/__init__.py 2012-04-13 18:43:14 UTC (rev 9055)
@@ -19,6 +19,10 @@
CalendarServer application code.
"""
+# Make sure we have twext's required Twisted patches loaded before we do
+# anything at all.
+__import__("twext")
+
#
# setuptools is annoying
#
Modified: CalendarServer/branches/users/glyph/ipv6-client/support/build.sh
===================================================================
--- CalendarServer/branches/users/glyph/ipv6-client/support/build.sh 2012-04-13 18:40:22 UTC (rev 9054)
+++ CalendarServer/branches/users/glyph/ipv6-client/support/build.sh 2012-04-13 18:43:14 UTC (rev 9055)
@@ -717,6 +717,8 @@
"PyGreSQL" "pgdb" "${pg}" \
"${pypi}/P/PyGreSQL/${pg}.tar.gz";
+ # Maintenance note: next time the Twisted dependency gets updated, check out
+ # twext/patches.py.
py_dependency -v 12 -r HEAD \
"Twisted" "twisted" "Twisted" \
"svn://svn.twistedmatrix.com/svn/Twisted/tags/releases/twisted-12.0.0";
Added: CalendarServer/branches/users/glyph/ipv6-client/twext/backport/__init__.py
===================================================================
Added: CalendarServer/branches/users/glyph/ipv6-client/twext/backport/internet/__init__.py
===================================================================
Added: CalendarServer/branches/users/glyph/ipv6-client/twext/backport/internet/address.py
===================================================================
--- CalendarServer/branches/users/glyph/ipv6-client/twext/backport/internet/address.py (rev 0)
+++ CalendarServer/branches/users/glyph/ipv6-client/twext/backport/internet/address.py 2012-04-13 18:43:14 UTC (rev 9055)
@@ -0,0 +1,144 @@
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+Address objects for network connections.
+"""
+
+import warnings, os
+
+from zope.interface import implements
+
+from twisted.internet.interfaces import IAddress
+from twisted.python import util
+
+
+class _IPAddress(object, util.FancyEqMixin):
+ """
+ An L{_IPAddress} represents the address of an IP socket endpoint, providing
+ common behavior for IPv4 and IPv6.
+
+ @ivar type: A string describing the type of transport, either 'TCP' or
+ 'UDP'.
+
+ @ivar host: A string containing the presentation format of the IP address;
+ for example, "127.0.0.1" or "::1".
+ @type host: C{str}
+
+ @ivar port: An integer representing the port number.
+ @type port: C{int}
+ """
+
+ implements(IAddress)
+
+ compareAttributes = ('type', 'host', 'port')
+
+ def __init__(self, type, host, port):
+ assert type in ('TCP', 'UDP')
+ self.type = type
+ self.host = host
+ self.port = port
+
+
+ def __repr__(self):
+ return '%s(%s, %r, %d)' % (
+ self.__class__.__name__, self.type, self.host, self.port)
+
+
+ def __hash__(self):
+ return hash((self.type, self.host, self.port))
+
+
+
+class IPv4Address(_IPAddress):
+ """
+ An L{IPv4Address} represents the address of an IPv4 socket endpoint.
+
+ @ivar host: A string containing a dotted-quad IPv4 address; for example,
+ "127.0.0.1".
+ @type host: C{str}
+ """
+
+ def __init__(self, type, host, port, _bwHack=None):
+ _IPAddress.__init__(self, type, host, port)
+ if _bwHack is not None:
+ warnings.warn("twisted.internet.address.IPv4Address._bwHack "
+ "is deprecated since Twisted 11.0",
+ DeprecationWarning, stacklevel=2)
+
+
+
+class IPv6Address(_IPAddress):
+ """
+ An L{IPv6Address} represents the address of an IPv6 socket endpoint.
+
+ @ivar host: A string containing a colon-separated, hexadecimal formatted
+ IPv6 address; for example, "::1".
+ @type host: C{str}
+ """
+
+
+
+class UNIXAddress(object, util.FancyEqMixin):
+ """
+ Object representing a UNIX socket endpoint.
+
+ @ivar name: The filename associated with this socket.
+ @type name: C{str}
+ """
+
+ implements(IAddress)
+
+ compareAttributes = ('name', )
+
+ def __init__(self, name, _bwHack = None):
+ self.name = name
+ if _bwHack is not None:
+ warnings.warn("twisted.internet.address.UNIXAddress._bwHack is deprecated since Twisted 11.0",
+ DeprecationWarning, stacklevel=2)
+
+
+ if getattr(os.path, 'samefile', None) is not None:
+ def __eq__(self, other):
+ """
+ overriding L{util.FancyEqMixin} to ensure the os level samefile
+ check is done if the name attributes do not match.
+ """
+ res = super(UNIXAddress, self).__eq__(other)
+ if res == False:
+ try:
+ return os.path.samefile(self.name, other.name)
+ except OSError:
+ pass
+ return res
+
+
+ def __repr__(self):
+ return 'UNIXAddress(%r)' % (self.name,)
+
+
+ def __hash__(self):
+ try:
+ s1 = os.stat(self.name)
+ return hash((s1.st_ino, s1.st_dev))
+ except OSError:
+ return hash(self.name)
+
+
+
+# These are for buildFactory backwards compatability due to
+# stupidity-induced inconsistency.
+
+class _ServerFactoryIPv4Address(IPv4Address):
+ """Backwards compatability hack. Just like IPv4Address in practice."""
+
+ def __eq__(self, other):
+ if isinstance(other, tuple):
+ warnings.warn("IPv4Address.__getitem__ is deprecated. Use attributes instead.",
+ category=DeprecationWarning, stacklevel=2)
+ return (self.host, self.port) == other
+ elif isinstance(other, IPv4Address):
+ a = (self.type, self.host, self.port)
+ b = (other.type, other.host, other.port)
+ return a == b
+ return False
Added: CalendarServer/branches/users/glyph/ipv6-client/twext/backport/internet/endpoints.py
===================================================================
--- CalendarServer/branches/users/glyph/ipv6-client/twext/backport/internet/endpoints.py (rev 0)
+++ CalendarServer/branches/users/glyph/ipv6-client/twext/backport/internet/endpoints.py 2012-04-13 18:43:14 UTC (rev 9055)
@@ -0,0 +1,1159 @@
+# -*- test-case-name: twisted.internet.test.test_endpoints -*-
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+"""
+Implementations of L{IStreamServerEndpoint} and L{IStreamClientEndpoint} that
+wrap the L{IReactorTCP}, L{IReactorSSL}, and L{IReactorUNIX} interfaces.
+
+This also implements an extensible mini-language for describing endpoints,
+parsed by the L{clientFromString} and L{serverFromString} functions.
+
+ at since: 10.1
+"""
+
+import os, socket
+
+from zope.interface import implements, directlyProvides
+import warnings
+
+from twisted.internet import interfaces, defer, error, fdesc
+from twisted.internet.protocol import ClientFactory, Protocol
+from twisted.plugin import IPlugin, getPlugins
+from twisted.internet.interfaces import IStreamServerEndpointStringParser
+from twisted.internet.interfaces import IStreamClientEndpointStringParser
+from twisted.python.filepath import FilePath
+from twisted.python.systemd import ListenFDs
+
+
+__all__ = ["clientFromString", "serverFromString",
+ "TCP4ServerEndpoint", "TCP4ClientEndpoint",
+ "UNIXServerEndpoint", "UNIXClientEndpoint",
+ "SSL4ServerEndpoint", "SSL4ClientEndpoint",
+ "AdoptedStreamServerEndpoint"]
+
+
+class _WrappingProtocol(Protocol):
+ """
+ Wrap another protocol in order to notify my user when a connection has
+ been made.
+
+ @ivar _connectedDeferred: The L{Deferred} that will callback
+ with the C{wrappedProtocol} when it is connected.
+
+ @ivar _wrappedProtocol: An L{IProtocol} provider that will be
+ connected.
+ """
+
+ def __init__(self, connectedDeferred, wrappedProtocol):
+ """
+ @param connectedDeferred: The L{Deferred} that will callback
+ with the C{wrappedProtocol} when it is connected.
+
+ @param wrappedProtocol: An L{IProtocol} provider that will be
+ connected.
+ """
+ self._connectedDeferred = connectedDeferred
+ self._wrappedProtocol = wrappedProtocol
+
+ if interfaces.IHalfCloseableProtocol.providedBy(
+ self._wrappedProtocol):
+ directlyProvides(self, interfaces.IHalfCloseableProtocol)
+
+
+ def logPrefix(self):
+ """
+ Transparently pass through the wrapped protocol's log prefix.
+ """
+ if interfaces.ILoggingContext.providedBy(self._wrappedProtocol):
+ return self._wrappedProtocol.logPrefix()
+ return self._wrappedProtocol.__class__.__name__
+
+
+ def connectionMade(self):
+ """
+ Connect the C{self._wrappedProtocol} to our C{self.transport} and
+ callback C{self._connectedDeferred} with the C{self._wrappedProtocol}
+ """
+ self._wrappedProtocol.makeConnection(self.transport)
+ self._connectedDeferred.callback(self._wrappedProtocol)
+
+
+ def dataReceived(self, data):
+ """
+ Proxy C{dataReceived} calls to our C{self._wrappedProtocol}
+ """
+ return self._wrappedProtocol.dataReceived(data)
+
+
+ def connectionLost(self, reason):
+ """
+ Proxy C{connectionLost} calls to our C{self._wrappedProtocol}
+ """
+ return self._wrappedProtocol.connectionLost(reason)
+
+
+ def readConnectionLost(self):
+ """
+ Proxy L{IHalfCloseableProtocol.readConnectionLost} to our
+ C{self._wrappedProtocol}
+ """
+ self._wrappedProtocol.readConnectionLost()
+
+
+ def writeConnectionLost(self):
+ """
+ Proxy L{IHalfCloseableProtocol.writeConnectionLost} to our
+ C{self._wrappedProtocol}
+ """
+ self._wrappedProtocol.writeConnectionLost()
+
+
+
+class _WrappingFactory(ClientFactory):
+ """
+ Wrap a factory in order to wrap the protocols it builds.
+
+ @ivar _wrappedFactory: A provider of I{IProtocolFactory} whose
+ buildProtocol method will be called and whose resulting protocol
+ will be wrapped.
+
+ @ivar _onConnection: An L{Deferred} that fires when the protocol is
+ connected
+ """
+ protocol = _WrappingProtocol
+
+ def __init__(self, wrappedFactory):
+ """
+ @param wrappedFactory: A provider of I{IProtocolFactory} whose
+ buildProtocol method will be called and whose resulting protocol
+ will be wrapped.
+ """
+ self._wrappedFactory = wrappedFactory
+ self._onConnection = defer.Deferred(canceller=self._canceller)
+
+
+ def startedConnecting(self, connector):
+ self._connector = connector
+
+
+ def _canceller(self, deferred):
+ deferred.errback(
+ error.ConnectingCancelledError(
+ self._connector.getDestination()))
+ self._connector.stopConnecting()
+
+
+ def doStart(self):
+ """
+ Start notifications are passed straight through to the wrapped factory.
+ """
+ self._wrappedFactory.doStart()
+
+
+ def doStop(self):
+ """
+ Stop notifications are passed straight through to the wrapped factory.
+ """
+ self._wrappedFactory.doStop()
+
+
+ def buildProtocol(self, addr):
+ """
+ Proxy C{buildProtocol} to our C{self._wrappedFactory} or errback
+ the C{self._onConnection} L{Deferred}.
+
+ @return: An instance of L{_WrappingProtocol} or C{None}
+ """
+ try:
+ proto = self._wrappedFactory.buildProtocol(addr)
+ except:
+ self._onConnection.errback()
+ else:
+ return self.protocol(self._onConnection, proto)
+
+
+ def clientConnectionFailed(self, connector, reason):
+ """
+ Errback the C{self._onConnection} L{Deferred} when the
+ client connection fails.
+ """
+ if not self._onConnection.called:
+ self._onConnection.errback(reason)
+
+
+
+class TCP4ServerEndpoint(object):
+ """
+ TCP server endpoint with an IPv4 configuration
+
+ @ivar _reactor: An L{IReactorTCP} provider.
+
+ @type _port: int
+ @ivar _port: The port number on which to listen for incoming connections.
+
+ @type _backlog: int
+ @ivar _backlog: size of the listen queue
+
+ @type _interface: str
+ @ivar _interface: the hostname to bind to, defaults to '' (all)
+ """
+ implements(interfaces.IStreamServerEndpoint)
+
+ def __init__(self, reactor, port, backlog=50, interface=''):
+ """
+ @param reactor: An L{IReactorTCP} provider.
+ @param port: The port number used listening
+ @param backlog: size of the listen queue
+ @param interface: the hostname to bind to, defaults to '' (all)
+ """
+ self._reactor = reactor
+ self._port = port
+ self._listenArgs = dict(backlog=50, interface='')
+ self._backlog = backlog
+ self._interface = interface
+
+
+ def listen(self, protocolFactory):
+ """
+ Implement L{IStreamServerEndpoint.listen} to listen on a TCP socket
+ """
+ return defer.execute(self._reactor.listenTCP,
+ self._port,
+ protocolFactory,
+ backlog=self._backlog,
+ interface=self._interface)
+
+
+
+class TCP4ClientEndpoint(object):
+ """
+ TCP client endpoint with an IPv4 configuration.
+
+ @ivar _reactor: An L{IReactorTCP} provider.
+
+ @type _host: str
+ @ivar _host: The hostname to connect to as a C{str}
+
+ @type _port: int
+ @ivar _port: The port to connect to as C{int}
+
+ @type _timeout: int
+ @ivar _timeout: number of seconds to wait before assuming the
+ connection has failed.
+
+ @type _bindAddress: tuple
+ @type _bindAddress: a (host, port) tuple of local address to bind
+ to, or None.
+ """
+ implements(interfaces.IStreamClientEndpoint)
+
+ def __init__(self, reactor, host, port, timeout=30, bindAddress=None):
+ """
+ @param reactor: An L{IReactorTCP} provider
+ @param host: A hostname, used when connecting
+ @param port: The port number, used when connecting
+ @param timeout: number of seconds to wait before assuming the
+ connection has failed.
+ @param bindAddress: a (host, port tuple of local address to bind to,
+ or None.
+ """
+ self._reactor = reactor
+ self._host = host
+ self._port = port
+ self._timeout = timeout
+ self._bindAddress = bindAddress
+
+
+ def connect(self, protocolFactory):
+ """
+ Implement L{IStreamClientEndpoint.connect} to connect via TCP.
+ """
+ try:
+ wf = _WrappingFactory(protocolFactory)
+ self._reactor.connectTCP(
+ self._host, self._port, wf,
+ timeout=self._timeout, bindAddress=self._bindAddress)
+ return wf._onConnection
+ except:
+ return defer.fail()
+
+
+
+class SSL4ServerEndpoint(object):
+ """
+ SSL secured TCP server endpoint with an IPv4 configuration.
+
+ @ivar _reactor: An L{IReactorSSL} provider.
+
+ @type _host: str
+ @ivar _host: The hostname to connect to as a C{str}
+
+ @type _port: int
+ @ivar _port: The port to connect to as C{int}
+
+ @type _sslContextFactory: L{OpenSSLCertificateOptions}
+ @var _sslContextFactory: SSL Configuration information as an
+ L{OpenSSLCertificateOptions}
+
+ @type _backlog: int
+ @ivar _backlog: size of the listen queue
+
+ @type _interface: str
+ @ivar _interface: the hostname to bind to, defaults to '' (all)
+ """
+ implements(interfaces.IStreamServerEndpoint)
+
+ def __init__(self, reactor, port, sslContextFactory,
+ backlog=50, interface=''):
+ """
+ @param reactor: An L{IReactorSSL} provider.
+ @param port: The port number used listening
+ @param sslContextFactory: An instance of
+ L{twisted.internet._sslverify.OpenSSLCertificateOptions}.
+ @param timeout: number of seconds to wait before assuming the
+ connection has failed.
+ @param bindAddress: a (host, port tuple of local address to bind to,
+ or None.
+ """
+ self._reactor = reactor
+ self._port = port
+ self._sslContextFactory = sslContextFactory
+ self._backlog = backlog
+ self._interface = interface
+
+
+ def listen(self, protocolFactory):
+ """
+ Implement L{IStreamServerEndpoint.listen} to listen for SSL on a
+ TCP socket.
+ """
+ return defer.execute(self._reactor.listenSSL, self._port,
+ protocolFactory,
+ contextFactory=self._sslContextFactory,
+ backlog=self._backlog,
+ interface=self._interface)
+
+
+
+class SSL4ClientEndpoint(object):
+ """
+ SSL secured TCP client endpoint with an IPv4 configuration
+
+ @ivar _reactor: An L{IReactorSSL} provider.
+
+ @type _host: str
+ @ivar _host: The hostname to connect to as a C{str}
+
+ @type _port: int
+ @ivar _port: The port to connect to as C{int}
+
+ @type _sslContextFactory: L{OpenSSLCertificateOptions}
+ @var _sslContextFactory: SSL Configuration information as an
+ L{OpenSSLCertificateOptions}
+
+ @type _timeout: int
+ @ivar _timeout: number of seconds to wait before assuming the
+ connection has failed.
+
+ @type _bindAddress: tuple
+ @ivar _bindAddress: a (host, port) tuple of local address to bind
+ to, or None.
+ """
+ implements(interfaces.IStreamClientEndpoint)
+
+ def __init__(self, reactor, host, port, sslContextFactory,
+ timeout=30, bindAddress=None):
+ """
+ @param reactor: An L{IReactorSSL} provider.
+ @param host: A hostname, used when connecting
+ @param port: The port number, used when connecting
+ @param sslContextFactory: SSL Configuration information as An instance
+ of L{OpenSSLCertificateOptions}.
+ @param timeout: number of seconds to wait before assuming the
+ connection has failed.
+ @param bindAddress: a (host, port tuple of local address to bind to,
+ or None.
+ """
+ self._reactor = reactor
+ self._host = host
+ self._port = port
+ self._sslContextFactory = sslContextFactory
+ self._timeout = timeout
+ self._bindAddress = bindAddress
+
+
+ def connect(self, protocolFactory):
+ """
+ Implement L{IStreamClientEndpoint.connect} to connect with SSL over
+ TCP.
+ """
+ try:
+ wf = _WrappingFactory(protocolFactory)
+ self._reactor.connectSSL(
+ self._host, self._port, wf, self._sslContextFactory,
+ timeout=self._timeout, bindAddress=self._bindAddress)
+ return wf._onConnection
+ except:
+ return defer.fail()
+
+
+
+class UNIXServerEndpoint(object):
+ """
+ UnixSocket server endpoint.
+
+ @type path: str
+ @ivar path: a path to a unix socket on the filesystem.
+
+ @type _listenArgs: dict
+ @ivar _listenArgs: A C{dict} of keyword args that will be passed
+ to L{IReactorUNIX.listenUNIX}
+
+ @var _reactor: An L{IReactorTCP} provider.
+ """
+ implements(interfaces.IStreamServerEndpoint)
+
+ def __init__(self, reactor, address, backlog=50, mode=0666, wantPID=0):
+ """
+ @param reactor: An L{IReactorUNIX} provider.
+ @param address: The path to the Unix socket file, used when listening
+ @param listenArgs: An optional dict of keyword args that will be
+ passed to L{IReactorUNIX.listenUNIX}
+ @param backlog: number of connections to allow in backlog.
+ @param mode: mode to set on the unix socket. This parameter is
+ deprecated. Permissions should be set on the directory which
+ contains the UNIX socket.
+ @param wantPID: if True, create a pidfile for the socket.
+ """
+ self._reactor = reactor
+ self._address = address
+ self._backlog = backlog
+ self._mode = mode
+ self._wantPID = wantPID
+
+
+ def listen(self, protocolFactory):
+ """
+ Implement L{IStreamServerEndpoint.listen} to listen on a UNIX socket.
+ """
+ return defer.execute(self._reactor.listenUNIX, self._address,
+ protocolFactory,
+ backlog=self._backlog,
+ mode=self._mode,
+ wantPID=self._wantPID)
+
+
+
+class UNIXClientEndpoint(object):
+ """
+ UnixSocket client endpoint.
+
+ @type _path: str
+ @ivar _path: a path to a unix socket on the filesystem.
+
+ @type _timeout: int
+ @ivar _timeout: number of seconds to wait before assuming the connection
+ has failed.
+
+ @type _checkPID: bool
+ @ivar _checkPID: if True, check for a pid file to verify that a server
+ is listening.
+
+ @var _reactor: An L{IReactorUNIX} provider.
+ """
+ implements(interfaces.IStreamClientEndpoint)
+
+ def __init__(self, reactor, path, timeout=30, checkPID=0):
+ """
+ @param reactor: An L{IReactorUNIX} provider.
+ @param path: The path to the Unix socket file, used when connecting
+ @param timeout: number of seconds to wait before assuming the
+ connection has failed.
+ @param checkPID: if True, check for a pid file to verify that a server
+ is listening.
+ """
+ self._reactor = reactor
+ self._path = path
+ self._timeout = timeout
+ self._checkPID = checkPID
+
+
+ def connect(self, protocolFactory):
+ """
+ Implement L{IStreamClientEndpoint.connect} to connect via a
+ UNIX Socket
+ """
+ def _canceller(deferred):
+ connector.stopConnecting()
+ deferred.errback(
+ error.ConnectingCancelledError(connector.getDestination()))
+
+ try:
+ wf = _WrappingFactory(protocolFactory)
+ connector = self._reactor.connectUNIX(
+ self._path, wf,
+ timeout=self._timeout,
+ checkPID=self._checkPID)
+ return wf._onConnection
+ except:
+ return defer.fail()
+
+
+
+class AdoptedStreamServerEndpoint(object):
+ """
+ An endpoint for listening on a file descriptor initialized outside of
+ Twisted.
+
+ @ivar _used: A C{bool} indicating whether this endpoint has been used to
+ listen with a factory yet. C{True} if so.
+ """
+ _close = os.close
+ _setNonBlocking = staticmethod(fdesc.setNonBlocking)
+
+ def __init__(self, reactor, fileno, addressFamily):
+ """
+ @param reactor: An L{IReactorSocket} provider.
+
+ @param fileno: An integer file descriptor corresponding to a listening
+ I{SOCK_STREAM} socket.
+
+ @param addressFamily: The address family of the socket given by
+ C{fileno}.
+ """
+ self.reactor = reactor
+ self.fileno = fileno
+ self.addressFamily = addressFamily
+ self._used = False
+
+
+ def listen(self, factory):
+ """
+ Implement L{IStreamServerEndpoint.listen} to start listening on, and
+ then close, C{self._fileno}.
+ """
+ if self._used:
+ return defer.fail(error.AlreadyListened())
+ self._used = True
+
+ try:
+ self._setNonBlocking(self.fileno)
+ port = self.reactor.adoptStreamPort(
+ self.fileno, self.addressFamily, factory)
+ self._close(self.fileno)
+ except:
+ return defer.fail()
+ return defer.succeed(port)
+
+
+
+def _parseTCP(factory, port, interface="", backlog=50):
+ """
+ Internal parser function for L{_parseServer} to convert the string
+ arguments for a TCP(IPv4) stream endpoint into the structured arguments.
+
+ @param factory: the protocol factory being parsed, or C{None}. (This was a
+ leftover argument from when this code was in C{strports}, and is now
+ mostly None and unused.)
+
+ @type factory: L{IProtocolFactory} or C{NoneType}
+
+ @param port: the integer port number to bind
+ @type port: C{str}
+
+ @param interface: the interface IP to listen on
+ @param backlog: the length of the listen queue
+ @type backlog: C{str}
+
+ @return: a 2-tuple of (args, kwargs), describing the parameters to
+ L{IReactorTCP.listenTCP} (or, modulo argument 2, the factory, arguments
+ to L{TCP4ServerEndpoint}.
+ """
+ return (int(port), factory), {'interface': interface,
+ 'backlog': int(backlog)}
+
+
+
+def _parseUNIX(factory, address, mode='666', backlog=50, lockfile=True):
+ """
+ Internal parser function for L{_parseServer} to convert the string
+ arguments for a UNIX (AF_UNIX/SOCK_STREAM) stream endpoint into the
+ structured arguments.
+
+ @param factory: the protocol factory being parsed, or C{None}. (This was a
+ leftover argument from when this code was in C{strports}, and is now
+ mostly None and unused.)
+
+ @type factory: L{IProtocolFactory} or C{NoneType}
+
+ @param address: the pathname of the unix socket
+ @type address: C{str}
+
+ @param backlog: the length of the listen queue
+ @type backlog: C{str}
+
+ @param lockfile: A string '0' or '1', mapping to True and False
+ respectively. See the C{wantPID} argument to C{listenUNIX}
+
+ @return: a 2-tuple of (args, kwargs), describing the parameters to
+ L{IReactorTCP.listenUNIX} (or, modulo argument 2, the factory,
+ arguments to L{UNIXServerEndpoint}.
+ """
+ return (
+ (address, factory),
+ {'mode': int(mode, 8), 'backlog': int(backlog),
+ 'wantPID': bool(int(lockfile))})
+
+
+
+def _parseSSL(factory, port, privateKey="server.pem", certKey=None,
+ sslmethod=None, interface='', backlog=50):
+ """
+ Internal parser function for L{_parseServer} to convert the string
+ arguments for an SSL (over TCP/IPv4) stream endpoint into the structured
+ arguments.
+
+ @param factory: the protocol factory being parsed, or C{None}. (This was a
+ leftover argument from when this code was in C{strports}, and is now
+ mostly None and unused.)
+
+ @type factory: L{IProtocolFactory} or C{NoneType}
+
+ @param port: the integer port number to bind
+ @type port: C{str}
+
+ @param interface: the interface IP to listen on
+ @param backlog: the length of the listen queue
+ @type backlog: C{str}
+
+ @param privateKey: The file name of a PEM format private key file.
+ @type privateKey: C{str}
+
+ @param certKey: The file name of a PEM format certificate file.
+ @type certKey: C{str}
+
+ @param sslmethod: The string name of an SSL method, based on the name of a
+ constant in C{OpenSSL.SSL}. Must be one of: "SSLv23_METHOD",
+ "SSLv2_METHOD", "SSLv3_METHOD", "TLSv1_METHOD".
+ @type sslmethod: C{str}
+
+ @return: a 2-tuple of (args, kwargs), describing the parameters to
+ L{IReactorSSL.listenSSL} (or, modulo argument 2, the factory, arguments
+ to L{SSL4ServerEndpoint}.
+ """
+ from twisted.internet import ssl
+ if certKey is None:
+ certKey = privateKey
+ kw = {}
+ if sslmethod is not None:
+ kw['sslmethod'] = getattr(ssl.SSL, sslmethod)
+ cf = ssl.DefaultOpenSSLContextFactory(privateKey, certKey, **kw)
+ return ((int(port), factory, cf),
+ {'interface': interface, 'backlog': int(backlog)})
+
+
+class _SystemdParser(object):
+ """
+ Stream server endpoint string parser for the I{systemd} endpoint type.
+
+ @ivar prefix: See L{IStreamClientEndpointStringParser.prefix}.
+
+ @ivar _sddaemon: A L{ListenFDs} instance used to translate an index into an
+ actual file descriptor.
+ """
+ implements(IPlugin, IStreamServerEndpointStringParser)
+
+ _sddaemon = ListenFDs.fromEnvironment()
+
+ prefix = "systemd"
+
+ def _parseServer(self, reactor, domain, index):
+ """
+ Internal parser function for L{_parseServer} to convert the string
+ arguments for a systemd server endpoint into structured arguments for
+ L{AdoptedStreamServerEndpoint}.
+
+ @param reactor: An L{IReactorSocket} provider.
+
+ @param domain: The domain (or address family) of the socket inherited
+ from systemd. This is a string like C{"INET"} or C{"UNIX"}, ie the
+ name of an address family from the L{socket} module, without the
+ C{"AF_"} prefix.
+ @type domain: C{str}
+
+ @param index: An offset into the list of file descriptors inherited from
+ systemd.
+ @type index: C{str}
+
+ @return: A two-tuple of parsed positional arguments and parsed keyword
+ arguments (a tuple and a dictionary). These can be used to
+ construct a L{AdoptedStreamServerEndpoint}.
+ """
+ index = int(index)
+ fileno = self._sddaemon.inheritedDescriptors()[index]
+ addressFamily = getattr(socket, 'AF_' + domain)
+ return AdoptedStreamServerEndpoint(reactor, fileno, addressFamily)
+
+
+ def parseStreamServer(self, reactor, *args, **kwargs):
+ # Delegate to another function with a sane signature. This function has
+ # an insane signature to trick zope.interface into believing the
+ # interface is correctly implemented.
+ return self._parseServer(reactor, *args, **kwargs)
+
+
+
+_serverParsers = {"tcp": _parseTCP,
+ "unix": _parseUNIX,
+ "ssl": _parseSSL,
+ }
+
+_OP, _STRING = range(2)
+
+def _tokenize(description):
+ """
+ Tokenize a strports string and yield each token.
+
+ @param description: a string as described by L{serverFromString} or
+ L{clientFromString}.
+
+ @return: an iterable of 2-tuples of (L{_OP} or L{_STRING}, string). Tuples
+ starting with L{_OP} will contain a second element of either ':' (i.e.
+ 'next parameter') or '=' (i.e. 'assign parameter value'). For example,
+ the string 'hello:greet\=ing=world' would result in a generator
+ yielding these values::
+
+ _STRING, 'hello'
+ _OP, ':'
+ _STRING, 'greet=ing'
+ _OP, '='
+ _STRING, 'world'
+ """
+ current = ''
+ ops = ':='
+ nextOps = {':': ':=', '=': ':'}
+ description = iter(description)
+ for n in description:
+ if n in ops:
+ yield _STRING, current
+ yield _OP, n
+ current = ''
+ ops = nextOps[n]
+ elif n == '\\':
+ current += description.next()
+ else:
+ current += n
+ yield _STRING, current
+
+
+
+def _parse(description):
+ """
+ Convert a description string into a list of positional and keyword
+ parameters, using logic vaguely like what Python does.
+
+ @param description: a string as described by L{serverFromString} or
+ L{clientFromString}.
+
+ @return: a 2-tuple of C{(args, kwargs)}, where 'args' is a list of all
+ ':'-separated C{str}s not containing an '=' and 'kwargs' is a map of
+ all C{str}s which do contain an '='. For example, the result of
+ C{_parse('a:b:d=1:c')} would be C{(['a', 'b', 'c'], {'d': '1'})}.
+ """
+ args, kw = [], {}
+ def add(sofar):
+ if len(sofar) == 1:
+ args.append(sofar[0])
+ else:
+ kw[sofar[0]] = sofar[1]
+ sofar = ()
+ for (type, value) in _tokenize(description):
+ if type is _STRING:
+ sofar += (value,)
+ elif value == ':':
+ add(sofar)
+ sofar = ()
+ add(sofar)
+ return args, kw
+
+
+# Mappings from description "names" to endpoint constructors.
+_endpointServerFactories = {
+ 'TCP': TCP4ServerEndpoint,
+ 'SSL': SSL4ServerEndpoint,
+ 'UNIX': UNIXServerEndpoint,
+ }
+
+_endpointClientFactories = {
+ 'TCP': TCP4ClientEndpoint,
+ 'SSL': SSL4ClientEndpoint,
+ 'UNIX': UNIXClientEndpoint,
+ }
+
+
+_NO_DEFAULT = object()
+
+def _parseServer(description, factory, default=None):
+ """
+ Parse a stports description into a 2-tuple of arguments and keyword values.
+
+ @param description: A description in the format explained by
+ L{serverFromString}.
+ @type description: C{str}
+
+ @param factory: A 'factory' argument; this is left-over from
+ twisted.application.strports, it's not really used.
+ @type factory: L{IProtocolFactory} or L{None}
+
+ @param default: Deprecated argument, specifying the default parser mode to
+ use for unqualified description strings (those which do not have a ':'
+ and prefix).
+ @type default: C{str} or C{NoneType}
+
+ @return: a 3-tuple of (plugin or name, arguments, keyword arguments)
+ """
+ args, kw = _parse(description)
+ if not args or (len(args) == 1 and not kw):
+ deprecationMessage = (
+ "Unqualified strport description passed to 'service'."
+ "Use qualified endpoint descriptions; for example, 'tcp:%s'."
+ % (description,))
+ if default is None:
+ default = 'tcp'
+ warnings.warn(
+ deprecationMessage, category=DeprecationWarning, stacklevel=4)
+ elif default is _NO_DEFAULT:
+ raise ValueError(deprecationMessage)
+ # If the default has been otherwise specified, the user has already
+ # been warned.
+ args[0:0] = [default]
+ endpointType = args[0]
+ parser = _serverParsers.get(endpointType)
+ if parser is None:
+ for plugin in getPlugins(IStreamServerEndpointStringParser):
+ if plugin.prefix == endpointType:
+ return (plugin, args[1:], kw)
+ raise ValueError("Unknown endpoint type: '%s'" % (endpointType,))
+ return (endpointType.upper(),) + parser(factory, *args[1:], **kw)
+
+
+
+def _serverFromStringLegacy(reactor, description, default):
+ """
+ Underlying implementation of L{serverFromString} which avoids exposing the
+ deprecated 'default' argument to anything but L{strports.service}.
+ """
+ nameOrPlugin, args, kw = _parseServer(description, None, default)
+ if type(nameOrPlugin) is not str:
+ plugin = nameOrPlugin
+ return plugin.parseStreamServer(reactor, *args, **kw)
+ else:
+ name = nameOrPlugin
+ # Chop out the factory.
+ args = args[:1] + args[2:]
+ return _endpointServerFactories[name](reactor, *args, **kw)
+
+
+
+def serverFromString(reactor, description):
+ """
+ Construct a stream server endpoint from an endpoint description string.
+
+ The format for server endpoint descriptions is a simple string. It is a
+ prefix naming the type of endpoint, then a colon, then the arguments for
+ that endpoint.
+
+ For example, you can call it like this to create an endpoint that will
+ listen on TCP port 80::
+
+ serverFromString(reactor, "tcp:80")
+
+ Additional arguments may be specified as keywords, separated with colons.
+ For example, you can specify the interface for a TCP server endpoint to
+ bind to like this::
+
+ serverFromString(reactor, "tcp:80:interface=127.0.0.1")
+
+ SSL server endpoints may be specified with the 'ssl' prefix, and the
+ private key and certificate files may be specified by the C{privateKey} and
+ C{certKey} arguments::
+
+ serverFromString(reactor, "ssl:443:privateKey=key.pem:certKey=crt.pem")
+
+ If a private key file name (C{privateKey}) isn't provided, a "server.pem"
+ file is assumed to exist which contains the private key. If the certificate
+ file name (C{certKey}) isn't provided, the private key file is assumed to
+ contain the certificate as well.
+
+ You may escape colons in arguments with a backslash, which you will need to
+ use if you want to specify a full pathname argument on Windows::
+
+ serverFromString(reactor,
+ "ssl:443:privateKey=C\\:/key.pem:certKey=C\\:/cert.pem")
+
+ finally, the 'unix' prefix may be used to specify a filesystem UNIX socket,
+ optionally with a 'mode' argument to specify the mode of the socket file
+ created by C{listen}::
+
+ serverFromString(reactor, "unix:/var/run/finger")
+ serverFromString(reactor, "unix:/var/run/finger:mode=660")
+
+ This function is also extensible; new endpoint types may be registered as
+ L{IStreamServerEndpointStringParser} plugins. See that interface for more
+ information.
+
+ @param reactor: The server endpoint will be constructed with this reactor.
+
+ @param description: The strports description to parse.
+
+ @return: A new endpoint which can be used to listen with the parameters
+ given by by C{description}.
+
+ @rtype: L{IStreamServerEndpoint<twisted.internet.interfaces.IStreamServerEndpoint>}
+
+ @raise ValueError: when the 'description' string cannot be parsed.
+
+ @since: 10.2
+ """
+ return _serverFromStringLegacy(reactor, description, _NO_DEFAULT)
+
+
+
+def quoteStringArgument(argument):
+ """
+ Quote an argument to L{serverFromString} and L{clientFromString}. Since
+ arguments are separated with colons and colons are escaped with
+ backslashes, some care is necessary if, for example, you have a pathname,
+ you may be tempted to interpolate into a string like this::
+
+ serverFromString("ssl:443:privateKey=%s" % (myPathName,))
+
+ This may appear to work, but will have portability issues (Windows
+ pathnames, for example). Usually you should just construct the appropriate
+ endpoint type rather than interpolating strings, which in this case would
+ be L{SSL4ServerEndpoint}. There are some use-cases where you may need to
+ generate such a string, though; for example, a tool to manipulate a
+ configuration file which has strports descriptions in it. To be correct in
+ those cases, do this instead::
+
+ serverFromString("ssl:443:privateKey=%s" %
+ (quoteStringArgument(myPathName),))
+
+ @param argument: The part of the endpoint description string you want to
+ pass through.
+
+ @type argument: C{str}
+
+ @return: The quoted argument.
+
+ @rtype: C{str}
+ """
+ return argument.replace('\\', '\\\\').replace(':', '\\:')
+
+
+
+def _parseClientTCP(*args, **kwargs):
+ """
+ Perform any argument value coercion necessary for TCP client parameters.
+
+ Valid positional arguments to this function are host and port.
+
+ Valid keyword arguments to this function are all L{IReactorTCP.connectTCP}
+ arguments.
+
+ @return: The coerced values as a C{dict}.
+ """
+
+ if len(args) == 2:
+ kwargs['port'] = int(args[1])
+ kwargs['host'] = args[0]
+ elif len(args) == 1:
+ if 'host' in kwargs:
+ kwargs['port'] = int(args[0])
+ else:
+ kwargs['host'] = args[0]
+
+ try:
+ kwargs['port'] = int(kwargs['port'])
+ except KeyError:
+ pass
+
+ try:
+ kwargs['timeout'] = int(kwargs['timeout'])
+ except KeyError:
+ pass
+ return kwargs
+
+
+
+def _loadCAsFromDir(directoryPath):
+ """
+ Load certificate-authority certificate objects in a given directory.
+
+ @param directoryPath: a L{FilePath} pointing at a directory to load .pem
+ files from.
+
+ @return: a C{list} of L{OpenSSL.crypto.X509} objects.
+ """
+ from twisted.internet import ssl
+
+ caCerts = {}
+ for child in directoryPath.children():
+ if not child.basename().split('.')[-1].lower() == 'pem':
+ continue
+ try:
+ data = child.getContent()
+ except IOError:
+ # Permission denied, corrupt disk, we don't care.
+ continue
+ try:
+ theCert = ssl.Certificate.loadPEM(data)
+ except ssl.SSL.Error:
+ # Duplicate certificate, invalid certificate, etc. We don't care.
+ pass
+ else:
+ caCerts[theCert.digest()] = theCert.original
+ return caCerts.values()
+
+
+
+def _parseClientSSL(*args, **kwargs):
+ """
+ Perform any argument value coercion necessary for SSL client parameters.
+
+ Valid keyword arguments to this function are all L{IReactorSSL.connectSSL}
+ arguments except for C{contextFactory}. Instead, C{certKey} (the path name
+ of the certificate file) C{privateKey} (the path name of the private key
+ associated with the certificate) are accepted and used to construct a
+ context factory.
+
+ Valid positional arguments to this function are host and port.
+
+ @param caCertsDir: The one parameter which is not part of
+ L{IReactorSSL.connectSSL}'s signature, this is a path name used to
+ construct a list of certificate authority certificates. The directory
+ will be scanned for files ending in C{.pem}, all of which will be
+ considered valid certificate authorities for this connection.
+
+ @type caCertsDir: C{str}
+
+ @return: The coerced values as a C{dict}.
+ """
+ from twisted.internet import ssl
+ kwargs = _parseClientTCP(*args, **kwargs)
+ certKey = kwargs.pop('certKey', None)
+ privateKey = kwargs.pop('privateKey', None)
+ caCertsDir = kwargs.pop('caCertsDir', None)
+ if certKey is not None:
+ certx509 = ssl.Certificate.loadPEM(
+ FilePath(certKey).getContent()).original
+ else:
+ certx509 = None
+ if privateKey is not None:
+ privateKey = ssl.PrivateCertificate.loadPEM(
+ FilePath(privateKey).getContent()).privateKey.original
+ else:
+ privateKey = None
+ if caCertsDir is not None:
+ verify = True
+ caCerts = _loadCAsFromDir(FilePath(caCertsDir))
+ else:
+ verify = False
+ caCerts = None
+ kwargs['sslContextFactory'] = ssl.CertificateOptions(
+ method=ssl.SSL.SSLv23_METHOD,
+ certificate=certx509,
+ privateKey=privateKey,
+ verify=verify,
+ caCerts=caCerts
+ )
+ return kwargs
+
+
+
+def _parseClientUNIX(**kwargs):
+ """
+ Perform any argument value coercion necessary for UNIX client parameters.
+
+ Valid keyword arguments to this function are all L{IReactorUNIX.connectUNIX}
+ arguments except for C{checkPID}. Instead, C{lockfile} is accepted and has
+ the same meaning.
+
+ @return: The coerced values as a C{dict}.
+ """
+ try:
+ kwargs['checkPID'] = bool(int(kwargs.pop('lockfile')))
+ except KeyError:
+ pass
+ try:
+ kwargs['timeout'] = int(kwargs['timeout'])
+ except KeyError:
+ pass
+ return kwargs
+
+_clientParsers = {
+ 'TCP': _parseClientTCP,
+ 'SSL': _parseClientSSL,
+ 'UNIX': _parseClientUNIX,
+ }
+
+
+
+def clientFromString(reactor, description):
+ """
+ Construct a client endpoint from a description string.
+
+ Client description strings are much like server description strings,
+ although they take all of their arguments as keywords, aside from host and
+ port.
+
+ You can create a TCP client endpoint with the 'host' and 'port' arguments,
+ like so::
+
+ clientFromString(reactor, "tcp:host=www.example.com:port=80")
+
+ or, without specifying host and port keywords::
+
+ clientFromString(reactor, "tcp:www.example.com:80")
+
+ Or you can specify only one or the other, as in the following 2 examples::
+
+ clientFromString(reactor, "tcp:host=www.example.com:80")
+ clientFromString(reactor, "tcp:www.example.com:port=80")
+
+ or an SSL client endpoint with those arguments, plus the arguments used by
+ the server SSL, for a client certificate::
+
+ clientFromString(reactor, "ssl:web.example.com:443:"
+ "privateKey=foo.pem:certKey=foo.pem")
+
+ to specify your certificate trust roots, you can identify a directory with
+ PEM files in it with the C{caCertsDir} argument::
+
+ clientFromString(reactor, "ssl:host=web.example.com:port=443:"
+ "caCertsDir=/etc/ssl/certs")
+
+ This function is also extensible; new endpoint types may be registered as
+ L{IStreamClientEndpointStringParser} plugins. See that interface for more
+ information.
+
+ @param reactor: The client endpoint will be constructed with this reactor.
+
+ @param description: The strports description to parse.
+
+ @return: A new endpoint which can be used to connect with the parameters
+ given by by C{description}.
+ @rtype: L{IStreamClientEndpoint<twisted.internet.interfaces.IStreamClientEndpoint>}
+
+ @since: 10.2
+ """
+ args, kwargs = _parse(description)
+ aname = args.pop(0)
+ name = aname.upper()
+ for plugin in getPlugins(IStreamClientEndpointStringParser):
+ if plugin.prefix.upper() == name:
+ return plugin.parseStreamClient(*args, **kwargs)
+ if name not in _clientParsers:
+ raise ValueError("Unknown endpoint type: %r" % (aname,))
+ kwargs = _clientParsers[name](*args, **kwargs)
+ return _endpointClientFactories[name](reactor, **kwargs)
Added: CalendarServer/branches/users/glyph/ipv6-client/twext/backport/internet/tcp.py
===================================================================
--- CalendarServer/branches/users/glyph/ipv6-client/twext/backport/internet/tcp.py (rev 0)
+++ CalendarServer/branches/users/glyph/ipv6-client/twext/backport/internet/tcp.py 2012-04-13 18:43:14 UTC (rev 9055)
@@ -0,0 +1,1122 @@
+# -*- test-case-name: twisted.test.test_tcp -*-
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+Various asynchronous TCP/IP classes.
+
+End users shouldn't use this module directly - use the reactor APIs instead.
+"""
+
+
+# System Imports
+import types
+import socket
+import sys
+import operator
+import struct
+
+from zope.interface import implements
+
+from twisted.python.runtime import platformType
+from twisted.python import versions, deprecate
+
+try:
+ # Try to get the memory BIO based startTLS implementation, available since
+ # pyOpenSSL 0.10
+ from twisted.internet._newtls import (
+ ConnectionMixin as _TLSConnectionMixin,
+ ClientMixin as _TLSClientMixin,
+ ServerMixin as _TLSServerMixin)
+except ImportError:
+ try:
+ # Try to get the socket BIO based startTLS implementation, available in
+ # all pyOpenSSL versions
+ from twisted.internet._oldtls import (
+ ConnectionMixin as _TLSConnectionMixin,
+ ClientMixin as _TLSClientMixin,
+ ServerMixin as _TLSServerMixin)
+ except ImportError:
+ # There is no version of startTLS available
+ class _TLSConnectionMixin(object):
+ TLS = False
+ class _TLSClientMixin(object):
+ pass
+ class _TLSServerMixin(object):
+ pass
+
+if platformType == 'win32':
+ # no such thing as WSAEPERM or error code 10001 according to winsock.h or MSDN
+ EPERM = object()
+ from errno import WSAEINVAL as EINVAL
+ from errno import WSAEWOULDBLOCK as EWOULDBLOCK
+ from errno import WSAEINPROGRESS as EINPROGRESS
+ from errno import WSAEALREADY as EALREADY
+ from errno import WSAECONNRESET as ECONNRESET
+ from errno import WSAEISCONN as EISCONN
+ from errno import WSAENOTCONN as ENOTCONN
+ from errno import WSAEINTR as EINTR
+ from errno import WSAENOBUFS as ENOBUFS
+ from errno import WSAEMFILE as EMFILE
+ # No such thing as WSAENFILE, either.
+ ENFILE = object()
+ # Nor ENOMEM
+ ENOMEM = object()
+ EAGAIN = EWOULDBLOCK
+ from errno import WSAECONNRESET as ECONNABORTED
+
+ from twisted.python.win32 import formatError as strerror
+else:
+ from errno import EPERM
+ from errno import EINVAL
+ from errno import EWOULDBLOCK
+ from errno import EINPROGRESS
+ from errno import EALREADY
+ from errno import ECONNRESET
+ from errno import EISCONN
+ from errno import ENOTCONN
+ from errno import EINTR
+ from errno import ENOBUFS
+ from errno import EMFILE
+ from errno import ENFILE
+ from errno import ENOMEM
+ from errno import EAGAIN
+ from errno import ECONNABORTED
+
+ from os import strerror
+
+
+from errno import errorcode
+
+# Twisted Imports
+from twisted.internet import base, address, fdesc
+from twisted.internet.task import deferLater
+from twisted.python import log, failure, reflect
+from twisted.python.util import unsignedID
+from twisted.internet.error import CannotListenError
+from twisted.internet import abstract, main, interfaces, error
+
+# Not all platforms have, or support, this flag.
+_AI_NUMERICSERV = getattr(socket, "AI_NUMERICSERV", 0)
+
+
+
+class _SocketCloser(object):
+ _socketShutdownMethod = 'shutdown'
+
+ def _closeSocket(self, orderly):
+ # The call to shutdown() before close() isn't really necessary, because
+ # we set FD_CLOEXEC now, which will ensure this is the only process
+ # holding the FD, thus ensuring close() really will shutdown the TCP
+ # socket. However, do it anyways, just to be safe.
+ skt = self.socket
+ try:
+ if orderly:
+ getattr(skt, self._socketShutdownMethod)(2)
+ else:
+ # Set SO_LINGER to 1,0 which, by convention, causes a
+ # connection reset to be sent when close is called,
+ # instead of the standard FIN shutdown sequence.
+ self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
+ struct.pack("ii", 1, 0))
+
+ except socket.error:
+ pass
+ try:
+ skt.close()
+ except socket.error:
+ pass
+
+
+
+class _AbortingMixin(object):
+ """
+ Common implementation of C{abortConnection}.
+
+ @ivar _aborting: Set to C{True} when C{abortConnection} is called.
+ @type _aborting: C{bool}
+ """
+ _aborting = False
+
+ def abortConnection(self):
+ """
+ Aborts the connection immediately, dropping any buffered data.
+
+ @since: 11.1
+ """
+ if self.disconnected or self._aborting:
+ return
+ self._aborting = True
+ self.stopReading()
+ self.stopWriting()
+ self.doRead = lambda *args, **kwargs: None
+ self.doWrite = lambda *args, **kwargs: None
+ self.reactor.callLater(0, self.connectionLost,
+ failure.Failure(error.ConnectionAborted()))
+
+
+
+class Connection(_TLSConnectionMixin, abstract.FileDescriptor, _SocketCloser,
+ _AbortingMixin):
+ """
+ Superclass of all socket-based FileDescriptors.
+
+ This is an abstract superclass of all objects which represent a TCP/IP
+ connection based socket.
+
+ @ivar logstr: prefix used when logging events related to this connection.
+ @type logstr: C{str}
+ """
+ implements(interfaces.ITCPTransport, interfaces.ISystemHandle)
+
+
+ def __init__(self, skt, protocol, reactor=None):
+ abstract.FileDescriptor.__init__(self, reactor=reactor)
+ self.socket = skt
+ self.socket.setblocking(0)
+ self.fileno = skt.fileno
+ self.protocol = protocol
+
+
+ def getHandle(self):
+ """Return the socket for this connection."""
+ return self.socket
+
+
+ def doRead(self):
+ """Calls self.protocol.dataReceived with all available data.
+
+ This reads up to self.bufferSize bytes of data from its socket, then
+ calls self.dataReceived(data) to process it. If the connection is not
+ lost through an error in the physical recv(), this function will return
+ the result of the dataReceived call.
+ """
+ try:
+ data = self.socket.recv(self.bufferSize)
+ except socket.error, se:
+ if se.args[0] == EWOULDBLOCK:
+ return
+ else:
+ return main.CONNECTION_LOST
+ if not data:
+ return main.CONNECTION_DONE
+ rval = self.protocol.dataReceived(data)
+ if rval is not None:
+ offender = self.protocol.dataReceived
+ warningFormat = (
+ 'Returning a value other than None from %(fqpn)s is '
+ 'deprecated since %(version)s.')
+ warningString = deprecate.getDeprecationWarningString(
+ offender, versions.Version('Twisted', 11, 0, 0),
+ format=warningFormat)
+ deprecate.warnAboutFunction(offender, warningString)
+ return rval
+
+
+ def writeSomeData(self, data):
+ """
+ Write as much as possible of the given data to this TCP connection.
+
+ This sends up to C{self.SEND_LIMIT} bytes from C{data}. If the
+ connection is lost, an exception is returned. Otherwise, the number
+ of bytes successfully written is returned.
+ """
+ try:
+ # Limit length of buffer to try to send, because some OSes are too
+ # stupid to do so themselves (ahem windows)
+ return self.socket.send(buffer(data, 0, self.SEND_LIMIT))
+ except socket.error, se:
+ if se.args[0] == EINTR:
+ return self.writeSomeData(data)
+ elif se.args[0] in (EWOULDBLOCK, ENOBUFS):
+ return 0
+ else:
+ return main.CONNECTION_LOST
+
+
+ def _closeWriteConnection(self):
+ try:
+ getattr(self.socket, self._socketShutdownMethod)(1)
+ except socket.error:
+ pass
+ p = interfaces.IHalfCloseableProtocol(self.protocol, None)
+ if p:
+ try:
+ p.writeConnectionLost()
+ except:
+ f = failure.Failure()
+ log.err()
+ self.connectionLost(f)
+
+
+ def readConnectionLost(self, reason):
+ p = interfaces.IHalfCloseableProtocol(self.protocol, None)
+ if p:
+ try:
+ p.readConnectionLost()
+ except:
+ log.err()
+ self.connectionLost(failure.Failure())
+ else:
+ self.connectionLost(reason)
+
+
+
+ def connectionLost(self, reason):
+ """See abstract.FileDescriptor.connectionLost().
+ """
+ # Make sure we're not called twice, which can happen e.g. if
+ # abortConnection() is called from protocol's dataReceived and then
+ # code immediately after throws an exception that reaches the
+ # reactor. We can't rely on "disconnected" attribute for this check
+ # since twisted.internet._oldtls does evil things to it:
+ if not hasattr(self, "socket"):
+ return
+ abstract.FileDescriptor.connectionLost(self, reason)
+ self._closeSocket(not reason.check(error.ConnectionAborted))
+ protocol = self.protocol
+ del self.protocol
+ del self.socket
+ del self.fileno
+ protocol.connectionLost(reason)
+
+
+ logstr = "Uninitialized"
+
+ def logPrefix(self):
+ """Return the prefix to log with when I own the logging thread.
+ """
+ return self.logstr
+
+ def getTcpNoDelay(self):
+ return operator.truth(self.socket.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY))
+
+ def setTcpNoDelay(self, enabled):
+ self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, enabled)
+
+ def getTcpKeepAlive(self):
+ return operator.truth(self.socket.getsockopt(socket.SOL_SOCKET,
+ socket.SO_KEEPALIVE))
+
+ def setTcpKeepAlive(self, enabled):
+ self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, enabled)
+
+
+
+
+class _BaseBaseClient(object):
+ """
+ Code shared with other (non-POSIX) reactors for management of general
+ outgoing connections.
+
+ Requirements upon subclasses are documented as instance variables rather
+ than abstract methods, in order to avoid MRO confusion, since this base is
+ mixed in to unfortunately weird and distinctive multiple-inheritance
+ hierarchies and many of these attributes are provided by peer classes
+ rather than descendant classes in those hierarchies.
+
+ @ivar addressFamily: The address family constant (C{socket.AF_INET},
+ C{socket.AF_INET6}, C{socket.AF_UNIX}) of the underlying socket of this
+ client connection.
+ @type addressFamily: C{int}
+
+ @ivar socketType: The socket type constant (C{socket.SOCK_STREAM} or
+ C{socket.SOCK_DGRAM}) of the underlying socket.
+ @type socketType: C{int}
+
+ @ivar _requiresResolution: A flag indicating whether the address of this
+ client will require name resolution. C{True} if the hostname of said
+ address indicates a name that must be resolved by hostname lookup,
+ C{False} if it indicates an IP address literal.
+ @type _requiresResolution: C{bool}
+
+ @cvar _commonConnection: Subclasses must provide this attribute, which
+ indicates the L{Connection}-alike class to invoke C{__init__} and
+ C{connectionLost} on.
+ @type _commonConnection: C{type}
+
+ @ivar _stopReadingAndWriting: Subclasses must implement in order to remove
+ this transport from its reactor's notifications in response to a
+ terminated connection attempt.
+ @type _stopReadingAndWriting: 0-argument callable returning C{None}
+
+ @ivar _closeSocket: Subclasses must implement in order to close the socket
+ in response to a terminated connection attempt.
+ @type _closeSocket: 1-argument callable; see L{_SocketCloser._closeSocket}
+
+ @ivar _collectSocketDetails: Clean up references to the attached socket in
+ its underlying OS resource (such as a file descriptor or file handle),
+ as part of post connection-failure cleanup.
+ @type _collectSocketDetails: 0-argument callable returning C{None}.
+
+ @ivar reactor: The class pointed to by C{_commonConnection} should set this
+ attribute in its constructor.
+ @type reactor: L{twisted.internet.interfaces.IReactorTime},
+ L{twisted.internet.interfaces.IReactorCore},
+ L{twisted.internet.interfaces.IReactorFDSet}
+ """
+
+ addressFamily = socket.AF_INET
+ socketType = socket.SOCK_STREAM
+
+ def _finishInit(self, whenDone, skt, error, reactor):
+ """
+ Called by subclasses to continue to the stage of initialization where
+ the socket connect attempt is made.
+
+ @param whenDone: A 0-argument callable to invoke once the connection is
+ set up. This is C{None} if the connection could not be prepared
+ due to a previous error.
+
+ @param skt: The socket object to use to perform the connection.
+ @type skt: C{socket._socketobject}
+
+ @param error: The error to fail the connection with.
+
+ @param reactor: The reactor to use for this client.
+ @type reactor: L{twisted.internet.interfaces.IReactorTime}
+ """
+ if whenDone:
+ self._commonConnection.__init__(self, skt, None, reactor)
+ reactor.callLater(0, whenDone)
+ else:
+ reactor.callLater(0, self.failIfNotConnected, error)
+
+
+ def resolveAddress(self):
+ """
+ Resolve the name that was passed to this L{_BaseBaseClient}, if
+ necessary, and then move on to attempting the connection once an
+ address has been determined. (The connection will be attempted
+ immediately within this function if either name resolution can be
+ synchronous or the address was an IP address literal.)
+
+ @note: You don't want to call this method from outside, as it won't do
+ anything useful; it's just part of the connection bootstrapping
+ process. Also, although this method is on L{_BaseBaseClient} for
+ historical reasons, it's not used anywhere except for L{Client}
+ itself.
+
+ @return: C{None}
+ """
+ if self._requiresResolution:
+ d = self.reactor.resolve(self.addr[0])
+ d.addCallback(lambda n: (n,) + self.addr[1:])
+ d.addCallbacks(self._setRealAddress, self.failIfNotConnected)
+ else:
+ self._setRealAddress(self.addr)
+
+
+ def _setRealAddress(self, address):
+ """
+ Set the resolved address of this L{_BaseBaseClient} and initiate the
+ connection attempt.
+
+ @param address: Depending on whether this is an IPv4 or IPv6 connection
+ attempt, a 2-tuple of C{(host, port)} or a 4-tuple of C{(host,
+ port, flow, scope)}. At this point it is a fully resolved address,
+ and the 'host' portion will always be an IP address, not a DNS
+ name.
+ """
+ self.realAddress = address
+ self.doConnect()
+
+
+ def failIfNotConnected(self, err):
+ """
+ Generic method called when the attemps to connect failed. It basically
+ cleans everything it can: call connectionFailed, stop read and write,
+ delete socket related members.
+ """
+ if (self.connected or self.disconnected or
+ not hasattr(self, "connector")):
+ return
+
+ self._stopReadingAndWriting()
+ try:
+ self._closeSocket(True)
+ except AttributeError:
+ pass
+ else:
+ self._collectSocketDetails()
+ self.connector.connectionFailed(failure.Failure(err))
+ del self.connector
+
+
+ def stopConnecting(self):
+ """
+ If a connection attempt is still outstanding (i.e. no connection is
+ yet established), immediately stop attempting to connect.
+ """
+ self.failIfNotConnected(error.UserError())
+
+
+ def connectionLost(self, reason):
+ """
+ Invoked by lower-level logic when it's time to clean the socket up.
+ Depending on the state of the connection, either inform the attached
+ L{Connector} that the connection attempt has failed, or inform the
+ connected L{IProtocol} that the established connection has been lost.
+
+ @param reason: the reason that the connection was terminated
+ @type reason: L{Failure}
+ """
+ if not self.connected:
+ self.failIfNotConnected(error.ConnectError(string=reason))
+ else:
+ self._commonConnection.connectionLost(self, reason)
+ self.connector.connectionLost(reason)
+
+
+
+class BaseClient(_BaseBaseClient, _TLSClientMixin, Connection):
+ """
+ A base class for client TCP (and similiar) sockets.
+
+ @ivar realAddress: The address object that will be used for socket.connect;
+ this address is an address tuple (the number of elements dependent upon
+ the address family) which does not contain any names which need to be
+ resolved.
+ @type realAddress: C{tuple}
+
+ @ivar _base: L{Connection}, which is the base class of this class which has
+ all of the useful file descriptor methods. This is used by
+ L{_TLSServerMixin} to call the right methods to directly manipulate the
+ transport, as is necessary for writing TLS-encrypted bytes (whereas
+ those methods on L{Server} will go through another layer of TLS if it
+ has been enabled).
+ """
+
+ _base = Connection
+ _commonConnection = Connection
+
+ def _stopReadingAndWriting(self):
+ """
+ Implement the POSIX-ish (i.e.
+ L{twisted.internet.interfaces.IReactorFDSet}) method of detaching this
+ socket from the reactor for L{_BaseBaseClient}.
+ """
+ if hasattr(self, "reactor"):
+ # this doesn't happen if we failed in __init__
+ self.stopReading()
+ self.stopWriting()
+
+
+ def _collectSocketDetails(self):
+ """
+ Clean up references to the socket and its file descriptor.
+
+ @see: L{_BaseBaseClient}
+ """
+ del self.socket, self.fileno
+
+
+ def createInternetSocket(self):
+ """(internal) Create a non-blocking socket using
+ self.addressFamily, self.socketType.
+ """
+ s = socket.socket(self.addressFamily, self.socketType)
+ s.setblocking(0)
+ fdesc._setCloseOnExec(s.fileno())
+ return s
+
+
+ def doConnect(self):
+ """
+ Initiate the outgoing connection attempt.
+
+ @note: Applications do not need to call this method; it will be invoked
+ internally as part of L{IReactorTCP.connectTCP}.
+ """
+ self.doWrite = self.doConnect
+ self.doRead = self.doConnect
+ if not hasattr(self, "connector"):
+ # this happens when connection failed but doConnect
+ # was scheduled via a callLater in self._finishInit
+ return
+
+ err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
+ if err:
+ self.failIfNotConnected(error.getConnectError((err, strerror(err))))
+ return
+
+ # doConnect gets called twice. The first time we actually need to
+ # start the connection attempt. The second time we don't really
+ # want to (SO_ERROR above will have taken care of any errors, and if
+ # it reported none, the mere fact that doConnect was called again is
+ # sufficient to indicate that the connection has succeeded), but it
+ # is not /particularly/ detrimental to do so. This should get
+ # cleaned up some day, though.
+ try:
+ connectResult = self.socket.connect_ex(self.realAddress)
+ except socket.error, se:
+ connectResult = se.args[0]
+ if connectResult:
+ if connectResult == EISCONN:
+ pass
+ # on Windows EINVAL means sometimes that we should keep trying:
+ # http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/connect_2.asp
+ elif ((connectResult in (EWOULDBLOCK, EINPROGRESS, EALREADY)) or
+ (connectResult == EINVAL and platformType == "win32")):
+ self.startReading()
+ self.startWriting()
+ return
+ else:
+ self.failIfNotConnected(error.getConnectError((connectResult, strerror(connectResult))))
+ return
+
+ # If I have reached this point without raising or returning, that means
+ # that the socket is connected.
+ del self.doWrite
+ del self.doRead
+ # we first stop and then start, to reset any references to the old doRead
+ self.stopReading()
+ self.stopWriting()
+ self._connectDone()
+
+
+ def _connectDone(self):
+ """
+ This is a hook for when a connection attempt has succeeded.
+
+ Here, we build the protocol from the
+ L{twisted.internet.protocol.ClientFactory} that was passed in, compute
+ a log string, begin reading so as to send traffic to the newly built
+ protocol, and finally hook up the protocol itself.
+
+ This hook is overridden by L{ssl.Client} to initiate the TLS protocol.
+ """
+ self.protocol = self.connector.buildProtocol(self.getPeer())
+ self.connected = 1
+ logPrefix = self._getLogPrefix(self.protocol)
+ self.logstr = "%s,client" % logPrefix
+ self.startReading()
+ self.protocol.makeConnection(self)
+
+
+
+_NUMERIC_ONLY = socket.AI_NUMERICHOST | _AI_NUMERICSERV
+
+def _resolveIPv6(ip, port):
+ """
+ Resolve an IPv6 literal into an IPv6 address.
+
+ This is necessary to resolve any embedded scope identifiers to the relevant
+ C{sin6_scope_id} for use with C{socket.connect()}, C{socket.listen()}, or
+ C{socket.bind()}; see U{RFC 3493 <https://tools.ietf.org/html/rfc3493>} for
+ more information.
+
+ @param ip: An IPv6 address literal.
+ @type ip: C{str}
+
+ @param port: A port number.
+ @type port: C{int}
+
+ @return: a 4-tuple of C{(host, port, flow, scope)}, suitable for use as an
+ IPv6 address.
+
+ @raise socket.gaierror: if either the IP or port is not numeric as it
+ should be.
+ """
+ return socket.getaddrinfo(ip, port, 0, 0, 0, _NUMERIC_ONLY)[0][4]
+
+
+
+class _BaseTCPClient(object):
+ """
+ Code shared with other (non-POSIX) reactors for management of outgoing TCP
+ connections (both TCPv4 and TCPv6).
+
+ @note: In order to be functional, this class must be mixed into the same
+ hierarchy as L{_BaseBaseClient}. It would subclass L{_BaseBaseClient}
+ directly, but the class hierarchy here is divided in strange ways out
+ of the need to share code along multiple axes; specifically, with the
+ IOCP reactor and also with UNIX clients in other reactors.
+
+ @ivar _addressType: The Twisted _IPAddress implementation for this client
+ @type _addressType: L{IPv4Address} or L{IPv6Address}
+
+ @ivar connector: The L{Connector} which is driving this L{_BaseTCPClient}'s
+ connection attempt.
+
+ @ivar addr: The address that this socket will be connecting to.
+ @type addr: If IPv4, a 2-C{tuple} of C{(str host, int port)}. If IPv6, a
+ 4-C{tuple} of (C{str host, int port, int ignored, int scope}).
+
+ @ivar createInternetSocket: Subclasses must implement this as a method to
+ create a python socket object of the appropriate address family and
+ socket type.
+ @type createInternetSocket: 0-argument callable returning
+ C{socket._socketobject}.
+ """
+
+ _addressType = address.IPv4Address
+
+ def __init__(self, host, port, bindAddress, connector, reactor=None):
+ # BaseClient.__init__ is invoked later
+ self.connector = connector
+ self.addr = (host, port)
+
+ whenDone = self.resolveAddress
+ err = None
+ skt = None
+
+ if abstract.isIPAddress(host):
+ self._requiresResolution = False
+ elif abstract.isIPv6Address(host):
+ self._requiresResolution = False
+ self.addr = _resolveIPv6(host, port)
+ self.addressFamily = socket.AF_INET6
+ self._addressType = address.IPv6Address
+ else:
+ self._requiresResolution = True
+ try:
+ skt = self.createInternetSocket()
+ except socket.error, se:
+ err = error.ConnectBindError(se.args[0], se.args[1])
+ whenDone = None
+ if whenDone and bindAddress is not None:
+ try:
+ if abstract.isIPv6Address(bindAddress[0]):
+ bindinfo = _resolveIPv6(*bindAddress)
+ else:
+ bindinfo = bindAddress
+ skt.bind(bindinfo)
+ except socket.error, se:
+ err = error.ConnectBindError(se.args[0], se.args[1])
+ whenDone = None
+ self._finishInit(whenDone, skt, err, reactor)
+
+
+ def getHost(self):
+ """
+ Returns an L{IPv4Address} or L{IPv6Address}.
+
+ This indicates the address from which I am connecting.
+ """
+ return self._addressType('TCP', *self.socket.getsockname()[:2])
+
+
+ def getPeer(self):
+ """
+ Returns an L{IPv4Address} or L{IPv6Address}.
+
+ This indicates the address that I am connected to.
+ """
+ # an ipv6 realAddress has more than two elements, but the IPv6Address
+ # constructor still only takes two.
+ return self._addressType('TCP', *self.realAddress[:2])
+
+
+ def __repr__(self):
+ s = '<%s to %s at %x>' % (self.__class__, self.addr, unsignedID(self))
+ return s
+
+
+
+class Client(_BaseTCPClient, BaseClient):
+ """
+ A transport for a TCP protocol; either TCPv4 or TCPv6.
+
+ Do not create these directly; use L{IReactorTCP.connectTCP}.
+ """
+
+
+
+class Server(_TLSServerMixin, Connection):
+ """
+ Serverside socket-stream connection class.
+
+ This is a serverside network connection transport; a socket which came from
+ an accept() on a server.
+
+ @ivar _base: L{Connection}, which is the base class of this class which has
+ all of the useful file descriptor methods. This is used by
+ L{_TLSServerMixin} to call the right methods to directly manipulate the
+ transport, as is necessary for writing TLS-encrypted bytes (whereas
+ those methods on L{Server} will go through another layer of TLS if it
+ has been enabled).
+ """
+ _base = Connection
+
+ _addressType = address.IPv4Address
+
+ def __init__(self, sock, protocol, client, server, sessionno, reactor):
+ """
+ Server(sock, protocol, client, server, sessionno)
+
+ Initialize it with a socket, a protocol, a descriptor for my peer (a
+ tuple of host, port describing the other end of the connection), an
+ instance of Port, and a session number.
+ """
+ Connection.__init__(self, sock, protocol, reactor)
+ if len(client) != 2:
+ self._addressType = address.IPv6Address
+ self.server = server
+ self.client = client
+ self.sessionno = sessionno
+ self.hostname = client[0]
+
+ logPrefix = self._getLogPrefix(self.protocol)
+ self.logstr = "%s,%s,%s" % (logPrefix,
+ sessionno,
+ self.hostname)
+ self.repstr = "<%s #%s on %s>" % (self.protocol.__class__.__name__,
+ self.sessionno,
+ self.server._realPortNumber)
+ self.startReading()
+ self.connected = 1
+
+ def __repr__(self):
+ """A string representation of this connection.
+ """
+ return self.repstr
+
+
+ def getHost(self):
+ """
+ Returns an L{IPv4Address} or L{IPv6Address}.
+
+ This indicates the server's address.
+ """
+ host, port = self.socket.getsockname()[:2]
+ return self._addressType('TCP', host, port)
+
+
+ def getPeer(self):
+ """
+ Returns an L{IPv4Address} or L{IPv6Address}.
+
+ This indicates the client's address.
+ """
+ return self._addressType('TCP', *self.client[:2])
+
+
+
+class Port(base.BasePort, _SocketCloser):
+ """
+ A TCP server port, listening for connections.
+
+ When a connection is accepted, this will call a factory's buildProtocol
+ with the incoming address as an argument, according to the specification
+ described in L{twisted.internet.interfaces.IProtocolFactory}.
+
+ If you wish to change the sort of transport that will be used, the
+ C{transport} attribute will be called with the signature expected for
+ C{Server.__init__}, so it can be replaced.
+
+ @ivar deferred: a deferred created when L{stopListening} is called, and
+ that will fire when connection is lost. This is not to be used it
+ directly: prefer the deferred returned by L{stopListening} instead.
+ @type deferred: L{defer.Deferred}
+
+ @ivar disconnecting: flag indicating that the L{stopListening} method has
+ been called and that no connections should be accepted anymore.
+ @type disconnecting: C{bool}
+
+ @ivar connected: flag set once the listen has successfully been called on
+ the socket.
+ @type connected: C{bool}
+
+ @ivar _type: A string describing the connections which will be created by
+ this port. Normally this is C{"TCP"}, since this is a TCP port, but
+ when the TLS implementation re-uses this class it overrides the value
+ with C{"TLS"}. Only used for logging.
+
+ @ivar _preexistingSocket: If not C{None}, a L{socket.socket} instance which
+ was created and initialized outside of the reactor and will be used to
+ listen for connections (instead of a new socket being created by this
+ L{Port}).
+ """
+
+ implements(interfaces.IListeningPort)
+
+ socketType = socket.SOCK_STREAM
+
+ transport = Server
+ sessionno = 0
+ interface = ''
+ backlog = 50
+
+ _type = 'TCP'
+
+ # Actual port number being listened on, only set to a non-None
+ # value when we are actually listening.
+ _realPortNumber = None
+
+ # An externally initialized socket that we will use, rather than creating
+ # our own.
+ _preexistingSocket = None
+
+ addressFamily = socket.AF_INET
+ _addressType = address.IPv4Address
+
+ def __init__(self, port, factory, backlog=50, interface='', reactor=None):
+ """Initialize with a numeric port to listen on.
+ """
+ base.BasePort.__init__(self, reactor=reactor)
+ self.port = port
+ self.factory = factory
+ self.backlog = backlog
+ if abstract.isIPv6Address(interface):
+ self.addressFamily = socket.AF_INET6
+ self._addressType = address.IPv6Address
+ self.interface = interface
+
+
+ @classmethod
+ def _fromListeningDescriptor(cls, reactor, fd, addressFamily, factory):
+ """
+ Create a new L{Port} based on an existing listening I{SOCK_STREAM}
+ I{AF_INET} socket.
+
+ Arguments are the same as to L{Port.__init__}, except where noted.
+
+ @param fd: An integer file descriptor associated with a listening
+ socket. The socket must be in non-blocking mode. Any additional
+ attributes desired, such as I{FD_CLOEXEC}, must also be set already.
+
+ @param addressFamily: The address family (sometimes called I{domain}) of
+ the existing socket. For example, L{socket.AF_INET}.
+
+ @return: A new instance of C{cls} wrapping the socket given by C{fd}.
+ """
+ port = socket.fromfd(fd, addressFamily, cls.socketType)
+ interface = port.getsockname()[0]
+ self = cls(None, factory, None, interface, reactor)
+ self._preexistingSocket = port
+ return self
+
+
+ def __repr__(self):
+ if self._realPortNumber is not None:
+ return "<%s of %s on %s>" % (self.__class__,
+ self.factory.__class__, self._realPortNumber)
+ else:
+ return "<%s of %s (not listening)>" % (self.__class__, self.factory.__class__)
+
+ def createInternetSocket(self):
+ s = base.BasePort.createInternetSocket(self)
+ if platformType == "posix" and sys.platform != "cygwin":
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ return s
+
+
+ def startListening(self):
+ """Create and bind my socket, and begin listening on it.
+
+ This is called on unserialization, and must be called after creating a
+ server to begin listening on the specified port.
+ """
+ if self._preexistingSocket is None:
+ # Create a new socket and make it listen
+ try:
+ skt = self.createInternetSocket()
+ if self.addressFamily == socket.AF_INET6:
+ addr = _resolveIPv6(self.interface, self.port)
+ else:
+ addr = (self.interface, self.port)
+ skt.bind(addr)
+ except socket.error, le:
+ raise CannotListenError, (self.interface, self.port, le)
+ skt.listen(self.backlog)
+ else:
+ # Re-use the externally specified socket
+ skt = self._preexistingSocket
+ self._preexistingSocket = None
+
+ # Make sure that if we listened on port 0, we update that to
+ # reflect what the OS actually assigned us.
+ self._realPortNumber = skt.getsockname()[1]
+
+ log.msg("%s starting on %s" % (
+ self._getLogPrefix(self.factory), self._realPortNumber))
+
+ # The order of the next 5 lines is kind of bizarre. If no one
+ # can explain it, perhaps we should re-arrange them.
+ self.factory.doStart()
+ self.connected = True
+ self.socket = skt
+ self.fileno = self.socket.fileno
+ self.numberAccepts = 100
+
+ self.startReading()
+
+
+ def _buildAddr(self, address):
+ host, port = address[:2]
+ return self._addressType('TCP', host, port)
+
+
+ def doRead(self):
+ """Called when my socket is ready for reading.
+
+ This accepts a connection and calls self.protocol() to handle the
+ wire-level protocol.
+ """
+ try:
+ if platformType == "posix":
+ numAccepts = self.numberAccepts
+ else:
+ # win32 event loop breaks if we do more than one accept()
+ # in an iteration of the event loop.
+ numAccepts = 1
+ for i in range(numAccepts):
+ # we need this so we can deal with a factory's buildProtocol
+ # calling our loseConnection
+ if self.disconnecting:
+ return
+ try:
+ skt, addr = self.socket.accept()
+ except socket.error, e:
+ if e.args[0] in (EWOULDBLOCK, EAGAIN):
+ self.numberAccepts = i
+ break
+ elif e.args[0] == EPERM:
+ # Netfilter on Linux may have rejected the
+ # connection, but we get told to try to accept()
+ # anyway.
+ continue
+ elif e.args[0] in (EMFILE, ENOBUFS, ENFILE, ENOMEM, ECONNABORTED):
+
+ # Linux gives EMFILE when a process is not allowed
+ # to allocate any more file descriptors. *BSD and
+ # Win32 give (WSA)ENOBUFS. Linux can also give
+ # ENFILE if the system is out of inodes, or ENOMEM
+ # if there is insufficient memory to allocate a new
+ # dentry. ECONNABORTED is documented as possible on
+ # both Linux and Windows, but it is not clear
+ # whether there are actually any circumstances under
+ # which it can happen (one might expect it to be
+ # possible if a client sends a FIN or RST after the
+ # server sends a SYN|ACK but before application code
+ # calls accept(2), however at least on Linux this
+ # _seems_ to be short-circuited by syncookies.
+
+ log.msg("Could not accept new connection (%s)" % (
+ errorcode[e.args[0]],))
+ break
+ raise
+
+ fdesc._setCloseOnExec(skt.fileno())
+ protocol = self.factory.buildProtocol(self._buildAddr(addr))
+ if protocol is None:
+ skt.close()
+ continue
+ s = self.sessionno
+ self.sessionno = s+1
+ transport = self.transport(skt, protocol, addr, self, s, self.reactor)
+ protocol.makeConnection(transport)
+ else:
+ self.numberAccepts = self.numberAccepts+20
+ except:
+ # Note that in TLS mode, this will possibly catch SSL.Errors
+ # raised by self.socket.accept()
+ #
+ # There is no "except SSL.Error:" above because SSL may be
+ # None if there is no SSL support. In any case, all the
+ # "except SSL.Error:" suite would probably do is log.deferr()
+ # and return, so handling it here works just as well.
+ log.deferr()
+
+ def loseConnection(self, connDone=failure.Failure(main.CONNECTION_DONE)):
+ """
+ Stop accepting connections on this port.
+
+ This will shut down the socket and call self.connectionLost(). It
+ returns a deferred which will fire successfully when the port is
+ actually closed, or with a failure if an error occurs shutting down.
+ """
+ self.disconnecting = True
+ self.stopReading()
+ if self.connected:
+ self.deferred = deferLater(
+ self.reactor, 0, self.connectionLost, connDone)
+ return self.deferred
+
+ stopListening = loseConnection
+
+ def _logConnectionLostMsg(self):
+ """
+ Log message for closing port
+ """
+ log.msg('(%s Port %s Closed)' % (self._type, self._realPortNumber))
+
+
+ def connectionLost(self, reason):
+ """
+ Cleans up the socket.
+ """
+ self._logConnectionLostMsg()
+ self._realPortNumber = None
+
+ base.BasePort.connectionLost(self, reason)
+ self.connected = False
+ self._closeSocket(True)
+ del self.socket
+ del self.fileno
+
+ try:
+ self.factory.doStop()
+ finally:
+ self.disconnecting = False
+
+
+ def logPrefix(self):
+ """Returns the name of my class, to prefix log entries with.
+ """
+ return reflect.qual(self.factory.__class__)
+
+
+ def getHost(self):
+ """
+ Return an L{IPv4Address} or L{IPv6Address} indicating the listening
+ address of this port.
+ """
+ host, port = self.socket.getsockname()[:2]
+ return self._addressType('TCP', host, port)
+
+
+
+class Connector(base.BaseConnector):
+ """
+ A L{Connector} provides of L{twisted.internet.interfaces.IConnector} for
+ all POSIX-style reactors.
+
+ @ivar _addressType: the type returned by L{Connector.getDestination}.
+ Either L{IPv4Address} or L{IPv6Address}, depending on the type of
+ address.
+ @type _addressType: C{type}
+ """
+ _addressType = address.IPv4Address
+
+ def __init__(self, host, port, factory, timeout, bindAddress, reactor=None):
+ if isinstance(port, types.StringTypes):
+ try:
+ port = socket.getservbyname(port, 'tcp')
+ except socket.error, e:
+ raise error.ServiceNameUnknownError(string="%s (%r)" % (e, port))
+ self.host, self.port = host, port
+ if abstract.isIPv6Address(host):
+ self._addressType = address.IPv6Address
+ self.bindAddress = bindAddress
+ base.BaseConnector.__init__(self, factory, timeout, reactor)
+
+
+ def _makeTransport(self):
+ """
+ Create a L{Client} bound to this L{Connector}.
+
+ @return: a new L{Client}
+ @rtype: L{Client}
+ """
+ return Client(self.host, self.port, self.bindAddress, self, self.reactor)
+
+
+ def getDestination(self):
+ """
+ @see: L{twisted.internet.interfaces.IConnector.getDestination}.
+ """
+ return self._addressType('TCP', self.host, self.port)
+
+
Modified: CalendarServer/branches/users/glyph/ipv6-client/twext/patches.py
===================================================================
--- CalendarServer/branches/users/glyph/ipv6-client/twext/patches.py 2012-04-13 18:40:22 UTC (rev 9054)
+++ CalendarServer/branches/users/glyph/ipv6-client/twext/patches.py 2012-04-13 18:43:14 UTC (rev 9055)
@@ -1,5 +1,5 @@
##
-# Copyright (c) 2005-2010 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2012 Apple Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -20,6 +20,65 @@
__all__ = []
+import sys
+
+from twisted import version
+from twisted.python.versions import Version
+from twisted.python.modules import getModule
+
+def _hasIPv6ClientSupport():
+ """
+ Does the loaded version of Twisted have IPv6 client support?
+ """
+ lastVersionWithoutIPv6Clients = Version("twisted", 12, 0, 0)
+ if version > lastVersionWithoutIPv6Clients:
+ return True
+ elif version == lastVersionWithoutIPv6Clients:
+ # It could be a snapshot of trunk or a branch with this bug fixed. Don't
+ # load the module, though, as that would be a bunch of unnecessary work.
+ return "_resolveIPv6" in (getModule("twisted.internet.tcp")
+ .filePath.getContent())
+ else:
+ return False
+
+
+
+def _addBackports():
+ """
+ We currently require 2 backported bugfixes from a future release of Twisted,
+ for IPv6 support:
+
+ - U{IPv6 client support <http://tm.tl/5085>}
+
+ - U{TCP endpoint cancellation <http://tm.tl/4710>}
+
+ This function will activate those backports. (Note it must be run before
+ any of the modules in question are imported or it will raise an exception.)
+
+ This function, L{_hasIPv6ClientSupport}, and all the associated backports
+ (i.e., all of C{twext/backport}) should be removed upon upgrading our
+ minimum required Twisted version.
+ """
+ from twext.backport import internet as bpinternet
+ from twisted import internet
+ internet.__path__[:] = bpinternet.__path__ + internet.__path__
+
+ # Make sure none of the backports are loaded yet.
+ backports = getModule("twext.backport.internet")
+ for submod in backports.iterModules():
+ subname = submod.name.split(".")[-1]
+ tiname = 'twisted.internet.' + subname
+ if tiname in sys.modules:
+ raise RuntimeError(
+ tiname + "already loaded, cannot load required backport")
+
+
+
+if not _hasIPv6ClientSupport():
+ _addBackports()
+
+
+
from twisted.mail.imap4 import Command
Command._1_RESPONSES += tuple(['BYE'])
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120413/6364222d/attachment-0001.html>
More information about the calendarserver-changes
mailing list