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 -*- 

2from __future__ import annotations 

3 

4# -- stdlib -- 

5from typing import Optional, Sequence, TYPE_CHECKING, cast 

6import logging 

7 

8# -- third party -- 

9from gevent import Greenlet, getcurrent 

10 

11# -- own -- 

12from endpoint import Endpoint, EndpointDied 

13import wire 

14 

15# -- typing -- 

16if TYPE_CHECKING: 

17 from server.core import Core # noqa: F401 

18 

19 

20# -- code -- 

21log = logging.getLogger('server.core.endpoint') 

22 

23 

24class Pivot(Exception): 

25 pass 

26 

27 

28class Client(object): 

29 __slots__ = ('_ep', '_gr', 'core', '_') 

30 

31 def __init__(self, core: Core, ep: Optional[Endpoint]): 

32 self._ep: Optional[Endpoint] = ep 

33 self._gr: Optional[Greenlet] = None 

34 self.core = core 

35 

36 self._: dict = {} 

37 

38 def _before_serve(self) -> None: 

39 core = self.core 

40 self._gr = getcurrent() 

41 core.events.client_connected.emit(self) 

42 

43 def _serve(self) -> None: 

44 core = self.core 

45 tbl = core.events.client_command 

46 

47 while True: 

48 if not self._ep: 48 ↛ 49line 48 didn't jump to line 49, because the condition on line 48 was never true

49 break 

50 

51 try: 

52 for msg in self._ep.messages(timeout=90): 52 ↛ 48line 52 didn't jump to line 48, because the loop on line 52 didn't complete

53 tbl[msg.__class__].emit((self, msg)) 

54 

55 except EndpointDied: 55 ↛ 58line 55 didn't jump to line 58

56 break 

57 

58 except Pivot: 

59 continue 

60 

61 except Exception as e: 

62 if core.options.testing: 

63 core.crash(e) 

64 raise 

65 log.exception("Error occurred when handling client command") 

66 

67 self._ep and self._ep.close() 

68 self._ep = None 

69 self._gr = None 

70 core.events.client_dropped.emit(self) 

71 

72 def serve(self) -> None: 

73 self._before_serve() 

74 self._serve() 

75 

76 def terminate(self) -> None: 

77 self._gr and self._gr.kill(EndpointDied) 

78 

79 def is_dead(self) -> bool: 

80 return not self._gr or self._gr.ready() 

81 

82 def pivot_to(self, other: Client) -> None: 

83 if not self._ep: 

84 raise Exception("self._ep is not valid!") 

85 

86 other._ep = self._ep 

87 self._ep = None 

88 self._gr and self._gr.kill() # this skips client_dropped event 

89 

90 core = self.core 

91 if other._ep: 

92 other._gr and other._gr.kill(Pivot) 

93 else: 

94 other._gr = core.runner.spawn(other._serve) 

95 

96 def __repr__(self) -> str: 

97 if self._ep: 

98 return '%s:%s:%s' % ( 

99 self.__class__.__name__, 

100 *self._ep.address, 

101 ) 

102 else: 

103 return '%s:?' % ( 

104 self.__class__.__name__, 

105 ) 

106 

107 def get_greenlet(self) -> Optional[Greenlet]: 

108 return self._gr 

109 

110 def write(self, v: wire.ServerToClient) -> None: 

111 ep = self._ep 

112 if ep: ep.write(v) 

113 

114 def write_bulk(self, vl: Sequence[wire.ServerToClient]) -> None: 

115 ep = self._ep 

116 if ep: ep.write_bulk(cast(Sequence[wire.Message], vl)) 

117 

118 def raw_write(self, v: bytes) -> None: 

119 ep = self._ep 

120 if ep: ep.raw_write(v)