[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