[Twisted-Python] Telnet MCCP Transport

exarkun at twistedmatrix.com exarkun at twistedmatrix.com
Sun Sep 5 09:49:08 EDT 2010


On 4 Sep, 01:19 pm, simonvermeersch at gmail.com wrote:
>Hello everyone,
>
>I couldn't find an implementation of MCCP (Mud Client Compression
>Protocol) using Twisted so I made my own, which I'm sharing if anyone
>else has a use for it.
>
>You can find a description of MCCP here:
>http://www.mudstandards.org/MCCP_Specification
>
>Since I'm fairly new to Twisted I would appreciate any input about how
>to improve this if you find a better/cleaner way to do it.
>
>import zlib
>from twisted.conch.telnet import TelnetTransport
>from twisted.conch.telnet import DO, DONT
>
>COMPRESS2 = chr(86)
>
>class MCCPTelnetTransport(TelnetTransport):
>    def connectionMade(self):
>        self.zlib = None
>        TelnetTransport.connectionMade(self)
>        self.will(COMPRESS2)
>
>    def commandReceived(self, command, argument):
>        if argument == COMPRESS2:
>            if command == DO:
>                self.requestNegotiation(COMPRESS2, "")
>                self.zlib = zlib.compressobj()
>            elif command == DONT:
>                pass
>        else:
>            TelnetTransport.commandReceived(self, command, argument)
>
>    def write(self, data):
>        data = data.replace('\n', '\r\n')
>        if self.zlib:
>            data = self.zlib.compress(data)
>            data += self.zlib.flush(zlib.Z_SYNC_FLUSH)
>        self.transport.write(data)

This approach has some drawbacks:

  * Duplication of the logic in TelnetTransport.write.  In this case, the 
duplication appears to be imprecise: the original escapes IAC as well as 
does newline translation, but your version only does the latter.

  * If there's some other similar option you decide you want to support, 
you'll probably have to implement it by adding the feature directly to 
this class.  Since MCCPTelnetTransport is a TelnetTransport subclass, 
you won't be able to compose it with another similar class in any way 
(either through inheritance or composition).  This has slightly 
unfortunate code re-use consequences.

  * If you ever put a transport.write() call into connectionMade, I think 
the result will be a corrupted transport (if the server supports MCCP). 
This is because the bytes will be sent uncompressed because the server 
will not have yet had a chance to send the DO COMPRESS2 response, so the 
zlib object will not yet exist.

Only one idea really comes to mind for an implementation without (at 
least some of) these drawbacks, and it may not really be worth the 
trouble.  The idea is that you would start off with an extra compression 
protocol in the stack.  It would act as a transport to TelnetTransport 
and as a protocol to whatever lower level transport (eg tcp) you're 
using.  It would have methods for enabling and disabling compression 
which you could call from your option negotiation methods.

  * Since it would have a write method which sometimes compressed bytes 
before passing them on, you wouldn't have to override 
TelnetTransport.write, so you wouldn't have the duplication of code 
there.

  * Since most of the code would be on a separate class, it *might* be 
easier to re-use.  Although since it creates a new interface that your 
TelnetTransport subclass wants to use, this might not be true (you might 
always need to use the two classes together, and they might break if you 
inserted extra classes between them).

  * It could buffer writes while negotiations were pending.  Although you 
could also just implement this yourself on MCCPTelnetTransport.

So, maybe a wash.  It might just be better to fix the problems with your 
current approach and move on.

Jean-Paul



More information about the Twisted-Python mailing list