Ticket #3277: lineReceiver-bug-3277.patch

File lineReceiver-bug-3277.patch, 7.6 KB (added by spiv, 12 years ago)
  • twisted/protocols/basic.py

    === modified file 'twisted/protocols/basic.py'
     
    133133
    134134    def dataReceived(self, data):
    135135        """Translates bytes into lines, and calls lineReceived."""
    136         lines  = (self._buffer+data).split(self.delimiter)
     136        dataSoFar = self._buffer + data
     137        lines = dataSoFar.split(self.delimiter)
    137138        self._buffer = lines.pop(-1)
    138139        for line in lines:
    139140            if self.transport.disconnecting:
     
    143144                # the one that told it to close.
    144145                return
    145146            if len(line) > self.MAX_LENGTH:
    146                 return self.lineLengthExceeded(line)
     147                return self.lineLengthExceeded(dataSoFar)
    147148            else:
    148149                self.lineReceived(line)
    149150        if len(self._buffer) > self.MAX_LENGTH:
     
    163164        """Called when the maximum line length has been reached.
    164165        Override if it needs to be dealt with in some special way.
    165166        """
     167        self.transport.loseConnection()
    166168        return error.ConnectionLost('Line length exceeded')
    167169
    168170
     
    232234            else:
    233235                linelength = len(line)
    234236                if linelength > self.MAX_LENGTH:
    235                     exceeded = line + self.__buffer
     237                    exceeded = line + self.delimiter + self.__buffer
    236238                    self.__buffer = ''
    237239                    return self.lineLengthExceeded(exceeded)
    238240                why = self.lineReceived(line)
     
    291293        be more than one line, or may be only the initial portion of the
    292294        line.
    293295        """
    294         return self.transport.loseConnection()
     296        self.transport.loseConnection()
     297        return error.ConnectionLost('Line length exceeded')
    295298
    296299
    297300class StringTooLongError(AssertionError):
  • twisted/test/test_protocols.py

    === modified file 'twisted/test/test_protocols.py'
     
    8585            self.setLineMode(line[self.MAX_LENGTH + 1:])
    8686
    8787
    88 class LineOnlyTester(basic.LineOnlyReceiver):
    89     """
    90     A buffering line only receiver.
    91     """
    92     delimiter = '\n'
    93     MAX_LENGTH = 64
    94 
    95     def connectionMade(self):
    96         """
    97         Create/clean data received on connection.
    98         """
    99         self.received = []
    100 
    101     def lineReceived(self, line):
    102         """
    103         Save received data.
    104         """
    105         self.received.append(line)
    106 
    10788class WireTestCase(unittest.TestCase):
    10889    """
    10990    Test wire protocols.
     
    297278        self.assertEqual(protocol.rest, '')
    298279
    299280
     281class LineOnlyReceiverTestsMixin:
     282    """
     283    Tests to apply to both LineReceiver and LineOnlyReceiver.
     284    """
    300285
    301 class LineOnlyReceiverTestCase(unittest.TestCase):
    302     """
    303     Test line only receiveer.
    304     """
    305286    buffer = """foo
    306287    bleakness
    307288    desolation
    308289    plastic forks
    309290    """
    310291
     292    def makeLineReceiver(self):
     293        """
     294        Construct a line receiver for testing with.
     295
     296        It will be a simple subclass of whatever self.lineReceiverClass is.
     297        The subclass will log calls to lineReceived and lineLengthExceeded to a
     298        '.calls' attribute.
     299        """
     300        transport = protocol.FileWrapper(proto_helpers.StringIOWithoutClosing())
     301        baseClass = self.klass
     302        class LoggingLineReceiver(baseClass):
     303            """
     304            A line receiver subclass that records calls made to it, but
     305            otherwise behaves like its base class.
     306            """
     307            delimiter = '\n'
     308            MAX_LENGTH = 64
     309
     310            def connectionMade(self):
     311                self.calls = []
     312                return baseClass.connectionMade(self)
     313
     314            def lineReceived(self, line):
     315                self.calls.append(('lineReceived', line))
     316
     317            def lineLengthExceeded(self, line):
     318                self.calls.append(('lineLengthExceeded', line))
     319                return baseClass.lineLengthExceeded(self, line)
     320           
     321        lineReceiver = LoggingLineReceiver()
     322        lineReceiver.makeConnection(transport)
     323        return lineReceiver
     324
    311325    def test_buffer(self):
    312326        """
    313327        Test buffering over line protocol: data received should match buffer.
    314328        """
    315         t = proto_helpers.StringTransport()
    316         a = LineOnlyTester()
    317         a.makeConnection(t)
     329        lineReceiver = self.makeLineReceiver()
    318330        for c in self.buffer:
    319             a.dataReceived(c)
    320         self.assertEquals(a.received, self.buffer.split('\n')[:-1])
     331            lineReceiver.dataReceived(c)
     332        expectedLines = self.buffer.split('\n')[:-1]
     333        expectedCalls = [('lineReceived', line) for line in expectedLines]
     334        self.assertEquals(expectedCalls, lineReceiver.calls)
    321335
    322336    def test_lineTooLong(self):
    323337        """
    324         Test sending a line too long: it should close the connection.
     338        When a line greater than MAX_LENGTH is received, lineLengthExceeded is
     339        called.  The default implementation lineLengthExceeded closes the
     340        connection, and returns a ConnectionLost error.
    325341        """
    326         t = proto_helpers.StringTransport()
    327         a = LineOnlyTester()
    328         a.makeConnection(t)
    329         res = a.dataReceived('x'*200)
     342        lineReceiver = self.makeLineReceiver()
     343        res = lineReceiver.dataReceived('x'*200)
     344        self.failUnlessEqual(
     345            [('lineLengthExceeded', 'x'*200)], lineReceiver.calls)
     346        self.assertTrue(lineReceiver.transport.closed)
    330347        self.assertIsInstance(res, error.ConnectionLost)
     348        self.assertEqual(('Line length exceeded',), res.args)
     349
     350    def test_longLineWithDelimiter(self):
     351        """
     352        When MAX_LENGTH is exceeded *and* a delimiter has been received,
     353        lineLengthExceeded is called with the right bytes.
     354
     355        See http://twistedmatrix.com/trac/ticket/3277
     356        """
     357        # Set up a line receiver with a short MAX_LENGTH that logs
     358        # lineLengthExceeded events.
     359        lineReceiver = self.makeLineReceiver()
     360        lineReceiver.MAX_LENGTH = 10
     361        # Call dataReceived with two lines, the first longer than MAX_LENGTH.
     362        longLine = ('x' * 11) + '\n'
     363        nextLine = 'next line\n'
     364        lineReceiver.dataReceived(longLine + nextLine)
     365        # We expect lineLengthExceeded to be called with exactly what we just
     366        # passed dataReceived.  lineReceived is not called.
     367        expectedCalls = [('lineLengthExceeded', longLine + nextLine)]
     368        self.assertEqual(expectedCalls, lineReceiver.calls)
     369
     370    def test_lineReceiverAsProducer(self):
     371        """
     372        Test produce/unproduce in receiving.
     373        """
     374        lineReceiver = self.makeLineReceiver()
     375        if isinstance(lineReceiver, basic.LineOnlyReceiver):
     376            raise unittest.SkipTest(
     377                'LineOnlyReceiver does not implement IPushProducer')
     378        lineReceiver.transport.registerProducer(lineReceiver, False)
     379        lineReceiver.dataReceived('hello world\n')
     380        lineReceiver.transport.unregisterProducer()
     381        lineReceiver.dataReceived('goodbye\n')
     382        self.assertEquals(
     383            [('lineReceived', 'hello world'), ('lineReceived', 'goodbye')],
     384            lineReceiver.calls)
     385
     386
     387class LineOnlyReceiverTestCase(unittest.TestCase, LineOnlyReceiverTestsMixin):
     388    """
     389    LineOnlyReceiverTestsMixin applied to LineOnlyReceiver.
     390    """
     391
     392    klass = basic.LineOnlyReceiver
     393
     394
     395class LineReceiverLineOnlyTestCase(unittest.TestCase,
     396    LineOnlyReceiverTestsMixin):
     397    """
     398    LineOnlyReceiverTestsMixin applied to LineReceiver.
     399    """
     400
     401    klass = basic.LineReceiver
    331402
    332403
    333404