diff --git a/src/xpra/client/ui_client_base.py b/src/xpra/client/ui_client_base.py index 54e4ba6d59..5b2f43926f 100644 --- a/src/xpra/client/ui_client_base.py +++ b/src/xpra/client/ui_client_base.py @@ -919,14 +919,10 @@ def make_hello(self): "encodings.core" : self.get_core_encodings(), }) control_commands = ["show_session_info", "enable_bencode", "enable_zlib"] - for k,b in {"lz4" : compression.use_lz4, - "bz2" : compression.use_bz2, - "zlib" : compression.use_zlib, - "bencode" : packet_encoding.use_bencode, - "rencode" : packet_encoding.use_rencode, - "yaml" : packet_encoding.use_yaml}.items(): - if b: - control_commands.append("enable_"+k) + for x in compression.get_enabled_compressors(): + control_commands.append("enable_"+x) + for x in packet_encoding.get_enabled_encoders(): + control_commands.append("enable_"+x) capabilities["control_commands"] = control_commands for k,v in codec_versions.items(): capabilities["encoding.%s.version" % k] = v @@ -1357,21 +1353,14 @@ def _process_control(self, packet): args = packet[2:] log("calling show_session_info%s on server request", args) self.show_session_info(*args) - elif command=="enable_zlib": - log.info("switching to zlib on server request") - self._protocol.enable_zlib() - elif command=="enable_lz4": - log.info("switching to lz4 on server request") - self._protocol.enable_lz4() - elif command=="enable_bencode": - log.info("switching to bencode on server request") + elif command in ("enable_%x" for x in compression.get_enabled_compressors()): + compressor = command.split("_")[1] + log.info("switching to %s on server request", compressor) + self._protocol.enable_compressor(compressor) + elif command in ("enable_%x" for x in packet_encoding.get_enabled_encoders()): + packet_encoding = command.split("_")[1] + log.info("switching to %s on server request", packet_encoding) self._protocol.enable_bencode() - elif command=="enable_rencode": - log.info("switching to rencode on server request") - self._protocol.enable_rencode() - elif command=="enable_yaml": - log.info("switching to yaml on server request") - self._protocol.enable_yaml() elif command=="name": assert len(args)>=3 self.session_name = args[2] diff --git a/src/xpra/net/compression.py b/src/xpra/net/compression.py index 627a047f2f..0a528925f7 100644 --- a/src/xpra/net/compression.py +++ b/src/xpra/net/compression.py @@ -58,6 +58,15 @@ def nocompress(packet, level): use_bz2 = True use_lz4 = has_lz4 +#all the compressors we know about, in their default preference order: +ALL_COMPRESSORS = ["zlib", "lz4", "bz2"] + +_COMPRESSORS = { + "zlib" : zcompress, + "lz4" : lz4_compress, + "bz2" : bzcompress, + "none" : nocompress, + } def get_compression_caps(): return { @@ -67,6 +76,26 @@ def get_compression_caps(): "zlib.version" : zlib.__version__, } +def get_enabled_compressors(): + enabled = [x for x,b in { + "lz4" : use_lz4, + "bz2" : use_bz2, + "zlib" : use_zlib, + }.items() if b] + #order them: + return [x for x in ALL_COMPRESSORS if x in enabled] + +def get_compressor(c): + assert c=="none" or c in ALL_COMPRESSORS + return _COMPRESSORS[c] + +def get_compressor_name(c): + assert c in _COMPRESSORS.values(), "invalid compressor: %s" % c + for k,v in _COMPRESSORS.items(): + if v==c: + return k + raise Exception("impossible bug!") + class Compressed(object): def __init__(self, datatype, data): diff --git a/src/xpra/net/packet_encoding.py b/src/xpra/net/packet_encoding.py index 804860d3a3..901e3f3a0e 100644 --- a/src/xpra/net/packet_encoding.py +++ b/src/xpra/net/packet_encoding.py @@ -56,6 +56,16 @@ log("packet encoding: has_yaml=%s, use_yaml=%s, version=%s", has_yaml, use_yaml, yaml_version) +def do_bencode(data): + return bencode(data), 0 + +def do_rencode(data): + return rencode_dumps(data), FLAGS_RENCODE + +def do_yaml(data): + return yaml_encode(data), FLAGS_YAML + + def get_packet_encoding_caps(): caps = { "rencode" : use_rencode, @@ -73,6 +83,39 @@ def get_packet_encoding_caps(): caps["yaml.version"] = yaml_version return caps + +#all the encoders we know about, in their default preference order: +ALL_ENCODERS = ["bencode", "rencode", "yaml"] + +_ENCODERS = { + "rencode" : do_rencode, + "bencode" : do_bencode, + "yaml" : do_yaml, + } + +def get_enabled_encoders(): + enabled = [x for x,b in { + "rencode" : use_rencode, + "bencode" : use_bencode, + "yaml" : use_yaml, + }.items() if b] + #order them: + return [x for x in ALL_ENCODERS if x in enabled] + + +def get_encoder(e): + assert e in ALL_ENCODERS, "invalid encoder name: %s" % e + assert e in get_enabled_encoders(), "%s is not available" % e + return _ENCODERS[e] + +def get_encoder_name(e): + assert e in _ENCODERS.values(), "invalid encoder: %s" % e + for k,v in _ENCODERS.items(): + if v==e: + return k + raise Exception("impossible bug!") + + def get_packet_encoding_type(protocol_flags): if protocol_flags & FLAGS_RENCODE: return "rencode" diff --git a/src/xpra/net/protocol.py b/src/xpra/net/protocol.py index 91736f8181..03c99863db 100644 --- a/src/xpra/net/protocol.py +++ b/src/xpra/net/protocol.py @@ -23,12 +23,11 @@ from xpra.util import repr_ellipsized from xpra.net.bytestreams import ABORT from xpra.net import compression -from xpra.net.compression import nocompress, zcompress, bzcompress, lz4_compress, Compressed, LevelCompressed, get_compression_caps, InvalidCompressionException -from xpra.net.header import unpack_header, pack_header, pack_header_and_data, FLAGS_RENCODE, FLAGS_YAML, FLAGS_CIPHER, FLAGS_NOHEADER -from xpra.net.crypto import get_crypto_caps, get_cipher from xpra.net import packet_encoding -from xpra.net.packet_encoding import InvalidPacketEncodingException, get_packet_encoding_caps, rencode_dumps, decode, has_rencode, \ - bencode, has_bencode, yaml_encode, has_yaml +from xpra.net.compression import get_compression_caps, decompress, InvalidCompressionException, Compressed, LevelCompressed +from xpra.net.packet_encoding import get_packet_encoding_caps, decode, InvalidPacketEncodingException +from xpra.net.header import unpack_header, pack_header, pack_header_and_data, FLAGS_CIPHER, FLAGS_NOHEADER +from xpra.net.crypto import get_crypto_caps, get_cipher #stupid python version breakage: @@ -116,8 +115,10 @@ def __init__(self, scheduler, conn, process_packet_cb, get_packet_cb=None): self.receive_aliases = {} self._log_stats = None #None here means auto-detect self._closed = False + self.encoder = "none" self._encoder = self.noencode - self._compress = nocompress + self.compressor = "none" + self._compress = compression.nocompress self.compression_level = 0 self.cipher_in = None self.cipher_in_name = None @@ -136,16 +137,9 @@ def __init__(self, scheduler, conn, process_packet_cb, get_packet_cb=None): STATE_FIELDS = ("max_packet_size", "large_packets", "send_aliases", "receive_aliases", "cipher_in", "cipher_in_name", "cipher_in_block_size", "cipher_out", "cipher_out_name", "cipher_out_block_size", - "compression_level") + "compression_level", "encoder", "compressor") def save_state(self): - state = { - "zlib" : self._compress==zcompress, - "bz2" : self._compress==bzcompress, - "lz4" : lz4_compress and self._compress==lz4_compress, - "bencode" : self._encoder == self.bencode, - "rencode" : self._encoder == self.rencode, - "yaml" : self._encoder == self.yaml - } + state = {} for x in Protocol.STATE_FIELDS: state[x] = getattr(self, x) return state @@ -155,23 +149,9 @@ def restore_state(self, state): for x in Protocol.STATE_FIELDS: assert x in state, "field %s is missing" % x setattr(self, x, state[x]) - if state.get("lz4"): - self.enable_lz4() - elif state.get("bz2"): - self.enable_bz2() - elif state.get("zlib"): - self.enable_zlib() - else: - self.enable_nocompress() - - if state.get("rencode"): - self.enable_rencode() - elif state.get("bencode"): - self.enable_bencode() - elif state.get("yaml"): - self.enable_yaml() - else: - raise Exception("invalid state: no encoder specified!") + #special handling for compressor / encoder which are named objects: + self.enable_compressor(self.compressor) + self.enable_encoder(self.encoder) def wait_for_io_threads_exit(self, timeout=None): for t in (self._read_thread, self._write_thread): @@ -221,25 +201,15 @@ def get_info(self): "output.cipher" : self.cipher_out_name or "", "large_packets" : self.large_packets, "compression_level" : self.compression_level, - "max_packet_size" : self.max_packet_size} - if self._compress==zcompress: - info["compression"] = "zlib" - elif self._compress==lz4_compress: - info["compression"] = "lz4" - elif self._compress==bzcompress: - info["compression"] = "bz2" - elif self._compress==nocompress: - info["compression"] = "none" + "max_packet_size" : self.max_packet_size, + "compressor" : compression.get_compressor_name(self._compress), + "encoder" : packet_encoding.get_encoder_name(self._encoder)} for k,v in self.send_aliases.items(): info["send_alias." + str(k)] = v info["send_alias." + str(v)] = k for k,v in self.receive_aliases.items(): info["receive_alias." + str(k)] = v info["receive_alias." + str(v)] = k - try: - info["encoder"] = self._encoder.__name__ - except: - log.error("no __name__ defined on %s (type: %s)", self._encoder, type(self._encoder)) c = self._conn if c: try: @@ -370,101 +340,53 @@ def new_tree(append): def enable_default_encoder(self): - if packet_encoding.use_bencode: - self.enable_bencode() - elif packet_encoding.use_rencode: - self.enable_rencode() - else: - assert packet_encoding.use_yaml, "no packet encoders available!" - self.enable_yaml() + opts = packet_encoding.get_enabled_encoders() + assert len(opts)>0, "no packet encoders available!" + self.enable_encoder(opts[0]) def enable_encoder_from_caps(self, caps): - if packet_encoding.use_rencode and caps.boolget("rencode"): - self.enable_rencode() - elif packet_encoding.use_yaml and caps.boolget("yaml"): - self.enable_yaml() - elif packet_encoding.use_bencode and caps.boolget("bencode", True): - self.enable_bencode() - else: - log.error("no matching packet encoder found!") - return False - return True - - def enable_bencode(self): - assert has_bencode, "bencode cannot be enabled: the module failed to load!" - log("enable_bencode()") - self._encoder = self.bencode - - def enable_rencode(self): - assert has_rencode, "rencode cannot be enabled: the module failed to load!" - log("enable_rencode()") - self._encoder = self.rencode + opts = packet_encoding.get_enabled_encoders() + for e in opts: + if caps.boolget("rencode"): + self.enable_encoder(e) + return True + log.error("no matching packet encoder found!") + return False - def enable_yaml(self): - assert has_yaml, "yaml cannot be enabled: the module failed to load!" - log("enable_yaml()") - self._encoder = self.yaml + def enable_encoder(self, e): + self._encoder = packet_encoding.get_encoder(e) + self.encoder = e def enable_default_compressor(self): - if compression.use_zlib: - self.enable_zlib() - elif compression.use_lz4: - self.enable_lz4() - elif compression.use_bz2: - self.enable_bz2() + opts = compression.get_enabled_compressors() + if len(opts)>0: + self.enable_compressor(opts[0]) else: - self.enable_nocompress() + self.enable_compressor("none") def enable_compressor_from_caps(self, caps): if self.compression_level==0: - self.enable_nocompress() + self.enable_compressor("none") return - if caps.boolget("lz4") and compression.use_lz4 and self.compression_level==1: - self.enable_lz4() - elif caps.boolget("zlib") and compression.use_zlib: - self.enable_zlib() - elif caps.boolget("bz2") and compression.use_bz2: - self.enable_bz2() - #retry lz4 (without level check) - elif caps.boolget("lz4") and compression.use_lz4: - self.enable_lz4() - else: - log.error("no matching compressor found!") - self.enable_nocompress() - - def enable_nocompress(self): - log("nocompress()") - self._compress = nocompress - - def enable_zlib(self): - log("enable_zlib()") - self._compress = zcompress - - def enable_lz4(self): - assert compression.use_lz4, "lz4 cannot be enabled: the module failed to load!" - log("enable_lz4()") - self._compress = lz4_compress - - def enable_bz2(self): - log("enable_bz2()") - self._compress = bzcompress + opts = compression.get_enabled_compressors() + for c in opts: #ie: [zlib, lz4, bz2] + if caps.boolget(c): + self.enable_compressor(c) + return + log.warn("compression disabled: no matching compressor found") + self.enable_nocompress() + def enable_compressor(self, compressor): + self._compress = compression.get_compressor(compressor) + self.compressor = compressor + log("enable_compressor(%s): %s", compressor, self._compress) def noencode(self, data): #just send data as a string for clients that don't understand xpra packet format: return ": ".join([str(x) for x in data])+"\n", FLAGS_NOHEADER - def bencode(self, data): - return bencode(data), 0 - - def rencode(self, data): - return rencode_dumps(data), FLAGS_RENCODE - - def yaml(self, data): - return yaml_encode(data), FLAGS_YAML - def encode(self, packet_in): """ @@ -558,7 +480,7 @@ def _io_thread_loop(self, name, callback): except (OSError, IOError, socket_error), e: if not self._closed: self._internal_error("%s connection %s reset: %s" % (name, self._conn, e), exc_info=e.args[0] in ABORT) - except Exception, e: + except: #can happen during close(), in which case we just ignore: if not self._closed: log.error("%s error on %s", name, self._conn, exc_info=True) @@ -758,7 +680,7 @@ def debug_str(s): #uncompress if needed: if compression_level>0: try: - data = compression.decompress(data, compression_level) + data = decompress(data, compression_level) except InvalidCompressionException, e: self.invalid("invalid compression: %s" % e, data) return diff --git a/src/xpra/server/server_base.py b/src/xpra/server/server_base.py index f3037e11bc..ece53173c6 100644 --- a/src/xpra/server/server_base.py +++ b/src/xpra/server/server_base.py @@ -796,46 +796,25 @@ def debug_usage(): elif command=="compression": if len(args)!=1: return argn_err(1) - compression = args[0].lower() - opts = ("lz4", "zlib", "bz2") - if compression=="lz4": + c = args[0].lower() + from xpra.net import compression + opts = compression.get_enabled_compressors() #ie: [lz4, bz2, zlib] + if c in opts: for cproto in protos: - cproto.enable_lz4() - forward_all_clients(["enable_lz4"]) - return success() - elif compression=="zlib": - for cproto in protos: - cproto.enable_zlib() - forward_all_clients(["enable_zlib"]) - return success() - elif compression=="bz2": - for cproto in protos: - cproto.enable_zlib() - forward_all_clients(["enable_bz2"]) + cproto.enable_compressor(c) + forward_all_clients(["enable_%s" % c]) return success() return arg_err("must be one of: %s" % (", ".join(opts))) elif command=="encoder": if len(args)!=1: return argn_err(1) - encoder = args[0].lower() - from xpra.net.packet_encoding import use_bencode, use_rencode, use_yaml - opts = [x for x,b in {"bencode" : use_bencode, - "rencode" : use_rencode, - "yaml" : use_yaml}.items() if b] - if encoder=="bencode": - for cproto in protos: - cproto.enable_bencode() - forward_all_clients(["enable_bencode"]) - return success() - elif encoder=="rencode": - for cproto in protos: - cproto.enable_rencode() - forward_all_clients(["enable_rencode"]) - return success() - elif encoder=="yaml": + e = args[0].lower() + from xpra.net import packet_encoding + opts = packet_encoding.get_enabled_encoders() #ie: [rencode, bencode, yaml] + if e in opts: for cproto in protos: - cproto.enable_yaml() - forward_all_clients(["enable_yaml"]) + cproto.enable_encoder(e) + forward_all_clients(["enable_%s" % e]) return success() return arg_err("must be one of: %s" % (", ".join(opts))) elif command=="sound-output":