Coverage for /home/antoine/projects/xpra-git/dist/python3/lib64/python/xpra/client/mixins/rpc.py : 44%
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of Xpra.
2# Copyright (C) 2010-2020 Antoine Martin <antoine@xpra.org>
3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
4# later version. See the file COPYING for details.
5#pylint: disable-msg=E1101
7from xpra.util import envint, AtomicInteger
8from xpra.os_util import monotonic_time
9from xpra.util import typedict
10from xpra.client.mixins.stub_client_mixin import StubClientMixin
11from xpra.log import Logger
13log = Logger("client", "rpc")
15RPC_TIMEOUT = envint("XPRA_RPC_TIMEOUT", 5000)
18"""
19Utility superclass for client classes that handle RPC calls
20"""
21class RPCClient(StubClientMixin):
23 def __init__(self):
24 StubClientMixin.__init__(self)
25 #rpc / dbus:
26 self.rpc_counter = AtomicInteger()
27 self.rpc_pending_requests = {}
28 self.server_dbus_proxy = False
29 self.server_rpc_types = []
30 self.rpc_filter_timers = {}
32 def cleanup(self):
33 timers = tuple(self.rpc_filter_timers.values())
34 self.rpc_filter_timers = {}
35 for t in timers:
36 self.source_remove(t)
39 def run(self):
40 pass
43 def parse_server_capabilities(self, c : typedict) -> bool:
44 self.server_dbus_proxy = c.boolget("dbus_proxy")
45 #default for pre-0.16 servers:
46 if self.server_dbus_proxy:
47 default_rpc_types = ["dbus"]
48 else:
49 default_rpc_types = []
50 self.server_rpc_types = c.strtupleget("rpc-types", default_rpc_types)
51 return True
53 def process_ui_capabilities(self, caps : typedict):
54 pass
57 def rpc_call(self, rpc_type, rpc_args, reply_handler=None, error_handler=None):
58 assert rpc_type in self.server_rpc_types, "server does not support %s rpc" % rpc_type
59 rpcid = self.rpc_counter.increase()
60 self.rpc_filter_pending(rpcid)
61 #keep track of this request (for timeout / error and reply callbacks):
62 req = monotonic_time(), rpc_type, rpc_args, reply_handler, error_handler
63 self.rpc_pending_requests[rpcid] = req
64 log("sending %s rpc request %s to server: %s", rpc_type, rpcid, req)
65 packet = ["rpc", rpc_type, rpcid] + rpc_args
66 self.send(*packet)
67 self.rpc_filter_timers[rpcid] = self.timeout_add(RPC_TIMEOUT, self.rpc_filter_pending, rpcid)
69 def rpc_filter_pending(self, rpcid):
70 """ removes timed out dbus requests """
71 del self.rpc_filter_timers[rpcid]
72 for k in tuple(self.rpc_pending_requests.keys()):
73 v = self.rpc_pending_requests.get(k)
74 if v is None:
75 continue
76 t, rpc_type, _rpc_args, _reply_handler, ecb = v
77 if 1000*(monotonic_time()-t)>=RPC_TIMEOUT:
78 log.warn("%s rpc request: %s has timed out", rpc_type, _rpc_args)
79 try:
80 del self.rpc_pending_requests[k]
81 if ecb is not None:
82 ecb("timeout")
83 except Exception as e:
84 log.error("Error during timeout handler for %s rpc callback:", rpc_type)
85 log.error(" %s", e)
86 del e
89 ######################################################################
90 #packet handlers
91 def _process_rpc_reply(self, packet):
92 rpc_type, rpcid, success, args = packet[1:5]
93 log("rpc_reply: %s", (rpc_type, rpcid, success, args))
94 v = self.rpc_pending_requests.get(rpcid)
95 assert v is not None, "pending dbus handler not found for id %s" % rpcid
96 assert rpc_type==v[1], "rpc reply type does not match: expected %s got %s" % (v[1], rpc_type)
97 del self.rpc_pending_requests[rpcid]
98 if success:
99 ctype = "ok"
100 rh = v[-2] #ok callback
101 else:
102 ctype = "error"
103 rh = v[-1] #error callback
104 if rh is None:
105 log("no %s rpc callback defined, return values=%s", ctype, args)
106 return
107 log("calling %s callback %s(%s)", ctype, rh, args)
108 try:
109 rh(*args)
110 except Exception as e:
111 log.error("Error processing rpc reply handler %s(%s) :", rh, args)
112 log.error(" %s", e)
115 def init_authenticated_packet_handlers(self):
116 log("init_authenticated_packet_handlers()")
117 self.add_packet_handler("rpc-reply", self._process_rpc_reply)