Skip to content

Commit

Permalink
Merge banana-receive-wrong-dialect-7662
Browse files Browse the repository at this point in the history
Author: wolfgang61, exarkun
Reviewer: exarkun
Fixes: twisted#7662

Check the active Banana dialect before processing dialect-specific features in
the Banana protocol implementation.  Previously features of the PB dialect
could be used even when the None dialect was active.


git-svn-id: svn://svn.twistedmatrix.com/svn/Twisted/trunk@43224 bbbe8e31-12d6-0310-92fd-ac37d47ddeeb
  • Loading branch information
exarkun committed Oct 5, 2014
1 parent fbad806 commit 730e076
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 10 deletions.
4 changes: 1 addition & 3 deletions docs/core/specifications/banana.rst
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,7 @@ A profile is specified by a unique string. This specification defines two profil
profile that should be supported by all Banana implementations.
Additional profiles may be added in the future.




Extensions defined by a profile may only be used if that profile has been selected by client and server.


The ``"none"`` Profile
Expand Down
19 changes: 18 additions & 1 deletion twisted/spread/banana.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,17 @@ def setPrefixLimit(limit):
SIZE_LIMIT = 640 * 1024 # 640k is all you'll ever need :-)

class Banana(protocol.Protocol, styles.Ephemeral):
"""
L{Banana} implements the I{Banana} s-expression protocol, client and
server.
@ivar knownDialects: These are the profiles supported by this Banana
implementation.
@type knownDialects: L{list} of L{bytes}
"""

# The specification calls these profiles but this implementation calls them
# dialects instead.
knownDialects = ["pb", "none"]

prefixLimit = None
Expand Down Expand Up @@ -212,7 +223,13 @@ def dataReceived(self, chunk):
elif typebyte == VOCAB:
buffer = rest
num = b1282int(num)
gotItem(self.incomingVocabulary[num])
item = self.incomingVocabulary[num]
if self.currentDialect == b'pb':
# the sender issues VOCAB only for dialect pb
gotItem(item)
else:
raise NotImplementedError(
"Invalid item for pb protocol {0!r}".format(item))
elif typebyte == FLOAT:
if len(rest) >= 8:
buffer = rest[8:]
Expand Down
96 changes: 90 additions & 6 deletions twisted/test/test_banana.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,60 @@ def testInt2b128(self):
y = banana.b1282int(v)
assert y == i, "y = %s; i = %s" % (y,i)

class BananaTestCase(unittest.TestCase):


def selectDialect(protocol, dialect):
"""
Dictate a Banana dialect to use.
@param protocol: A L{banana.Banana} instance which has not yet had a
dialect negotiated.
@param dialect: A L{bytes} instance naming a Banana dialect to select.
"""
# We can't do this the normal way by delivering bytes because other setup
# stuff gets in the way (for example, clients and servers have incompatible
# negotiations for this step). So use the private API to make this happen.
protocol._selectDialect(dialect)



class BananaTestBase(unittest.TestCase):
"""
The base for test classes. It defines commonly used things and sets up a
connection for testing.
"""
encClass = banana.Banana

def setUp(self):
self.io = StringIO.StringIO()
self.enc = self.encClass()
self.enc.makeConnection(protocol.FileWrapper(self.io))
self.enc._selectDialect("none")
selectDialect(self.enc, b"none")
self.enc.expressionReceived = self.putResult


def putResult(self, result):
"""
Store an expression received by C{self.enc}.
@param result: The object that was received.
@type result: Any type supported by Banana.
"""
self.result = result


def tearDown(self):
self.enc.connectionLost(failure.Failure(main.CONNECTION_DONE))
del self.enc



class BananaTestCase(BananaTestBase):
"""
General banana tests.
"""

def testString(self):
self.enc.sendEncoded("hello")
l = []
Expand Down Expand Up @@ -306,18 +342,66 @@ def encoded(n):



class DialectTests(BananaTestBase):
"""
Tests for Banana's handling of dialects.
"""
vocab = b'remote'
legalPbItem = chr(banana.Banana.outgoingVocabulary[vocab]) + banana.VOCAB
illegalPbItem = chr(122) + banana.VOCAB

def test_dialectNotSet(self):
"""
If no dialect has been selected and a PB VOCAB item is received,
L{NotImplementedError} is raised.
"""
self.assertRaises(
NotImplementedError,
self.enc.dataReceived, self.legalPbItem)


def test_receivePb(self):
"""
If the PB dialect has been selected, a PB VOCAB item is accepted.
"""
selectDialect(self.enc, b'pb')
self.enc.dataReceived(self.legalPbItem)
self.assertEqual(self.result, self.vocab)


def test_receiveIllegalPb(self):
"""
If the PB dialect has been selected and an unrecognized PB VOCAB item
is received, L{banana.Banana.dataReceived} raises L{KeyError}.
"""
selectDialect(self.enc, b'pb')
self.assertRaises(KeyError, self.enc.dataReceived, self.illegalPbItem)


def test_sendPb(self):
"""
if pb dialect is selected, the sender must be able to send things in
that dialect.
"""
selectDialect(self.enc, b'pb')
self.enc.sendEncoded(self.vocab)
self.assertEqual(self.legalPbItem, self.io.getvalue())



class GlobalCoderTests(unittest.TestCase):
"""
Tests for the free functions L{banana.encode} and L{banana.decode}.
"""
def test_statelessDecode(self):
"""
Test that state doesn't carry over between calls to L{banana.decode}.
Calls to L{banana.decode} are independent of each other.
"""
# Banana encoding of 2 ** 449
undecodable = '\x7f' * 65 + '\x85'
undecodable = b'\x7f' * 65 + b'\x85'
self.assertRaises(banana.BananaError, banana.decode, undecodable)

# Banana encoding of 1
decodable = '\x01\x81'
# Banana encoding of 1. This should be decodable even though the
# previous call passed un-decodable data and triggered an exception.
decodable = b'\x01\x81'
self.assertEqual(banana.decode(decodable), 1)
1 change: 1 addition & 0 deletions twisted/topfiles/7662.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
twisted.spread.banana.Banana now raises NotImplementedError when receiving pb messages without pb being the selected dialect

0 comments on commit 730e076

Please sign in to comment.