Hide keyboard shortcuts

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 

6 

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 

12 

13log = Logger("client", "rpc") 

14 

15RPC_TIMEOUT = envint("XPRA_RPC_TIMEOUT", 5000) 

16 

17 

18""" 

19Utility superclass for client classes that handle RPC calls 

20""" 

21class RPCClient(StubClientMixin): 

22 

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 = {} 

31 

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) 

37 

38 

39 def run(self): 

40 pass 

41 

42 

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 

52 

53 def process_ui_capabilities(self, caps : typedict): 

54 pass 

55 

56 

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) 

68 

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 

87 

88 

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) 

113 

114 

115 def init_authenticated_packet_handlers(self): 

116 log("init_authenticated_packet_handlers()") 

117 self.add_packet_handler("rpc-reply", self._process_rpc_reply)