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

File udp-wouldblock-2790-1.patch, 5.4 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 errno, 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 test_sendToWouldBlock(self):
     25        """
     26        Test that the platform sendto(2) call fails with either L{EWOULDBLOCK}
     27        or L{EAGAIN} when the buffer is full.
     28        """
     29        # Make a server to which to connect
     30        port = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
     31        port.bind(('127.0.0.1', 0))
     32        serverPortNumber = port.getsockname()[1]
     33
     34        # Make a client to use to sendto to the server
     35        client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
     36        client.setblocking(False)
     37        data = '*' * 1024
     38        addr = ('127.0.0.1', serverPortNumber)
     39
     40        # Use up the buffer.
     41        for x in xrange(self.sendToLimit):
     42            try:
     43                client.sendto(data, addr)
     44            except socket.error, e:
     45                if e.args[0] in (EWOULDBLOCK, EAGAIN):
     46                    # The desired state has been achieved.
     47                    break
     48                else:
     49                    # Some unexpected error occurred.
     50                    raise
     51        else:
     52            self.fail("Could provoke neither EMFILE nor ENOBUFS from platform.")
     53    if platform.getType() != "win32":
     54        test_sendToWouldBlock.skip = (
     55            "Only Windows seems to be able to reliably provoke this behavior"
     56            "in the naive manner.")
     57
     58
     59
     60class SelectReactorTestCase(TestCase):
     61    """
     62    Tests for select-specific failure conditions.
     63    """
     64
     65    def _sendToFailureTest(self, socketErrorNumber):
     66        """
     67        Test behavior in the face of an exception from C{sendto(2)}.
     68
     69        On any exception which indicates the platform is unable or unwilling
     70        to allocate further resources to us, the existing port should remain
     71        listening, and the exception should not propagate outward from write.
     72
     73        @param socketErrorNumber: The errno to simulate from sendto.
     74        """
     75        class FakeSocket(object):
     76            """
     77            Pretend to be a socket in an overloaded system.
     78            """
     79            def sendto(self, data, addr):
     80                raise socket.error(
     81                    socketErrorNumber, os.strerror(socketErrorNumber))
     82
     83        protocol = DatagramProtocol()
     84        port = Port(0, protocol, interface='127.0.0.1')
     85        port._bindSocket()
     86        serverPortNumber = port.getHost().port
     87        originalSocket = port.socket
     88        try:
     89            port.socket = FakeSocket()
     90
     91            port.write('*', ('127.0.0.1', serverPortNumber))
     92        finally:
     93            port.socket = originalSocket
     94
     95
     96    def test_wouldBlockFromSendTo(self):
     97        """
     98        C{sendto(2)} can fail with C{EWOULDBLOCK} when there the send buffer is
     99        full. Test that this doesn't negatively impact any other existing
     100        connections.
     101
     102        C{EWOULDBLOCK} mainly occurs on Windows, but occurs on other platforms
     103        when the speed of the interfaces is different.
     104        """
     105        return self._sendToFailureTest(EWOULDBLOCK)
     106
     107
     108    def test_againFromSendTo(self):
     109        """
     110        Similar to L{test_eWouldBlockFromSendTo}, but test the case where
     111        C{sendto(2)} fails with C{EAGAIN}.
     112
     113        C{EAGAIN} and C{EWOULDBLOCK} are equal on Windows, Linux, OS X and
     114        FreeBSD, but may differ elsewhere.
     115        """
     116        return self._sendToFailureTest(EAGAIN)
     117
     118
     119if not interfaces.IReactorFDSet.providedBy(reactor):
     120    skipMsg = 'This test only applies to reactors that implement IReactorFDset'
     121    PlatformAssumptionsTestCase.skip = skipMsg
     122    SelectReactorTestCase.skip = skipMsg
     123
  • 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