OLD | NEW |
| (Empty) |
1 # Copyright (c) 2001-2008 Twisted Matrix Laboratories. | |
2 # See LICENSE for details. | |
3 | |
4 """ | |
5 Tests for implementations of L{IReactorUNIX} and L{IReactorUNIXDatagram}. | |
6 """ | |
7 | |
8 import stat, os, sys, types | |
9 import socket | |
10 | |
11 from twisted.internet import interfaces, reactor, protocol, error, address, defe
r, utils | |
12 from twisted.python import lockfile | |
13 from twisted.trial import unittest | |
14 | |
15 from twisted.test.test_tcp import MyServerFactory, MyClientFactory | |
16 | |
17 | |
18 class FailedConnectionClientFactory(protocol.ClientFactory): | |
19 def __init__(self, onFail): | |
20 self.onFail = onFail | |
21 | |
22 def clientConnectionFailed(self, connector, reason): | |
23 self.onFail.errback(reason) | |
24 | |
25 | |
26 | |
27 class UnixSocketTestCase(unittest.TestCase): | |
28 """ | |
29 Test unix sockets. | |
30 """ | |
31 def test_peerBind(self): | |
32 """ | |
33 The address passed to the server factory's C{buildProtocol} method and | |
34 the address returned by the connected protocol's transport's C{getPeer} | |
35 method match the address the client socket is bound to. | |
36 """ | |
37 filename = self.mktemp() | |
38 peername = self.mktemp() | |
39 serverFactory = MyServerFactory() | |
40 connMade = serverFactory.protocolConnectionMade = defer.Deferred() | |
41 unixPort = reactor.listenUNIX(filename, serverFactory) | |
42 self.addCleanup(unixPort.stopListening) | |
43 unixSocket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | |
44 self.addCleanup(unixSocket.close) | |
45 unixSocket.bind(peername) | |
46 unixSocket.connect(filename) | |
47 def cbConnMade(proto): | |
48 expected = address.UNIXAddress(peername) | |
49 self.assertEqual(serverFactory.peerAddresses, [expected]) | |
50 self.assertEqual(proto.transport.getPeer(), expected) | |
51 connMade.addCallback(cbConnMade) | |
52 return connMade | |
53 | |
54 | |
55 def test_dumber(self): | |
56 """ | |
57 L{IReactorUNIX.connectUNIX} can be used to connect a client to a server | |
58 started with L{IReactorUNIX.listenUNIX}. | |
59 """ | |
60 filename = self.mktemp() | |
61 serverFactory = MyServerFactory() | |
62 serverConnMade = defer.Deferred() | |
63 serverFactory.protocolConnectionMade = serverConnMade | |
64 unixPort = reactor.listenUNIX(filename, serverFactory) | |
65 self.addCleanup(unixPort.stopListening) | |
66 clientFactory = MyClientFactory() | |
67 clientConnMade = defer.Deferred() | |
68 clientFactory.protocolConnectionMade = clientConnMade | |
69 c = reactor.connectUNIX(filename, clientFactory) | |
70 d = defer.gatherResults([serverConnMade, clientConnMade]) | |
71 def allConnected((serverProtocol, clientProtocol)): | |
72 | |
73 # Incidental assertion which may or may not be redundant with some | |
74 # other test. This probably deserves its own test method. | |
75 self.assertEqual(clientFactory.peerAddresses, | |
76 [address.UNIXAddress(filename)]) | |
77 | |
78 clientProtocol.transport.loseConnection() | |
79 serverProtocol.transport.loseConnection() | |
80 d.addCallback(allConnected) | |
81 return d | |
82 | |
83 | |
84 def test_mode(self): | |
85 """ | |
86 The UNIX socket created by L{IReactorUNIX.listenUNIX} is created with | |
87 the mode specified. | |
88 """ | |
89 mode = 0600 | |
90 filename = self.mktemp() | |
91 serverFactory = MyServerFactory() | |
92 unixPort = reactor.listenUNIX(filename, serverFactory, mode=mode) | |
93 self.addCleanup(unixPort.stopListening) | |
94 self.assertEquals(stat.S_IMODE(os.stat(filename).st_mode), mode) | |
95 | |
96 | |
97 def test_pidFile(self): | |
98 """ | |
99 A lockfile is created and locked when L{IReactorUNIX.listenUNIX} is | |
100 called and released when the Deferred returned by the L{IListeningPort} | |
101 provider's C{stopListening} method is called back. | |
102 """ | |
103 filename = self.mktemp() | |
104 serverFactory = MyServerFactory() | |
105 serverConnMade = defer.Deferred() | |
106 serverFactory.protocolConnectionMade = serverConnMade | |
107 unixPort = reactor.listenUNIX(filename, serverFactory, wantPID=True) | |
108 self.assertTrue(lockfile.isLocked(filename + ".lock")) | |
109 | |
110 # XXX This part would test something about the checkPID parameter, but | |
111 # it doesn't actually. It should be rewritten to test the several | |
112 # different possible behaviors. -exarkun | |
113 clientFactory = MyClientFactory() | |
114 clientConnMade = defer.Deferred() | |
115 clientFactory.protocolConnectionMade = clientConnMade | |
116 c = reactor.connectUNIX(filename, clientFactory, checkPID=1) | |
117 | |
118 d = defer.gatherResults([serverConnMade, clientConnMade]) | |
119 def _portStuff((serverProtocol, clientProto)): | |
120 | |
121 # Incidental assertion which may or may not be redundant with some | |
122 # other test. This probably deserves its own test method. | |
123 self.assertEqual(clientFactory.peerAddresses, | |
124 [address.UNIXAddress(filename)]) | |
125 | |
126 clientProto.transport.loseConnection() | |
127 serverProtocol.transport.loseConnection() | |
128 return unixPort.stopListening() | |
129 d.addCallback(_portStuff) | |
130 | |
131 def _check(ignored): | |
132 self.failIf(lockfile.isLocked(filename + ".lock"), 'locked') | |
133 d.addCallback(_check) | |
134 return d | |
135 | |
136 | |
137 def test_socketLocking(self): | |
138 """ | |
139 L{IReactorUNIX.listenUNIX} raises L{error.CannotListenError} if passed | |
140 the name of a file on which a server is already listening. | |
141 """ | |
142 filename = self.mktemp() | |
143 serverFactory = MyServerFactory() | |
144 unixPort = reactor.listenUNIX(filename, serverFactory, wantPID=True) | |
145 | |
146 self.assertRaises( | |
147 error.CannotListenError, | |
148 reactor.listenUNIX, filename, serverFactory, wantPID=True) | |
149 | |
150 def stoppedListening(ign): | |
151 unixPort = reactor.listenUNIX(filename, serverFactory, wantPID=True) | |
152 return unixPort.stopListening() | |
153 | |
154 return unixPort.stopListening().addCallback(stoppedListening) | |
155 | |
156 | |
157 def _uncleanSocketTest(self, callback): | |
158 self.filename = self.mktemp() | |
159 source = ("from twisted.internet import protocol, reactor\n" | |
160 "reactor.listenUNIX(%r, protocol.ServerFactory(), wantPID=True
)\n") % (self.filename,) | |
161 env = {'PYTHONPATH': os.pathsep.join(sys.path)} | |
162 | |
163 d = utils.getProcessValue(sys.executable, ("-u", "-c", source), env=env) | |
164 d.addCallback(callback) | |
165 return d | |
166 | |
167 | |
168 def test_uncleanServerSocketLocking(self): | |
169 """ | |
170 If passed C{True} for the C{wantPID} parameter, a server can be started | |
171 listening with L{IReactorUNIX.listenUNIX} when passed the name of a | |
172 file on which a previous server which has not exited cleanly has been | |
173 listening using the C{wantPID} option. | |
174 """ | |
175 def ranStupidChild(ign): | |
176 # If this next call succeeds, our lock handling is correct. | |
177 p = reactor.listenUNIX(self.filename, MyServerFactory(), wantPID=Tru
e) | |
178 return p.stopListening() | |
179 return self._uncleanSocketTest(ranStupidChild) | |
180 | |
181 | |
182 def test_connectToUncleanServer(self): | |
183 """ | |
184 If passed C{True} for the C{checkPID} parameter, a client connection | |
185 attempt made with L{IReactorUNIX.connectUNIX} fails with | |
186 L{error.BadFileError}. | |
187 """ | |
188 def ranStupidChild(ign): | |
189 d = defer.Deferred() | |
190 f = FailedConnectionClientFactory(d) | |
191 c = reactor.connectUNIX(self.filename, f, checkPID=True) | |
192 return self.assertFailure(d, error.BadFileError) | |
193 return self._uncleanSocketTest(ranStupidChild) | |
194 | |
195 | |
196 def _reprTest(self, serverFactory, factoryName): | |
197 """ | |
198 Test the C{__str__} and C{__repr__} implementations of a UNIX port when | |
199 used with the given factory. | |
200 """ | |
201 filename = self.mktemp() | |
202 unixPort = reactor.listenUNIX(filename, serverFactory) | |
203 | |
204 connectedString = "<%s on %r>" % (factoryName, filename) | |
205 self.assertEqual(repr(unixPort), connectedString) | |
206 self.assertEqual(str(unixPort), connectedString) | |
207 | |
208 d = defer.maybeDeferred(unixPort.stopListening) | |
209 def stoppedListening(ign): | |
210 unconnectedString = "<%s (not listening)>" % (factoryName,) | |
211 self.assertEqual(repr(unixPort), unconnectedString) | |
212 self.assertEqual(str(unixPort), unconnectedString) | |
213 d.addCallback(stoppedListening) | |
214 return d | |
215 | |
216 | |
217 def test_reprWithClassicFactory(self): | |
218 """ | |
219 The two string representations of the L{IListeningPort} returned by | |
220 L{IReactorUNIX.listenUNIX} contains the name of the classic factory | |
221 class being used and the filename on which the port is listening or | |
222 indicates that the port is not listening. | |
223 """ | |
224 class ClassicFactory: | |
225 def doStart(self): | |
226 pass | |
227 | |
228 def doStop(self): | |
229 pass | |
230 | |
231 # Sanity check | |
232 self.assertIsInstance(ClassicFactory, types.ClassType) | |
233 | |
234 return self._reprTest( | |
235 ClassicFactory(), "twisted.test.test_unix.ClassicFactory") | |
236 | |
237 | |
238 def test_reprWithNewStyleFactory(self): | |
239 """ | |
240 The two string representations of the L{IListeningPort} returned by | |
241 L{IReactorUNIX.listenUNIX} contains the name of the new-style factory | |
242 class being used and the filename on which the port is listening or | |
243 indicates that the port is not listening. | |
244 """ | |
245 class NewStyleFactory(object): | |
246 def doStart(self): | |
247 pass | |
248 | |
249 def doStop(self): | |
250 pass | |
251 | |
252 # Sanity check | |
253 self.assertIsInstance(NewStyleFactory, type) | |
254 | |
255 return self._reprTest( | |
256 NewStyleFactory(), "twisted.test.test_unix.NewStyleFactory") | |
257 | |
258 | |
259 | |
260 class ClientProto(protocol.ConnectedDatagramProtocol): | |
261 started = stopped = False | |
262 gotback = None | |
263 | |
264 def __init__(self): | |
265 self.deferredStarted = defer.Deferred() | |
266 self.deferredGotBack = defer.Deferred() | |
267 | |
268 def stopProtocol(self): | |
269 self.stopped = True | |
270 | |
271 def startProtocol(self): | |
272 self.started = True | |
273 self.deferredStarted.callback(None) | |
274 | |
275 def datagramReceived(self, data): | |
276 self.gotback = data | |
277 self.deferredGotBack.callback(None) | |
278 | |
279 class ServerProto(protocol.DatagramProtocol): | |
280 started = stopped = False | |
281 gotwhat = gotfrom = None | |
282 | |
283 def __init__(self): | |
284 self.deferredStarted = defer.Deferred() | |
285 self.deferredGotWhat = defer.Deferred() | |
286 | |
287 def stopProtocol(self): | |
288 self.stopped = True | |
289 | |
290 def startProtocol(self): | |
291 self.started = True | |
292 self.deferredStarted.callback(None) | |
293 | |
294 def datagramReceived(self, data, addr): | |
295 self.gotfrom = addr | |
296 self.transport.write("hi back", addr) | |
297 self.gotwhat = data | |
298 self.deferredGotWhat.callback(None) | |
299 | |
300 | |
301 | |
302 class DatagramUnixSocketTestCase(unittest.TestCase): | |
303 """ | |
304 Test datagram UNIX sockets. | |
305 """ | |
306 def test_exchange(self): | |
307 """ | |
308 Test that a datagram can be sent to and received by a server and vice | |
309 versa. | |
310 """ | |
311 clientaddr = self.mktemp() | |
312 serveraddr = self.mktemp() | |
313 sp = ServerProto() | |
314 cp = ClientProto() | |
315 s = reactor.listenUNIXDatagram(serveraddr, sp) | |
316 self.addCleanup(s.stopListening) | |
317 c = reactor.connectUNIXDatagram(serveraddr, cp, bindAddress=clientaddr) | |
318 self.addCleanup(c.stopListening) | |
319 | |
320 d = defer.gatherResults([sp.deferredStarted, cp.deferredStarted]) | |
321 def write(ignored): | |
322 cp.transport.write("hi") | |
323 return defer.gatherResults([sp.deferredGotWhat, | |
324 cp.deferredGotBack]) | |
325 | |
326 def _cbTestExchange(ignored): | |
327 self.failUnlessEqual("hi", sp.gotwhat) | |
328 self.failUnlessEqual(clientaddr, sp.gotfrom) | |
329 self.failUnlessEqual("hi back", cp.gotback) | |
330 | |
331 d.addCallback(write) | |
332 d.addCallback(_cbTestExchange) | |
333 return d | |
334 | |
335 | |
336 def test_cannotListen(self): | |
337 """ | |
338 L{IReactorUNIXDatagram.listenUNIXDatagram} raises | |
339 L{error.CannotListenError} if the unix socket specified is already in | |
340 use. | |
341 """ | |
342 addr = self.mktemp() | |
343 p = ServerProto() | |
344 s = reactor.listenUNIXDatagram(addr, p) | |
345 self.failUnlessRaises(error.CannotListenError, reactor.listenUNIXDatagra
m, addr, p) | |
346 s.stopListening() | |
347 os.unlink(addr) | |
348 | |
349 # test connecting to bound and connected (somewhere else) address | |
350 | |
351 def _reprTest(self, serverProto, protocolName): | |
352 """ | |
353 Test the C{__str__} and C{__repr__} implementations of a UNIX datagram | |
354 port when used with the given protocol. | |
355 """ | |
356 filename = self.mktemp() | |
357 unixPort = reactor.listenUNIXDatagram(filename, serverProto) | |
358 | |
359 connectedString = "<%s on %r>" % (protocolName, filename) | |
360 self.assertEqual(repr(unixPort), connectedString) | |
361 self.assertEqual(str(unixPort), connectedString) | |
362 | |
363 stopDeferred = defer.maybeDeferred(unixPort.stopListening) | |
364 def stoppedListening(ign): | |
365 unconnectedString = "<%s (not listening)>" % (protocolName,) | |
366 self.assertEqual(repr(unixPort), unconnectedString) | |
367 self.assertEqual(str(unixPort), unconnectedString) | |
368 stopDeferred.addCallback(stoppedListening) | |
369 return stopDeferred | |
370 | |
371 | |
372 def test_reprWithClassicProtocol(self): | |
373 """ | |
374 The two string representations of the L{IListeningPort} returned by | |
375 L{IReactorUNIXDatagram.listenUNIXDatagram} contains the name of the | |
376 classic protocol class being used and the filename on which the port is | |
377 listening or indicates that the port is not listening. | |
378 """ | |
379 class ClassicProtocol: | |
380 def makeConnection(self, transport): | |
381 pass | |
382 | |
383 def doStop(self): | |
384 pass | |
385 | |
386 # Sanity check | |
387 self.assertIsInstance(ClassicProtocol, types.ClassType) | |
388 | |
389 return self._reprTest( | |
390 ClassicProtocol(), "twisted.test.test_unix.ClassicProtocol") | |
391 | |
392 | |
393 def test_reprWithNewStyleProtocol(self): | |
394 """ | |
395 The two string representations of the L{IListeningPort} returned by | |
396 L{IReactorUNIXDatagram.listenUNIXDatagram} contains the name of the | |
397 new-style protocol class being used and the filename on which the port | |
398 is listening or indicates that the port is not listening. | |
399 """ | |
400 class NewStyleProtocol(object): | |
401 def makeConnection(self, transport): | |
402 pass | |
403 | |
404 def doStop(self): | |
405 pass | |
406 | |
407 # Sanity check | |
408 self.assertIsInstance(NewStyleProtocol, type) | |
409 | |
410 return self._reprTest( | |
411 NewStyleProtocol(), "twisted.test.test_unix.NewStyleProtocol") | |
412 | |
413 | |
414 | |
415 if not interfaces.IReactorUNIX(reactor, None): | |
416 UnixSocketTestCase.skip = "This reactor does not support UNIX domain sockets
" | |
417 if not interfaces.IReactorUNIXDatagram(reactor, None): | |
418 DatagramUnixSocketTestCase.skip = "This reactor does not support UNIX datagr
am sockets" | |
OLD | NEW |