OLD | NEW |
| (Empty) |
1 | |
2 """Base classes handy for use with PB clients. | |
3 """ | |
4 | |
5 from twisted.spread import pb | |
6 | |
7 from twisted.spread.pb import PBClientFactory | |
8 from twisted.internet import protocol | |
9 from twisted.python import log | |
10 | |
11 class NewCredPerspective(pb.Avatar): | |
12 def attached(self, mind): | |
13 return self | |
14 def detached(self, mind): | |
15 pass | |
16 | |
17 class ReconnectingPBClientFactory(PBClientFactory, | |
18 protocol.ReconnectingClientFactory): | |
19 """Reconnecting client factory for PB brokers. | |
20 | |
21 Like PBClientFactory, but if the connection fails or is lost, the factory | |
22 will attempt to reconnect. | |
23 | |
24 Instead of using f.getRootObject (which gives a Deferred that can only | |
25 be fired once), override the gotRootObject method. | |
26 | |
27 Instead of using the newcred f.login (which is also one-shot), call | |
28 f.startLogin() with the credentials and client, and override the | |
29 gotPerspective method. | |
30 | |
31 Instead of using the oldcred f.getPerspective (also one-shot), call | |
32 f.startGettingPerspective() with the same arguments, and override | |
33 gotPerspective. | |
34 | |
35 gotRootObject and gotPerspective will be called each time the object is | |
36 received (once per successful connection attempt). You will probably want | |
37 to use obj.notifyOnDisconnect to find out when the connection is lost. | |
38 | |
39 If an authorization error occurs, failedToGetPerspective() will be | |
40 invoked. | |
41 | |
42 To use me, subclass, then hand an instance to a connector (like | |
43 TCPClient). | |
44 """ | |
45 | |
46 def __init__(self): | |
47 PBClientFactory.__init__(self) | |
48 self._doingLogin = False | |
49 self._doingGetPerspective = False | |
50 | |
51 def clientConnectionFailed(self, connector, reason): | |
52 PBClientFactory.clientConnectionFailed(self, connector, reason) | |
53 # Twisted-1.3 erroneously abandons the connection on non-UserErrors. | |
54 # To avoid this bug, don't upcall, and implement the correct version | |
55 # of the method here. | |
56 if self.continueTrying: | |
57 self.connector = connector | |
58 self.retry() | |
59 | |
60 def clientConnectionLost(self, connector, reason): | |
61 PBClientFactory.clientConnectionLost(self, connector, reason, | |
62 reconnecting=True) | |
63 RCF = protocol.ReconnectingClientFactory | |
64 RCF.clientConnectionLost(self, connector, reason) | |
65 | |
66 def clientConnectionMade(self, broker): | |
67 self.resetDelay() | |
68 PBClientFactory.clientConnectionMade(self, broker) | |
69 if self._doingLogin: | |
70 self.doLogin(self._root) | |
71 if self._doingGetPerspective: | |
72 self.doGetPerspective(self._root) | |
73 self.gotRootObject(self._root) | |
74 | |
75 # oldcred methods | |
76 | |
77 def getPerspective(self, *args): | |
78 raise RuntimeError, "getPerspective is one-shot: use startGettingPerspec
tive instead" | |
79 | |
80 def startGettingPerspective(self, username, password, serviceName, | |
81 perspectiveName=None, client=None): | |
82 self._doingGetPerspective = True | |
83 if perspectiveName == None: | |
84 perspectiveName = username | |
85 self._oldcredArgs = (username, password, serviceName, | |
86 perspectiveName, client) | |
87 | |
88 def doGetPerspective(self, root): | |
89 # oldcred getPerspective() | |
90 (username, password, | |
91 serviceName, perspectiveName, client) = self._oldcredArgs | |
92 d = self._cbAuthIdentity(root, username, password) | |
93 d.addCallback(self._cbGetPerspective, | |
94 serviceName, perspectiveName, client) | |
95 d.addCallbacks(self.gotPerspective, self.failedToGetPerspective) | |
96 | |
97 | |
98 # newcred methods | |
99 | |
100 def login(self, *args): | |
101 raise RuntimeError, "login is one-shot: use startLogin instead" | |
102 | |
103 def startLogin(self, credentials, client=None): | |
104 self._credentials = credentials | |
105 self._client = client | |
106 self._doingLogin = True | |
107 | |
108 def doLogin(self, root): | |
109 # newcred login() | |
110 d = self._cbSendUsername(root, self._credentials.username, | |
111 self._credentials.password, self._client) | |
112 d.addCallbacks(self.gotPerspective, self.failedToGetPerspective) | |
113 | |
114 | |
115 # methods to override | |
116 | |
117 def gotPerspective(self, perspective): | |
118 """The remote avatar or perspective (obtained each time this factory | |
119 connects) is now available.""" | |
120 pass | |
121 | |
122 def gotRootObject(self, root): | |
123 """The remote root object (obtained each time this factory connects) | |
124 is now available. This method will be called each time the connection | |
125 is established and the object reference is retrieved.""" | |
126 pass | |
127 | |
128 def failedToGetPerspective(self, why): | |
129 """The login process failed, most likely because of an authorization | |
130 failure (bad password), but it is also possible that we lost the new | |
131 connection before we managed to send our credentials. | |
132 """ | |
133 log.msg("ReconnectingPBClientFactory.failedToGetPerspective") | |
134 if why.check(pb.PBConnectionLost): | |
135 log.msg("we lost the brand-new connection") | |
136 # retrying might help here, let clientConnectionLost decide | |
137 return | |
138 # probably authorization | |
139 self.stopTrying() # logging in harder won't help | |
140 log.err(why) | |
OLD | NEW |