Added MCCP (compression of data stream between server-client). Not fully functioning yet, tintin++ tends to complain of compression errors after a while (the server degrades back to uncompressed mode gracefully though). MCCP is thus deactivated in the server at the moment.
This commit is contained in:
parent
2104fd391b
commit
a4f8019c4a
3 changed files with 105 additions and 10 deletions
64
src/server/mccp.py
Normal file
64
src/server/mccp.py
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
"""
|
||||||
|
|
||||||
|
MCCP - Mud Client Compression Protocol
|
||||||
|
|
||||||
|
The implements the MCCP v2 telnet protocol as per
|
||||||
|
http://tintin.sourceforge.net/mccp/. MCCP allows for the server to
|
||||||
|
compress data when sending to supporting clients, reducing bandwidth
|
||||||
|
by 70-90%.. The compression is done using Python's builtin zlib
|
||||||
|
library. If the client doesn't support MCCP, server sends uncompressed
|
||||||
|
instead. Note: On modern hardware you are not likely to notice the
|
||||||
|
effect of MCCP unless you have extremely heavy traffic or sits on a
|
||||||
|
terribly slow connection.
|
||||||
|
|
||||||
|
"""
|
||||||
|
import zlib
|
||||||
|
|
||||||
|
# negotiations for v1 and v2 of the protocol
|
||||||
|
MCCP = chr(86)
|
||||||
|
FLUSH = zlib.Z_SYNC_FLUSH
|
||||||
|
|
||||||
|
def mccp_compress(protocol, data):
|
||||||
|
"Handles zlib compression, if applicable"
|
||||||
|
if hasattr(protocol, 'zlib'):
|
||||||
|
data = protocol.zlib.compress(data)
|
||||||
|
data += protocol.zlib.flush(FLUSH)
|
||||||
|
return data
|
||||||
|
|
||||||
|
class Mccp(object):
|
||||||
|
"""
|
||||||
|
Implements the MCCP protocol. Add this to a
|
||||||
|
variable on the telnet protocol to set it up.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, protocol):
|
||||||
|
"""
|
||||||
|
initialize MCCP by storing protocol on
|
||||||
|
ourselves and calling the client to see if
|
||||||
|
it supports MCCP. Sets callbacks to
|
||||||
|
start zlib compression in that case.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.protocol = protocol
|
||||||
|
self.protocol.protocol_flags['MCCP'] = False
|
||||||
|
# ask if client will mccp, connect callbacks to handle answer
|
||||||
|
self.protocol.will(MCCP).addCallbacks(self.do_mccp, self.no_mccp)
|
||||||
|
|
||||||
|
def no_mccp(self, option):
|
||||||
|
"""
|
||||||
|
If client doesn't support mccp, don't do anything.
|
||||||
|
"""
|
||||||
|
print "deactivating mccp ..."
|
||||||
|
if hasattr(self.protocol, 'zlib'):
|
||||||
|
del self.protocol.zlib
|
||||||
|
self.protocol.protocol_flags['MCCP'] = False
|
||||||
|
|
||||||
|
def do_mccp(self, option):
|
||||||
|
"""
|
||||||
|
The client supports MCCP. Set things up by
|
||||||
|
creating a zlib compression stream.
|
||||||
|
"""
|
||||||
|
print "activating mccp ..."
|
||||||
|
self.protocol.protocol_flags['MCCP'] = True
|
||||||
|
self.protocol.requestNegotiation(MCCP, '')
|
||||||
|
self.protocol.zlib = zlib.compressobj(9)
|
||||||
|
|
@ -7,9 +7,9 @@ sessions etc.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from twisted.conch.telnet import Telnet, StatefulTelnetProtocol, IAC, LINEMODE
|
from twisted.conch.telnet import Telnet, StatefulTelnetProtocol, IAC, LINEMODE, DO, DONT
|
||||||
from src.server.session import Session
|
from src.server.session import Session
|
||||||
from src.server import ttype
|
from src.server import ttype, mccp
|
||||||
from src.utils import utils, ansi
|
from src.utils import utils, ansi
|
||||||
|
|
||||||
class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
|
|
@ -27,8 +27,11 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
client_address = self.transport.client
|
client_address = self.transport.client
|
||||||
self.init_session("telnet", client_address, self.factory.sessionhandler)
|
self.init_session("telnet", client_address, self.factory.sessionhandler)
|
||||||
|
|
||||||
# setup ttype
|
# setup ttype (client info)
|
||||||
self.ttype = ttype.Ttype(self)
|
#self.ttype = ttype.Ttype(self)
|
||||||
|
|
||||||
|
# setup mccp (data compression)
|
||||||
|
# self.mccp = mccp.Mccp(self) #TODO: mccp doesn't work quite right yet.
|
||||||
|
|
||||||
# add us to sessionhandler
|
# add us to sessionhandler
|
||||||
self.sessionhandler.connect(self)
|
self.sessionhandler.connect(self)
|
||||||
|
|
@ -38,7 +41,24 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
This sets up the options we allow for this protocol.
|
This sets up the options we allow for this protocol.
|
||||||
"""
|
"""
|
||||||
return (option == LINEMODE or
|
return (option == LINEMODE or
|
||||||
option == ttype.TTYPE)
|
option == ttype.TTYPE or
|
||||||
|
option == mccp.MCCP)
|
||||||
|
|
||||||
|
def enableLocal(self, option):
|
||||||
|
"""
|
||||||
|
Allow certain options on this protocol
|
||||||
|
"""
|
||||||
|
if option == mccp.MCCP:
|
||||||
|
#self.mccp.do_mccp(option)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def disableLocal(self, option):
|
||||||
|
if option == mccp.MCCP:
|
||||||
|
self.mccp.no_mccp(option)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return super(TelnetProtocol, self).disableLocal(option)
|
||||||
|
|
||||||
|
|
||||||
def connectionLost(self, reason):
|
def connectionLost(self, reason):
|
||||||
"""
|
"""
|
||||||
|
|
@ -58,8 +78,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
# print "dataRcv:", data,
|
# print "dataRcv:", data,
|
||||||
# try:
|
# try:
|
||||||
# for b in data:
|
# for b in data:
|
||||||
# print ord(b),
|
# print ord(b),
|
||||||
# if b == chr(24): print "ttype found!"
|
|
||||||
# print ""
|
# print ""
|
||||||
# except Exception, e:
|
# except Exception, e:
|
||||||
# print str(e) + ":", str(data)
|
# print str(e) + ":", str(data)
|
||||||
|
|
@ -68,6 +87,16 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
super(TelnetProtocol, self).dataReceived(data)
|
super(TelnetProtocol, self).dataReceived(data)
|
||||||
else:
|
else:
|
||||||
StatefulTelnetProtocol.dataReceived(self, data)
|
StatefulTelnetProtocol.dataReceived(self, data)
|
||||||
|
|
||||||
|
def _write(self, byt):
|
||||||
|
"hook overloading the one used in plain telnet"
|
||||||
|
#print "_write (%s): %s" % (self.state, " ".join(str(ord(c)) for c in byt))
|
||||||
|
super(TelnetProtocol, self)._write(mccp.mccp_compress(self, byt))
|
||||||
|
|
||||||
|
def sendLine(self, line):
|
||||||
|
"hook overloading the one used linereceiver"
|
||||||
|
#print "sendLine (%s):\n%s" % (self.state, line)
|
||||||
|
super(TelnetProtocol, self).sendLine(mccp.mccp_compress(self, line))
|
||||||
|
|
||||||
def lineReceived(self, string):
|
def lineReceived(self, string):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
"""
|
"""
|
||||||
|
TTYPE (MTTS) - Mud Terminal Type Standard
|
||||||
|
|
||||||
This module implements the TTYPE telnet protocol as per
|
This module implements the TTYPE telnet protocol as per
|
||||||
http://tintin.sourceforge.net/mtts/. It allows the server to ask the
|
http://tintin.sourceforge.net/mtts/. It allows the server to ask the
|
||||||
client about its capabilities. If the client also supports TTYPE, it
|
client about its capabilities. If the client also supports TTYPE, it
|
||||||
|
|
@ -42,9 +44,9 @@ class Ttype(object):
|
||||||
self.protocol.protocol_flags['TTYPE'] = {}
|
self.protocol.protocol_flags['TTYPE'] = {}
|
||||||
|
|
||||||
# setup protocol to handle ttype initialization and negotiation
|
# setup protocol to handle ttype initialization and negotiation
|
||||||
self.protocol.negotiationMap[TTYPE] = self.negotiate_ttype
|
self.protocol.negotiationMap[TTYPE] = self.do_ttype
|
||||||
# ask if client will ttype, connect callback if it does.
|
# ask if client will ttype, connect callback if it does.
|
||||||
self.protocol.will(TTYPE).addCallbacks(self.negotiate_ttype, self.no_ttype)
|
self.protocol.will(TTYPE).addCallbacks(self.do_ttype, self.no_ttype)
|
||||||
|
|
||||||
def no_ttype(self, option):
|
def no_ttype(self, option):
|
||||||
"""
|
"""
|
||||||
|
|
@ -52,7 +54,7 @@ class Ttype(object):
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def negotiate_ttype(self, option):
|
def do_ttype(self, option):
|
||||||
"""
|
"""
|
||||||
Handles negotiation of the ttype protocol once the
|
Handles negotiation of the ttype protocol once the
|
||||||
client has confirmed that it supports the ttype
|
client has confirmed that it supports the ttype
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue