Ticket #2790: udp-wouldblock-2790-2.patch

File udp-wouldblock-2790-2.patch, 5.8 KB (added by ghazel, 8 years ago)

patch with fix and unit tests

  • twisted/test/test_udp_internals.py

     
     1# Copyright (c) 2006-2010 Twisted Matrix Laboratories.
     2# See LICENSE for details.
     3
     4"""
     5Whitebox tests for UDP APIs.
     6"""
     7
     8import socket, os
     9
     10from twisted.trial.unittest import TestCase
     11
     12from twisted.internet.udp import EWOULDBLOCK, EAGAIN, Port
     13from twisted.internet.protocol import DatagramProtocol
     14from twisted.python.runtime import platform
     15from twisted.internet import reactor, interfaces
     16
     17
     18class PlatformAssumptionsTestCase(TestCase):
     19    """
     20    Test assumptions about platform behaviors.
     21    """
     22    sendToLimit = 2048
     23
     24    def setUp(self):
     25        self.openSockets = []
     26
     27
     28    def tearDown(self):
     29        while self.openSockets:
     30            self.openSockets.pop().close()
     31
     32
     33    def socket(self):
     34        """
     35        Create and return a new socket object, also tracking it so it can be
     36        closed in the test tear down.
     37        """
     38        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
     39        self.openSockets.append(s)
     40        return s
     41
     42
     43    def test_sendToWouldBlock(self):
     44        """
     45        Test that the platform sendto(2) call fails with either L{EWOULDBLOCK}
     46        or L{EAGAIN} when the buffer is full.
     47        """
     48        # Make a server to which to connect
     49        port = self.socket()
     50        port.bind(('127.0.0.1', 0))
     51        serverPortNumber = port.getsockname()[1]
     52
     53        # Make a client to use to sendto to the server
     54        client = self.socket()
     55        client.setblocking(False)
     56        data = '*' * 1024
     57        addr = ('127.0.0.1', serverPortNumber)
     58
     59        # Use up the buffer.
     60        for x in xrange(self.sendToLimit):
     61            try:
     62                client.sendto(data, addr)
     63            except socket.error, e:
     64                if e.args[0] in (EWOULDBLOCK, EAGAIN):
     65                    # The desired state has been achieved.
     66                    break
     67                else:
     68                    # Some unexpected error occurred.
     69                    raise
     70        else:
     71            self.fail("Could provoke neither EWOULDBLOCK nor EAGAIN from platform.")
     72    if platform.getType() != "win32":
     73        test_sendToWouldBlock.skip = (
     74            "Only Windows seems to be able to reliably provoke this behavior "
     75            "in the naive manner.")
     76
     77
     78
     79class SelectReactorTestCase(TestCase):
     80    """
     81    Tests for select-specific failure conditions.
     82    """
     83
     84    def _sendToFailureTest(self, socketErrorNumber):
     85        """
     86        Test behavior in the face of an exception from C{sendto(2)}.
     87
     88        On any exception which indicates the platform is unable or unwilling
     89        to allocate further resources to us, the existing port should remain
     90        listening, and the exception should not propagate outward from write.
     91
     92        @param socketErrorNumber: The errno to simulate from sendto.
     93        """
     94        class FakeSocket(object):
     95            """
     96            Pretend to be a socket in an overloaded system.
     97            """
     98            def sendto(self, data, addr):
     99                raise socket.error(
     100                    socketErrorNumber, os.strerror(socketErrorNumber))
     101
     102        protocol = DatagramProtocol()
     103        port = Port(0, protocol, interface='127.0.0.1')
     104        port._bindSocket()
     105        serverPortNumber = port.getHost().port
     106        originalSocket = port.socket
     107        try:
     108            port.socket = FakeSocket()
     109
     110            port.write('*', ('127.0.0.1', serverPortNumber))
     111        finally:
     112            port.socket = originalSocket
     113
     114
     115    def test_wouldBlockFromSendTo(self):
     116        """
     117        C{sendto(2)} can fail with C{EWOULDBLOCK} when there the send buffer is
     118        full. Test that this doesn't negatively impact any other existing
     119        connections.
     120
     121        C{EWOULDBLOCK} mainly occurs on Windows, but occurs on other platforms
     122        when the speed of the interfaces is different.
     123        """
     124        return self._sendToFailureTest(EWOULDBLOCK)
     125
     126
     127    def test_againFromSendTo(self):
     128        """
     129        Similar to L{test_eWouldBlockFromSendTo}, but test the case where
     130        C{sendto(2)} fails with C{EAGAIN}.
     131
     132        C{EAGAIN} and C{EWOULDBLOCK} are equal on Windows, Linux, OS X and
     133        FreeBSD, but may differ elsewhere.
     134        """
     135        return self._sendToFailureTest(EAGAIN)
     136
     137
     138if not interfaces.IReactorFDSet.providedBy(reactor):
     139    skipMsg = 'This test only applies to reactors that implement IReactorFDset'
     140    PlatformAssumptionsTestCase.skip = skipMsg
     141    SelectReactorTestCase.skip = skipMsg
     142
  • twisted/internet/udp.py

     
    155155                    raise error.MessageLengthError, "message too long"
    156156                elif no == ECONNREFUSED:
    157157                    self.protocol.connectionRefused()
     158                elif no in (EWOULDBLOCK, EAGAIN):
     159                    # in a perfect world we would buffer this data and notify
     160                    # the producer to stop producing.
     161                    return
    158162                else:
    159163                    raise
    160164        else:
     
    175179                    # think and the info is not necessarily useful. Nevertheless
    176180                    # maybe we should call connectionRefused? XXX
    177181                    return
     182                elif no in (EWOULDBLOCK, EAGAIN):
     183                    # in a perfect world we would buffer this data and notify
     184                    # the producer to stop producing.
     185                    return
    178186                else:
    179187                    raise
    180188