From 8d8ed0ce6efb46f5918e5b13fb2d21d0f3273258 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Wed, 23 Jul 2014 13:02:09 +0000 Subject: [PATCH] add support for yaml as packet encoder git-svn-id: https://xpra.org/svn/Xpra/trunk@6934 3bb7dfac-3a0b-4e04-842a-767bc560f471 --- src/tests/xpra/net/test_yaml.py | 24 +++++++++++++ src/xpra/client/client_base.py | 4 ++- src/xpra/client/gtk_base/session_info.py | 4 ++- src/xpra/client/ui_client_base.py | 13 ++++++- src/xpra/net/protocol.py | 43 +++++++++++++++++++++-- src/xpra/server/proxy_instance_process.py | 2 ++ src/xpra/server/server_base.py | 10 +++++- src/xpra/server/server_core.py | 4 ++- 8 files changed, 96 insertions(+), 8 deletions(-) create mode 100755 src/tests/xpra/net/test_yaml.py diff --git a/src/tests/xpra/net/test_yaml.py b/src/tests/xpra/net/test_yaml.py new file mode 100755 index 0000000000..ef863a5eab --- /dev/null +++ b/src/tests/xpra/net/test_yaml.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +# coding=utf8 +# This file is part of Xpra. +# Copyright (C) 2014 Antoine Martin +# Xpra is released under the terms of the GNU GPL v2, or, at your option, any +# later version. See the file COPYING for details. + +from xpra.net.protocol import yaml_encode,yaml_decode + +def test(): + d = [12, {'pycrypto.version': '2.6.1', 'window.resize-counter': True, 'build.version': '0.14.0'}] + v = yaml_encode(d) + d2 = yaml_decode(v) + print("yaml_encode(%s)=%s" % (d, v)) + print("yaml_decode(%s)=%s" % (v, d2)) + assert d2 + + +def main(): + test() + + +if __name__ == "__main__": + main() diff --git a/src/xpra/client/client_base.py b/src/xpra/client/client_base.py index 0a7cc42000..7b22ddbfaf 100644 --- a/src/xpra/client/client_base.py +++ b/src/xpra/client/client_base.py @@ -15,7 +15,7 @@ from xpra.log import Logger log = Logger("client") -from xpra.net.protocol import Protocol, use_lz4, use_rencode, get_network_caps +from xpra.net.protocol import Protocol, use_lz4, use_rencode, use_yaml, get_network_caps from xpra.scripts.config import ENCRYPTION_CIPHERS from xpra.version_util import version_compat_check, get_version_info, get_platform_info, local_version from xpra.platform.features import GOT_PASSWORD_PROMPT_SUGGESTION @@ -497,6 +497,8 @@ def parse_network_capabilities(self): c = self.server_capabilities if use_rencode and c.boolget("rencode"): self._protocol.enable_rencode() + elif use_yaml and c.boolget("yaml"): + self._protocol.enable_yaml() if use_lz4 and c.boolget("lz4") and self.compression_level==1: self._protocol.enable_lz4() if self.encryption: diff --git a/src/xpra/client/gtk_base/session_info.py b/src/xpra/client/gtk_base/session_info.py index f5747b640b..fc5afc4c49 100644 --- a/src/xpra/client/gtk_base/session_info.py +++ b/src/xpra/client/gtk_base/session_info.py @@ -634,6 +634,8 @@ def get_encoder_list(caps): l.append("bencode") if caps.get("rencode", False): l.append("rencode") + if caps.get("yaml", False): + l.append("yaml") return l self.client_packet_encoders_label.set_text(", ".join(get_encoder_list(get_network_caps()))) self.server_packet_encoders_label.set_text(", ".join(get_encoder_list(self.client.server_capabilities))) @@ -695,7 +697,7 @@ def set_sound_info(label, details, supported, prop): if level==0: compression_str = "None" else: - compression_str = " + ".join([x for x in ("zlib", "lz4", "bencode", "rencode") if protocol_state.get(x, False)==True]) + compression_str = " + ".join([x for x in ("zlib", "lz4", "bencode", "rencode", "yaml") if protocol_state.get(x, False)==True]) compression_str += ", level %s" % level self.compression_label.set_text(compression_str) diff --git a/src/xpra/client/ui_client_base.py b/src/xpra/client/ui_client_base.py index aca3e879f6..0201201067 100644 --- a/src/xpra/client/ui_client_base.py +++ b/src/xpra/client/ui_client_base.py @@ -912,13 +912,21 @@ def make_hello(self): "encodings.core" : self.get_core_encodings(), }) control_commands = ["show_session_info", "enable_bencode", "enable_zlib"] - from xpra.net.protocol import use_bencode, use_rencode + from xpra.net.protocol import use_bencode, use_rencode, use_yaml + for k,b in {"lz4" : use_lz4, + "bencode" : use_bencode, + "rencode" : use_rencode, + "yaml" : use_yaml}.items(): + if b: + control_commands.append("enable_"+k) if use_lz4: control_commands.append("enable_lz4") if use_bencode: control_commands.append("enable_bencode") if use_rencode: control_commands.append("enable_rencode") + if use_yaml: + control_commands.append("enable_yaml") capabilities["control_commands"] = control_commands for k,v in codec_versions.items(): capabilities["encoding.%s.version" % k] = v @@ -1361,6 +1369,9 @@ def _process_control(self, packet): 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/protocol.py b/src/xpra/net/protocol.py index 9aa5d921d1..a4d1070568 100644 --- a/src/xpra/net/protocol.py +++ b/src/xpra/net/protocol.py @@ -84,6 +84,20 @@ def lz4_compress(packet, level): use_bencode = has_bencode and os.environ.get("XPRA_USE_BENCODER", "1")=="1" log("protocol: has_bencode=%s, use_bencode=%s, version=%s", has_bencode, use_bencode, bencode_version) +yaml_encode, yaml_decode, yaml_version = None, None, None +try: + #json messes with strings and unicode (makes it unusable for us) + import yaml + yaml_encode = yaml.dump + yaml_decode = yaml.load + yaml_version = yaml.__version__ +except ImportError: + log("yaml not found") +has_yaml = yaml_encode is not None and yaml_decode is not None +use_yaml = has_yaml and os.environ.get("XPRA_USE_YAML", "1")=="1" +log("protocol: has_yaml=%s, use_yaml=%s, version=%s", has_yaml, use_yaml, yaml_version) + + #stupid python version breakage: if sys.version > '3': long = int #@ReservedAssignment @@ -148,6 +162,7 @@ def get_network_caps(legacy=True): "digest" : ("hmac", "xor"), "rencode" : use_rencode, "bencode" : use_bencode, + "yaml" : use_yaml, "lz4" : use_lz4, "mmap" : mmap, } @@ -170,9 +185,14 @@ def get_network_caps(legacy=True): pass if has_rencode: + assert rencode_version is not None caps["rencode.version"] = rencode_version if has_bencode: + assert bencode_version is not None caps["bencode.version"] = bencode_version + if has_yaml: + assert yaml_version is not None + caps["yaml.version"] = yaml_version return caps @@ -229,6 +249,7 @@ class Protocol(object): FLAGS_RENCODE = 0x1 FLAGS_CIPHER = 0x2 + FLAGS_JSON = 0x4 FLAGS_NOHEADER = 0x40 def __init__(self, scheduler, conn, process_packet_cb, get_packet_cb=None): @@ -257,7 +278,7 @@ def __init__(self, scheduler, conn, process_packet_cb, get_packet_cb=None): self.output_packetcount = 0 self.output_raw_packetcount = 0 #initial value which may get increased by client/server after handshake: - self.max_packet_size = 32*1024 + self.max_packet_size = 256*1024 self.abs_max_packet_size = 32*1024*1024 self.large_packets = ["hello"] self.send_aliases = {} @@ -293,6 +314,7 @@ def save_state(self): state["lz4"] = lz4_compress and self._compress==lz4_compress state["bencode"] = self._encoder == self.bencode state["rencode"] = self._encoder == self.rencode + state["yaml"] = self._encoder == self.yaml #state["connection"] = self._conn return state @@ -305,6 +327,8 @@ def restore_state(self, state): self.enable_lz4() if state.get("rencode", False): self.enable_rencode() + elif state.get("yaml", False): + self.enable_yaml() def wait_for_io_threads_exit(self, timeout=None): for t in (self._read_thread, self._write_thread): @@ -524,6 +548,12 @@ def enable_rencode(self): log("enable_rencode()") self._encoder = self.rencode + 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_zlib(self): log("enable_zlib()") self._compress = zcompress @@ -543,6 +573,10 @@ def bencode(self, data): def rencode(self, data): return rencode_dumps(data), Protocol.FLAGS_RENCODE + def yaml(self, data): + return yaml_encode(data), Protocol.FLAGS_JSON + + def encode(self, packet_in): """ Given a packet (tuple or list of items), converts it for the wire. @@ -605,11 +639,11 @@ def encode(self, packet_in): packet[0] = packet_type self.verify_packet(packet) raise e - if len(main_packet)>LARGE_PACKET_SIZE and packet_in[0] not in self.large_packets: + if False and len(main_packet)>LARGE_PACKET_SIZE and packet_in[0] not in self.large_packets: log.warn("found large packet (%s bytes): %s, argument types:%s, sizes: %s, packet head=%s", len(main_packet), packet_in[0], [type(x) for x in packet[1:]], [len(str(x)) for x in packet[1:]], repr_ellipsized(packet)) #compress, but don't bother for small packets: - if level>0 and len(main_packet)>min_comp_size: + if False and level>0 and len(main_packet)>min_comp_size: cl, cdata = self._compress(main_packet, level) packets.append((0, cl, cdata)) else: @@ -856,6 +890,9 @@ def debug_str(s): if protocol_flags & Protocol.FLAGS_RENCODE: assert has_rencode, "we don't support rencode mode but the other end sent us a rencoded packet! not an xpra client?" packet = list(rencode_loads(data)) + elif protocol_flags & Protocol.FLAGS_JSON: + assert has_yaml, "we don't support yaml mode but the other end sent us a yaml packet! not an xpra client?" + packet = list(yaml_decode(data)) else: #if sys.version>='3': # data = data.decode("latin1") diff --git a/src/xpra/server/proxy_instance_process.py b/src/xpra/server/proxy_instance_process.py index c9b17c49ad..b69bdfdaa4 100644 --- a/src/xpra/server/proxy_instance_process.py +++ b/src/xpra/server/proxy_instance_process.py @@ -352,6 +352,8 @@ def filter_client_caps(self, caps): def filter_server_caps(self, caps): if caps.get("rencode", False): self.server_protocol.enable_rencode() + elif caps.get("yaml", False): + self.server_protocol.enable_yaml() return self.filter_caps(caps, ("aliases", )) def filter_caps(self, caps, prefixes): diff --git a/src/xpra/server/server_base.py b/src/xpra/server/server_base.py index b21a84e395..d06b093b72 100644 --- a/src/xpra/server/server_base.py +++ b/src/xpra/server/server_base.py @@ -810,7 +810,10 @@ def debug_usage(): if len(args)!=1: return argn_err(1) encoder = args[0].lower() - opts = ("bencode", "rencode") + from xpra.net.protocol 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() @@ -821,6 +824,11 @@ def debug_usage(): cproto.enable_rencode() forward_all_clients(["enable_rencode"]) return success() + elif encoder=="yaml": + for cproto in protos: + cproto.enable_yaml() + forward_all_clients(["enable_yaml"]) + return success() return arg_err("must be one of: %s" % (", ".join(opts))) elif command=="sound-output": if len(args)<1: diff --git a/src/xpra/server/server_core.py b/src/xpra/server/server_core.py index 5fa234a789..5159762ee7 100644 --- a/src/xpra/server/server_core.py +++ b/src/xpra/server/server_core.py @@ -28,7 +28,7 @@ from xpra.platform import set_application_name from xpra.os_util import load_binary_file, get_machine_id, get_user_uuid, SIGNAMES from xpra.version_util import version_compat_check, get_version_info, get_platform_info, get_host_info, local_version, mk -from xpra.net.protocol import Protocol, use_lz4, use_rencode, new_cipher_caps, get_network_caps, repr_ellipsized +from xpra.net.protocol import Protocol, use_lz4, use_rencode, use_yaml, new_cipher_caps, get_network_caps, repr_ellipsized from xpra.server.background_worker import stop_worker from xpra.daemon_thread import make_daemon_thread from xpra.server.proxy import XpraProxy @@ -408,6 +408,8 @@ def _process_hello(self, proto, packet): proto.set_compression_level(c.intget("compression_level", self.compression_level)) if use_rencode and c.boolget("rencode"): proto.enable_rencode() + elif use_yaml and c.boolget("yaml"): + proto.enable_yaml() else: proto.enable_bencode()