[CalendarServer-changes] [9058] CalendarServer/branches/users/glyph/ipv6-client/twext/internet

source_changes at macosforge.org source_changes at macosforge.org
Fri Apr 13 11:44:01 PDT 2012


Revision: 9058
          http://trac.macosforge.org/projects/calendarserver/changeset/9058
Author:   glyph at apple.com
Date:     2012-04-13 11:44:01 -0700 (Fri, 13 Apr 2012)
Log Message:
-----------
Adapter endpoint that can wrap around a client factory.

Added Paths:
-----------
    CalendarServer/branches/users/glyph/ipv6-client/twext/internet/adaptendpoint.py
    CalendarServer/branches/users/glyph/ipv6-client/twext/internet/test/test_adaptendpoint.py

Added: CalendarServer/branches/users/glyph/ipv6-client/twext/internet/adaptendpoint.py
===================================================================
--- CalendarServer/branches/users/glyph/ipv6-client/twext/internet/adaptendpoint.py	                        (rev 0)
+++ CalendarServer/branches/users/glyph/ipv6-client/twext/internet/adaptendpoint.py	2012-04-13 18:44:01 UTC (rev 9058)
@@ -0,0 +1,131 @@
+# -*- test-case-name: twext.internet.test.test_adaptendpoint -*-
+##
+# Copyright (c) 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.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+"""
+Adapter for old-style connectTCP/connectSSL code to use endpoints and be happy;
+specifically, to receive the additional duplicate notifications that it wants to
+receive, L{clientConnectionLost} and L{clientConnectionFailed} on the factory.
+"""
+
+from zope.interface import implements
+
+from twisted.internet.interfaces import IConnector
+
+from twisted.internet.protocol import Factory
+from twisted.python import log
+
+
+
+class _WrappedProtocol(object):
+    """
+    A wrapped protocol.
+    """
+
+    def __init__(self, wrapped, wrapper):
+        self._wrapped = wrapped
+        self._wrapper = wrapper
+
+
+    def __getattr__(self, attr):
+        """
+        Relay all undefined methods to the wrapped protocol.
+        """
+        return getattr(self._wrapped, attr)
+
+
+    def connectionLost(self, reason):
+        try:
+            self._wrapped.connectionLost(reason)
+        except:
+            log.err()
+        self._wrapper.callClientConnectionLost(reason)
+
+
+
+class LegacyConnector(object):
+    """
+    Legacy IConnector interface implementation for stuff that uses endpoints.
+    """
+    implements(IConnector)
+
+
+    def __init__(self, wrapper):
+        self.wrapper = wrapper
+
+
+    def getDestination(self):
+        """
+        I don't know, endpoints don't have a destination.
+        """
+        return self.wrapper.endpoint
+
+
+    def connect(self):
+        self.wrapper.beginConnectionAttempt()
+
+
+    def stopConnecting(self):
+        self.wrapper.stopConnectionAttempt()
+        self.wrapper.endpoint.connect(self.wrapper)
+
+
+    def disconnect(self):
+        pass
+
+
+
+class LegacyClientFactoryWrapper(Factory):
+
+    def __init__(self, legacyFactory, endpoint):
+        self.currentlyConnecting = False
+        self.legacyFactory = legacyFactory
+        self.endpoint = endpoint
+        self._connectedProtocol = None
+
+
+    def buildProtocol(self, addr):
+        return _WrappedProtocol(self.legacyFactory.buildProtocol(addr), self)
+
+
+    def callStartedConnecting(self):
+        self.legacyFactory.startedConnecting(LegacyConnector(self))
+
+
+    def callClientConnectionLost(self, reason):
+        self.legacyFactory.clientConnectionLost(LegacyConnector(self), reason)
+
+
+    def callClientConnectionFailed(self):
+        self.legacyFactory.clientConnectionFailed()
+
+
+    def disconnect(self):
+        if self._connectedProtocol is not None:
+            self._connectedProtocol.transport.abortConnection()
+        else:
+            pass
+
+# from twisted.internet.interfaces import IStreamClientEndpoint
+
+def connect(endpoint, clientFactory):
+    """
+    Connect a L{twisted.internet.protocol.ClientFactory} using the given
+    L{twisted.internet.interfaces.IStreamClientEndpoint}.
+    """
+    wrap = LegacyClientFactoryWrapper(clientFactory, endpoint)
+    endpoint.connect(wrap)
+    return LegacyConnector(wrap)
+

Added: CalendarServer/branches/users/glyph/ipv6-client/twext/internet/test/test_adaptendpoint.py
===================================================================
--- CalendarServer/branches/users/glyph/ipv6-client/twext/internet/test/test_adaptendpoint.py	                        (rev 0)
+++ CalendarServer/branches/users/glyph/ipv6-client/twext/internet/test/test_adaptendpoint.py	2012-04-13 18:44:01 UTC (rev 9058)
@@ -0,0 +1,143 @@
+##
+# Copyright (c) 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.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+"""
+Tests for L{twext.internet.adaptendpoint}.
+"""
+
+from zope.interface.verify import verifyObject
+
+from twext.internet.adaptendpoint import connect
+from twisted.internet.defer import Deferred
+from twisted.python.failure import Failure
+
+from twisted.internet.protocol import ClientFactory, Protocol
+from twisted.internet.interfaces import IConnector
+from twisted.trial.unittest import TestCase
+
+class names(object):
+    def __init__(self, **kw):
+        self.__dict__.update(kw)
+
+
+class RecordingProtocol(Protocol, object):
+    def __init__(self):
+        super(RecordingProtocol, self).__init__()
+        self.made = []
+        self.data = []
+        self.lost = []
+
+
+    def connectionMade(self):
+        self.made.append(self.transport)
+
+
+    def dataReceived(self, data):
+        self.data.append(data)
+
+
+    def connectionLost(self, why):
+        self.lost.append(why)
+
+
+
+class RecordingClientFactory(ClientFactory):
+    """
+    L{ClientFactory} subclass that records the things that happen to it.
+    """
+
+    def __init__(self):
+        """
+        Create some records of things that are about to happen.
+        """
+        self.starts = []
+        self.built = []
+        self.fails = []
+        self.lost = []
+
+    def startedConnecting(self, ctr):
+        self.starts.append(ctr)
+
+    def clientConnectionFailed(self, ctr, reason):
+        self.fails.append(names(connector=ctr, reason=reason))
+
+    def clientConnectionLost(self, ctr, reason):
+        self.lost.append(names(connector=ctr, reason=reason))
+
+    def buildProtocol(self, addr):
+        b =  RecordingProtocol()
+        self.built.append(names(protocol=b, addr=addr))
+        return b
+
+
+class RecordingEndpoint(object):
+
+    def __init__(self):
+        self.attempts = []
+
+
+    def connect(self, factory):
+        d = Deferred()
+        self.attempts.append(names(deferred=d, factory=factory))
+        return d
+
+
+class AdaptEndpointTests(TestCase):
+    """
+    Tests for L{connect} and the objects that it coordinates.
+    """
+
+    def test_connectStartsConnection(self):
+        """
+        When used with a successful endpoint, L{connect} will simulate all
+        aspects of the connection process; C{buildProtocol}, C{connectionMade},
+        C{dataReceived}.
+        """
+        rcf = RecordingClientFactory()
+        e = RecordingEndpoint()
+        ctr = connect(e, rcf)
+        self.assertIdentical(ctr.getDestination(), e)
+        verifyObject(IConnector, ctr)
+        self.assertEqual(len(e.attempts), 1)
+        self.assertEqual(len(rcf.built), 0)
+        proto = e.attempts[0].factory.buildProtocol(object)
+        self.assertEqual(len(rcf.built), 1)
+        made = rcf.built[0].protocol.made
+        transport = object()
+        self.assertEqual(len(made), 0)
+        proto.makeConnection(transport)
+        self.assertEqual(len(made), 1)
+        self.assertIdentical(made[0], transport)
+
+
+    def test_connectionLost(self):
+        """
+        When the connection is lost, both the protocol and the factory will be
+        notified via connectionLost and clientConnectionLost.
+        """
+        rcf = RecordingClientFactory()
+        e = RecordingEndpoint()
+        connect(e, rcf)
+        why = Failure(RuntimeError())
+        proto = e.attempts[0].factory.buildProtocol(object)
+        proto.makeConnection(object())
+        proto.connectionLost(why)
+        self.assertEquals(len(rcf.built), 1)
+        self.assertEquals(rcf.built[0].protocol.lost, [why])
+        self.assertEquals(len(rcf.lost), 1)
+        self.assertIdentical(rcf.lost[0].reason, why)
+
+
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120413/44ba6fee/attachment-0001.html>


More information about the calendarserver-changes mailing list