[Twisted-Python] Telnet MCCP Transport

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) -- Simon Vermeersch

On 4 Sep, 01:19 pm, simonvermeersch@gmail.com wrote:
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

On 4 Sep, 01:19 pm, simonvermeersch@gmail.com wrote:
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
participants (2)
-
exarkun@twistedmatrix.com
-
Vermeersch Simon