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# -*- coding: utf-8 -*- 

2# This file is part of Xpra. 

3# Copyright (C) 2010-2020 Antoine Martin <antoine@xpra.org> 

4# Xpra is released under the terms of the GNU GPL v2, or, at your option, any 

5# later version. See the file COPYING for details. 

6 

7import os 

8import time 

9 

10from xpra.util import envbool, envint, typedict, CLIENT_PING_TIMEOUT 

11from xpra.os_util import monotonic_time, POSIX 

12from xpra.server.source.stub_source_mixin import StubSourceMixin 

13from xpra.log import Logger 

14 

15log = Logger("network") 

16 

17PING_DETAILS = envbool("XPRA_PING_DETAILS", True) 

18PING_TIMEOUT = envint("XPRA_PING_TIMEOUT", 60) 

19 

20 

21class NetworkStateMixin(StubSourceMixin): 

22 

23 @classmethod 

24 def is_needed(cls, caps : typedict) -> bool: 

25 #the 'network-state' capability were only added in v4, 

26 #so we have to enable the mixin by default: 

27 return caps.boolget("network-state", True) 

28 

29 def init_state(self): 

30 self.last_ping_echoed_time = 0 

31 self.check_ping_echo_timers = {} 

32 self.ping_timer = None 

33 self.bandwidth_limit = 0 

34 

35 def cleanup(self): 

36 self.cancel_ping_echo_timers() 

37 self.cancel_ping_timer() 

38 

39 def get_caps(self) -> dict: 

40 return {"ping-echo-sourceid" : True} 

41 

42 def get_info(self) -> dict: 

43 lpe = 0 

44 if self.last_ping_echoed_time>0: 

45 lpe = int(monotonic_time()*1000-self.last_ping_echoed_time) 

46 info = { 

47 "bandwidth-limit" : { 

48 "setting" : self.bandwidth_limit or 0, 

49 }, 

50 "last-ping-echo" : lpe, 

51 } 

52 return info 

53 

54 ###################################################################### 

55 # pings: 

56 def ping(self): 

57 self.ping_timer = None 

58 #NOTE: all ping time/echo time/load avg values are in milliseconds 

59 now_ms = int(1000*monotonic_time()) 

60 log("sending ping to %s with time=%s", self.protocol, now_ms) 

61 self.send_async("ping", now_ms, int(time.time()*1000), will_have_more=False) 

62 timeout = PING_TIMEOUT 

63 self.check_ping_echo_timers[now_ms] = self.timeout_add(timeout*1000, 

64 self.check_ping_echo_timeout, now_ms, timeout) 

65 

66 def check_ping_echo_timeout(self, now_ms, timeout): 

67 self.check_ping_echo_timers.pop(now_ms, None) 

68 if self.last_ping_echoed_time<now_ms and not self.is_closed(): 

69 self.disconnect(CLIENT_PING_TIMEOUT, "waited %s seconds without a response" % timeout) 

70 

71 def cancel_ping_echo_timers(self): 

72 timers = self.check_ping_echo_timers.values() 

73 self.check_ping_echo_timers = {} 

74 for t in timers: 

75 self.source_remove(t) 

76 

77 def process_ping(self, time_to_echo, sid): 

78 l1,l2,l3 = 0,0,0 

79 cl = -1 

80 if PING_DETAILS: 

81 #send back the load average: 

82 if POSIX: 

83 fl1, fl2, fl3 = os.getloadavg() 

84 l1,l2,l3 = int(fl1*1000), int(fl2*1000), int(fl3*1000) 

85 #and the last client ping latency we measured (if any): 

86 stats = getattr(self, "statistics", None) 

87 if stats and stats.client_ping_latency: 

88 _, cl = stats.client_ping_latency[-1] 

89 cl = int(1000.0*cl) 

90 self.send_async("ping_echo", time_to_echo, l1, l2, l3, cl, sid, will_have_more=False) 

91 #if the client is pinging us, ping it too: 

92 if not self.ping_timer: 

93 self.ping_timer = self.timeout_add(500, self.ping) 

94 

95 def cancel_ping_timer(self): 

96 pt = self.ping_timer 

97 if pt: 

98 self.ping_timer = None 

99 self.source_remove(pt) 

100 

101 def process_ping_echo(self, packet): 

102 echoedtime, l1, l2, l3, server_ping_latency = packet[1:6] 

103 timer = self.check_ping_echo_timers.pop(echoedtime, None) 

104 if timer: 

105 self.source_remove(timer) 

106 self.last_ping_echoed_time = echoedtime 

107 client_ping_latency = monotonic_time()-echoedtime/1000.0 

108 stats = getattr(self, "statistics", None) 

109 if stats and 0<client_ping_latency<60: 

110 stats.client_ping_latency.append((monotonic_time(), client_ping_latency)) 

111 self.client_load = l1, l2, l3 

112 if 0<=server_ping_latency<60000 and stats: 

113 stats.server_ping_latency.append((monotonic_time(), server_ping_latency/1000.0)) 

114 log("ping echo client load=%s, measured server latency=%s", self.client_load, server_ping_latency) 

115 

116 

117 def update_connection_data(self, data): 

118 log("update_connection_data(%s)", data) 

119 if not isinstance(data, dict): 

120 raise TypeError("connection-data must be a dictionary") 

121 self.client_connection_data = data