[CalendarServer-changes] [8482] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Tue Jan 3 18:05:04 PST 2012


Revision: 8482
          http://trac.macosforge.org/projects/calendarserver/changeset/8482
Author:   glyph at apple.com
Date:     2012-01-03 18:05:03 -0800 (Tue, 03 Jan 2012)
Log Message:
-----------
Test (and fix) for the bug where getpeername() raises an exception due to the
connection already being closed.

Modified Paths:
--------------
    CalendarServer/trunk/twext/internet/sendfdport.py
    CalendarServer/trunk/twext/web2/metafd.py

Added Paths:
-----------
    CalendarServer/trunk/twext/web2/test/test_metafd.py

Property Changed:
----------------
    CalendarServer/trunk/

Modified: CalendarServer/trunk/twext/internet/sendfdport.py
===================================================================
--- CalendarServer/trunk/twext/internet/sendfdport.py	2012-01-03 23:24:11 UTC (rev 8481)
+++ CalendarServer/trunk/twext/internet/sendfdport.py	2012-01-04 02:05:03 UTC (rev 8482)
@@ -235,16 +235,16 @@
     def __init__(self, fd, transportFactory, protocolFactory):
         """
         @param fd: a file descriptor
-
         @type fd: C{int}
-        
-        @param transportFactory: a 3-argument function that takes the socket
-            object produced from the file descriptor, the (non-ancillary) data
-            sent along with the incoming file descriptor, and the protocol
-            built along with it, and returns an L{ITransport} provider.  Note
-            that this should NOT call C{makeConnection} on the protocol that it
-            produces, as this class will do that.
 
+        @param transportFactory: a 4-argument function that takes the socket
+            object produced from the file descriptor, the peer address of that
+            socket, the (non-ancillary) data sent along with the incoming file
+            descriptor, and the protocol built along with it, and returns an
+            L{ITransport} provider.  Note that this should NOT call
+            C{makeConnection} on the protocol that it produces, as this class
+            will do that.
+
         @param protocolFactory: an L{IProtocolFactory}
         """
         FileDescriptor.__init__(self)
@@ -283,7 +283,8 @@
                 except SocketError:
                     peeraddr = ('0.0.0.0', 0)
                 protocol = self.protocolFactory.buildProtocol(peeraddr)
-                transport = self.transportFactory(skt, description, protocol)
+                transport = self.transportFactory(skt, peeraddr,
+                                                  description, protocol)
                 protocol.makeConnection(transport)
             except:
                 log.err()
@@ -312,4 +313,4 @@
         """
         self.statusQueue.append(statusMessage)
         self.startWriting()
-        
+

Modified: CalendarServer/trunk/twext/web2/metafd.py
===================================================================
--- CalendarServer/trunk/twext/web2/metafd.py	2012-01-03 23:24:11 UTC (rev 8481)
+++ CalendarServer/trunk/twext/web2/metafd.py	2012-01-04 02:05:03 UTC (rev 8482)
@@ -1,4 +1,4 @@
-
+# -*- test-case-name: twext.web2.test.test_metafd -*-
 ##
 # Copyright (c) 2010 Apple Inc. All rights reserved.
 #
@@ -47,13 +47,11 @@
 
     @ivar fd: the file descriptor of a UNIX socket being used to receive
         connections from a master process calling accept()
-
     @type fd: C{int}
 
     @ivar contextFactory: A context factory for building SSL/TLS connections
         for inbound connections tagged with the string 'SSL' as their
         descriptive data, or None if SSL is not enabled for this server.
-
     @type contextFactory: L{twisted.internet.ssl.ContextFactory} or C{NoneType}
     """
 
@@ -92,13 +90,12 @@
         self.reportingFactory.inheritedPort.stopReading()
 
 
-    def createTransport(self, skt, data, protocol):
+    def createTransport(self, skt, peer, data, protocol):
         """
         Create a TCP transport, from a socket object passed by the parent.
         """
         self._connectionCount += 1
-        transport = Server(skt, protocol,
-                           skt.getpeername(), JustEnoughLikeAPort,
+        transport = Server(skt, protocol, peer, JustEnoughLikeAPort,
                            self._connectionCount, reactor)
         if data == 'SSL':
             transport.startTLS(self.contextFactory)

Added: CalendarServer/trunk/twext/web2/test/test_metafd.py
===================================================================
--- CalendarServer/trunk/twext/web2/test/test_metafd.py	                        (rev 0)
+++ CalendarServer/trunk/twext/web2/test/test_metafd.py	2012-01-04 02:05:03 UTC (rev 8482)
@@ -0,0 +1,141 @@
+##
+# Copyright (c) 2011 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 twext.web2.metafd.
+"""
+
+from socket import error as SocketError
+from errno import ENOTCONN
+
+from twext.web2 import metafd
+from twext.web2.metafd import ReportingHTTPService
+from twext.web2.channel.http import HTTPChannel
+from twext.internet import sendfdport
+from twisted.internet.tcp import Server
+
+
+from twisted.trial.unittest import TestCase
+
+
+class FakeSocket(object):
+    """
+    A fake socket for testing.
+    """
+    def __init__(self, test):
+        self.test = test
+
+
+    def fileno(self):
+        return "not a socket"
+
+
+    def setblocking(self, blocking):
+        return
+
+
+    def getpeername(self):
+        if self.test.peerNameSucceed:
+            return ("4.3.2.1", 4321)
+        else:
+            raise SocketError(ENOTCONN, "Transport endpoint not connected")
+
+
+
+class InheritedPortForTesting(sendfdport.InheritedPort):
+    """
+    L{sendfdport.InheritedPort} subclass that prevents certain I/O operations
+    for better unit testing.
+    """
+
+    def startReading(self):
+        "Do nothing."
+
+
+    def stopReading(self):
+        "Do nothing."
+
+
+    def startWriting(self):
+        "Do nothing."
+
+
+    def stopWriting(self):
+        "Do nothing."
+
+
+
+class ServerTransportForTesting(Server):
+    """
+    tcp.Server replacement for testing purposes.
+    """
+
+    def startReading(self):
+        "Do nothing."
+
+    def stopReading(self):
+        "Do nothing."
+
+    def startWriting(self):
+        "Do nothing."
+
+    def stopWriting(self):
+        "Do nothing."
+
+    def __init__(self, *a, **kw):
+        super(ServerTransportForTesting, self).__init__(*a, **kw)
+        self.reactor = None
+
+
+
+class ReportingHTTPServiceTests(TestCase):
+    """
+    Tests for L{ReportingHTTPService}
+    """
+
+    peerNameSucceed = True
+
+    def setUp(self):
+        def fakefromfd(fd, addressFamily, socketType):
+            return FakeSocket(self)
+        def fakerecvfd(fd):
+            return "not an fd", "not a description"
+        def fakeclose(fd):
+            ""
+        self.patch(sendfdport, 'recvfd', fakerecvfd)
+        self.patch(sendfdport, 'fromfd', fakefromfd)
+        self.patch(sendfdport, 'close', fakeclose)
+        self.patch(metafd, 'InheritedPort', InheritedPortForTesting)
+        self.patch(metafd, 'Server', ServerTransportForTesting)
+        # This last stubbed out just to prevent dirty reactor warnings.
+        self.patch(HTTPChannel, "callLater", lambda *a, **k: None)
+        self.svc = ReportingHTTPService(None, None, None)
+        self.svc.startService()
+
+
+    def test_quickClosedSocket(self):
+        """
+        If a socket is closed very quickly after being {accept()}ed, requesting
+        its peer (or even host) address may fail with C{ENOTCONN}.  If this
+        happens, its transport should be supplied with a dummy peer address.
+        """
+        self.peerNameSucceed = False
+        self.svc.reportingFactory.inheritedPort.doRead()
+        channels = self.svc.reportingFactory.connectedChannels
+        self.assertEqual(len(channels), 1)
+        self.assertEqual(list(channels)[0].transport.getPeer().host, "0.0.0.0")
+
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120103/edd4e3fb/attachment-0001.html>


More information about the calendarserver-changes mailing list