1 # -*- test-case-name: twisted.application.test.test_internet,twisted.test.test_application,twisted.test.test_cooperator -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
8 Here are services to run clients, servers and periodic services using
11 If you want to run a server service, L{StreamServerEndpointService} defines a
12 service that can wrap an arbitrary L{IStreamServerEndpoint
13 <twisted.internet.interfaces.IStreamServerEndpoint>}
14 as an L{IService}. See also L{twisted.application.strports.service} for
15 constructing one of these directly from a descriptive string.
17 Additionally, this module (dynamically) defines various Service subclasses that
18 let you represent clients and servers in a Service hierarchy. Endpoints APIs
19 should be preferred for stream server services, but since those APIs do not yet
20 exist for clients or datagram services, many of these are still useful.
25 UNIXServer, UNIXClient,
28 UNIXDatagramServer, UNIXDatagramClient,
31 These classes take arbitrary arguments in their constructors and pass
32 them straight on to their respective reactor.listenXXX or
33 reactor.connectXXX calls.
35 For example, the following service starts a web server on port 8080:
36 C{TCPServer(8080, server.Site(r))}. See the documentation for the
37 reactor.listen/connect* methods for more information.
42 from twisted.python import log
43 from twisted.application import service
44 from twisted.internet import task
46 from twisted.internet.defer import CancelledError
49 def _maybeGlobalReactor(maybeReactor):
51 @return: the argument, or the global reactor if the argument is C{None}.
53 if maybeReactor is None:
54 from twisted.internet import reactor
60 class _VolatileDataService(service.Service):
64 def __getstate__(self):
65 d = service.Service.__getstate__(self)
66 for attr in self.volatile:
73 class _AbstractServer(_VolatileDataService):
75 @cvar volatile: list of attribute to remove from pickling.
76 @type volatile: C{list}
78 @ivar method: the type of method to call on the reactor, one of B{TCP},
79 B{UDP}, B{SSL} or B{UNIX}.
82 @ivar reactor: the current running reactor.
83 @type reactor: a provider of C{IReactorTCP}, C{IReactorUDP},
84 C{IReactorSSL} or C{IReactorUnix}.
86 @ivar _port: instance of port set when the service is started.
87 @type _port: a provider of L{twisted.internet.interfaces.IListeningPort}.
96 def __init__(self, *args, **kwargs):
98 if 'reactor' in kwargs:
99 self.reactor = kwargs.pop("reactor")
103 def privilegedStartService(self):
104 service.Service.privilegedStartService(self)
105 self._port = self._getPort()
108 def startService(self):
109 service.Service.startService(self)
110 if self._port is None:
111 self._port = self._getPort()
114 def stopService(self):
115 service.Service.stopService(self)
116 # TODO: if startup failed, should shutdown skip stopListening?
118 if self._port is not None:
119 d = self._port.stopListening()
126 Wrapper around the appropriate listen method of the reactor.
128 @return: the port object returned by the listen method.
129 @rtype: an object providing
130 L{twisted.internet.interfaces.IListeningPort}.
132 return getattr(_maybeGlobalReactor(self.reactor),
133 'listen%s' % (self.method,))(*self.args, **self.kwargs)
137 class _AbstractClient(_VolatileDataService):
139 @cvar volatile: list of attribute to remove from pickling.
140 @type volatile: C{list}
142 @ivar method: the type of method to call on the reactor, one of B{TCP},
143 B{UDP}, B{SSL} or B{UNIX}.
146 @ivar reactor: the current running reactor.
147 @type reactor: a provider of C{IReactorTCP}, C{IReactorUDP},
148 C{IReactorSSL} or C{IReactorUnix}.
150 @ivar _connection: instance of connection set when the service is started.
151 @type _connection: a provider of L{twisted.internet.interfaces.IConnector}.
153 volatile = ['_connection']
159 def __init__(self, *args, **kwargs):
161 if 'reactor' in kwargs:
162 self.reactor = kwargs.pop("reactor")
166 def startService(self):
167 service.Service.startService(self)
168 self._connection = self._getConnection()
171 def stopService(self):
172 service.Service.stopService(self)
173 if self._connection is not None:
174 self._connection.disconnect()
178 def _getConnection(self):
180 Wrapper around the appropriate connect method of the reactor.
182 @return: the port object returned by the connect method.
183 @rtype: an object providing L{twisted.internet.interfaces.IConnector}.
185 return getattr(_maybeGlobalReactor(self.reactor),
186 'connect%s' % (self.method,))(*self.args, **self.kwargs)
192 """Connect to %(tran)s
194 Call reactor.connect%(method)s when the service starts, with the
195 arguments given to the constructor.
198 """Serve %(tran)s clients
200 Call reactor.listen%(method)s when the service starts, with the
201 arguments given to the constructor. When the service stops,
202 stop listening. See twisted.internet.interfaces for documentation
203 on arguments to the reactor method.
208 for tran in 'TCP UNIX SSL UDP UNIXDatagram Multicast'.split():
209 for side in 'Server Client'.split():
210 if tran == "Multicast" and side == "Client":
212 base = globals()['_Abstract'+side]
213 method = {'Generic': 'With'}.get(tran, tran)
214 doc = _doc[side]%vars()
215 klass = types.ClassType(tran+side, (base,),
216 {'method': method, '__doc__': doc})
217 globals()[tran+side] = klass
221 class GenericServer(_AbstractServer):
223 Serve Generic clients
225 Call reactor.listenWith when the service starts, with the arguments given to
226 the constructor. When the service stops, stop listening. See
227 twisted.internet.interfaces for documentation on arguments to the reactor
230 This service is deprecated (because reactor.listenWith is deprecated).
234 def __init__(self, *args, **kwargs):
236 'GenericServer was deprecated in Twisted 10.1.',
237 category=DeprecationWarning,
239 _AbstractServer.__init__(self, *args, **kwargs)
243 class GenericClient(_AbstractClient):
247 Call reactor.connectWith when the service starts, with the arguments given
250 This service is deprecated (because reactor.connectWith is deprecated).
254 def __init__(self, *args, **kwargs):
256 'GenericClient was deprecated in Twisted 10.1.',
257 category=DeprecationWarning,
259 _AbstractClient.__init__(self, *args, **kwargs)
263 class TimerService(_VolatileDataService):
265 """Service to periodically call a function
267 Every C{step} seconds call the given function with the given arguments.
268 The service starts the calls when it starts, and cancels them
274 def __init__(self, step, callable, *args, **kwargs):
276 self.call = (callable, args, kwargs)
278 def startService(self):
279 service.Service.startService(self)
280 callable, args, kwargs = self.call
281 # we have to make a new LoopingCall each time we're started, because
282 # an active LoopingCall remains active when serialized. If
283 # LoopingCall were a _VolatileDataService, we wouldn't need to do
285 self._loop = task.LoopingCall(callable, *args, **kwargs)
286 self._loop.start(self.step, now=True).addErrback(self._failed)
288 def _failed(self, why):
289 # make a note that the LoopingCall is no longer looping, so we don't
290 # try to shut it down a second time in stopService. I think this
291 # should be in LoopingCall. -warner
292 self._loop.running = False
295 def stopService(self):
296 if self._loop.running:
298 return service.Service.stopService(self)
302 class CooperatorService(service.Service):
304 Simple L{service.IService} which starts and stops a L{twisted.internet.task.Cooperator}.
307 self.coop = task.Cooperator(started=False)
310 def coiterate(self, iterator):
311 return self.coop.coiterate(iterator)
314 def startService(self):
318 def stopService(self):
323 class StreamServerEndpointService(service.Service, object):
325 A L{StreamServerEndpointService} is an L{IService} which runs a server on a
326 listening port described by an L{IStreamServerEndpoint
327 <twisted.internet.interfaces.IStreamServerEndpoint>}.
329 @ivar factory: A server factory which will be used to listen on the
332 @ivar endpoint: An L{IStreamServerEndpoint
333 <twisted.internet.interfaces.IStreamServerEndpoint>} provider
334 which will be used to listen when the service starts.
336 @ivar _waitingForPort: a Deferred, if C{listen} has yet been invoked on the
337 endpoint, otherwise None.
339 @ivar _raiseSynchronously: Defines error-handling behavior for the case
340 where C{listen(...)} raises an exception before C{startService} or
341 C{privilegedStartService} have completed.
343 @type _raiseSynchronously: C{bool}
348 _raiseSynchronously = None
350 def __init__(self, endpoint, factory):
351 self.endpoint = endpoint
352 self.factory = factory
353 self._waitingForPort = None
356 def privilegedStartService(self):
358 Start listening on the endpoint.
360 service.Service.privilegedStartService(self)
361 self._waitingForPort = self.endpoint.listen(self.factory)
364 if self._raiseSynchronously:
365 raisedNow.append(err)
366 elif not err.check(CancelledError):
368 self._waitingForPort.addErrback(handleIt)
370 raisedNow[0].raiseException()
373 def startService(self):
375 Start listening on the endpoint, unless L{privilegedStartService} got
376 around to it already.
378 service.Service.startService(self)
379 if self._waitingForPort is None:
380 self.privilegedStartService()
383 def stopService(self):
385 Stop listening on the port if it is already listening, otherwise,
386 cancel the attempt to listen.
388 @return: a L{Deferred<twisted.internet.defer.Deferred>} which fires
389 with C{None} when the port has stopped listening.
391 self._waitingForPort.cancel()
394 return port.stopListening()
395 d = self._waitingForPort.addCallback(stopIt)
396 def stop(passthrough):
404 __all__ = (['TimerService', 'CooperatorService', 'MulticastServer',
405 'StreamServerEndpointService'] +
407 for tran in 'Generic TCP UNIX SSL UDP UNIXDatagram'.split()
408 for side in 'Server Client'.split()])