1 # -*- test-case-name: twisted.internet.test.test_endpoints -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
5 Implementations of L{IStreamServerEndpoint} and L{IStreamClientEndpoint} that
6 wrap the L{IReactorTCP}, L{IReactorSSL}, and L{IReactorUNIX} interfaces.
8 This also implements an extensible mini-language for describing endpoints,
9 parsed by the L{clientFromString} and L{serverFromString} functions.
16 from zope.interface import implements, directlyProvides
19 from twisted.internet import interfaces, defer, error, fdesc
20 from twisted.internet.protocol import ClientFactory, Protocol
21 from twisted.plugin import IPlugin, getPlugins
22 from twisted.internet.interfaces import IStreamServerEndpointStringParser
23 from twisted.internet.interfaces import IStreamClientEndpointStringParser
24 from twisted.python.filepath import FilePath
25 from twisted.python.systemd import ListenFDs
28 __all__ = ["clientFromString", "serverFromString",
29 "TCP4ServerEndpoint", "TCP4ClientEndpoint",
30 "UNIXServerEndpoint", "UNIXClientEndpoint",
31 "SSL4ServerEndpoint", "SSL4ClientEndpoint",
32 "AdoptedStreamServerEndpoint"]
35 class _WrappingProtocol(Protocol):
37 Wrap another protocol in order to notify my user when a connection has
40 @ivar _connectedDeferred: The L{Deferred} that will callback
41 with the C{wrappedProtocol} when it is connected.
43 @ivar _wrappedProtocol: An L{IProtocol} provider that will be
47 def __init__(self, connectedDeferred, wrappedProtocol):
49 @param connectedDeferred: The L{Deferred} that will callback
50 with the C{wrappedProtocol} when it is connected.
52 @param wrappedProtocol: An L{IProtocol} provider that will be
55 self._connectedDeferred = connectedDeferred
56 self._wrappedProtocol = wrappedProtocol
58 for iface in [interfaces.IHalfCloseableProtocol,
59 interfaces.IFileDescriptorReceiver]:
60 if iface.providedBy(self._wrappedProtocol):
61 directlyProvides(self, iface)
66 Transparently pass through the wrapped protocol's log prefix.
68 if interfaces.ILoggingContext.providedBy(self._wrappedProtocol):
69 return self._wrappedProtocol.logPrefix()
70 return self._wrappedProtocol.__class__.__name__
73 def connectionMade(self):
75 Connect the C{self._wrappedProtocol} to our C{self.transport} and
76 callback C{self._connectedDeferred} with the C{self._wrappedProtocol}
78 self._wrappedProtocol.makeConnection(self.transport)
79 self._connectedDeferred.callback(self._wrappedProtocol)
82 def dataReceived(self, data):
84 Proxy C{dataReceived} calls to our C{self._wrappedProtocol}
86 return self._wrappedProtocol.dataReceived(data)
89 def fileDescriptorReceived(self, descriptor):
91 Proxy C{fileDescriptorReceived} calls to our C{self._wrappedProtocol}
93 return self._wrappedProtocol.fileDescriptorReceived(descriptor)
96 def connectionLost(self, reason):
98 Proxy C{connectionLost} calls to our C{self._wrappedProtocol}
100 return self._wrappedProtocol.connectionLost(reason)
103 def readConnectionLost(self):
105 Proxy L{IHalfCloseableProtocol.readConnectionLost} to our
106 C{self._wrappedProtocol}
108 self._wrappedProtocol.readConnectionLost()
111 def writeConnectionLost(self):
113 Proxy L{IHalfCloseableProtocol.writeConnectionLost} to our
114 C{self._wrappedProtocol}
116 self._wrappedProtocol.writeConnectionLost()
120 class _WrappingFactory(ClientFactory):
122 Wrap a factory in order to wrap the protocols it builds.
124 @ivar _wrappedFactory: A provider of I{IProtocolFactory} whose buildProtocol
125 method will be called and whose resulting protocol will be wrapped.
127 @ivar _onConnection: An L{Deferred} that fires when the protocol is
130 @ivar _connector: A L{connector <twisted.internet.interfaces.IConnector>}
131 that is managing the current or previous connection attempt.
133 protocol = _WrappingProtocol
135 def __init__(self, wrappedFactory):
137 @param wrappedFactory: A provider of I{IProtocolFactory} whose
138 buildProtocol method will be called and whose resulting protocol
141 self._wrappedFactory = wrappedFactory
142 self._onConnection = defer.Deferred(canceller=self._canceller)
145 def startedConnecting(self, connector):
147 A connection attempt was started. Remember the connector which started
148 said attempt, for use later.
150 self._connector = connector
153 def _canceller(self, deferred):
155 The outgoing connection attempt was cancelled. Fail that L{Deferred}
156 with a L{error.ConnectingCancelledError}.
158 @param deferred: The L{Deferred <defer.Deferred>} that was cancelled;
159 should be the same as C{self._onConnection}.
160 @type deferred: L{Deferred <defer.Deferred>}
162 @note: This relies on startedConnecting having been called, so it may
163 seem as though there's a race condition where C{_connector} may not
164 have been set. However, using public APIs, this condition is
165 impossible to catch, because a connection API
166 (C{connectTCP}/C{SSL}/C{UNIX}) is always invoked before a
167 L{_WrappingFactory}'s L{Deferred <defer.Deferred>} is returned to
168 C{connect()}'s caller.
173 error.ConnectingCancelledError(
174 self._connector.getDestination()))
175 self._connector.stopConnecting()
180 Start notifications are passed straight through to the wrapped factory.
182 self._wrappedFactory.doStart()
187 Stop notifications are passed straight through to the wrapped factory.
189 self._wrappedFactory.doStop()
192 def buildProtocol(self, addr):
194 Proxy C{buildProtocol} to our C{self._wrappedFactory} or errback
195 the C{self._onConnection} L{Deferred}.
197 @return: An instance of L{_WrappingProtocol} or C{None}
200 proto = self._wrappedFactory.buildProtocol(addr)
202 self._onConnection.errback()
204 return self.protocol(self._onConnection, proto)
207 def clientConnectionFailed(self, connector, reason):
209 Errback the C{self._onConnection} L{Deferred} when the
210 client connection fails.
212 if not self._onConnection.called:
213 self._onConnection.errback(reason)
217 class TCP4ServerEndpoint(object):
219 TCP server endpoint with an IPv4 configuration
221 @ivar _reactor: An L{IReactorTCP} provider.
224 @ivar _port: The port number on which to listen for incoming connections.
227 @ivar _backlog: size of the listen queue
229 @type _interface: str
230 @ivar _interface: the hostname to bind to, defaults to '' (all)
232 implements(interfaces.IStreamServerEndpoint)
234 def __init__(self, reactor, port, backlog=50, interface=''):
236 @param reactor: An L{IReactorTCP} provider.
237 @param port: The port number used listening
238 @param backlog: size of the listen queue
239 @param interface: the hostname to bind to, defaults to '' (all)
241 self._reactor = reactor
243 self._listenArgs = dict(backlog=50, interface='')
244 self._backlog = backlog
245 self._interface = interface
248 def listen(self, protocolFactory):
250 Implement L{IStreamServerEndpoint.listen} to listen on a TCP socket
252 return defer.execute(self._reactor.listenTCP,
255 backlog=self._backlog,
256 interface=self._interface)
260 class TCP4ClientEndpoint(object):
262 TCP client endpoint with an IPv4 configuration.
264 @ivar _reactor: An L{IReactorTCP} provider.
267 @ivar _host: The hostname to connect to as a C{str}
270 @ivar _port: The port to connect to as C{int}
273 @ivar _timeout: number of seconds to wait before assuming the
274 connection has failed.
276 @type _bindAddress: tuple
277 @type _bindAddress: a (host, port) tuple of local address to bind
280 implements(interfaces.IStreamClientEndpoint)
282 def __init__(self, reactor, host, port, timeout=30, bindAddress=None):
284 @param reactor: An L{IReactorTCP} provider
285 @param host: A hostname, used when connecting
286 @param port: The port number, used when connecting
287 @param timeout: number of seconds to wait before assuming the
288 connection has failed.
289 @param bindAddress: a (host, port tuple of local address to bind to,
292 self._reactor = reactor
295 self._timeout = timeout
296 self._bindAddress = bindAddress
299 def connect(self, protocolFactory):
301 Implement L{IStreamClientEndpoint.connect} to connect via TCP.
304 wf = _WrappingFactory(protocolFactory)
305 self._reactor.connectTCP(
306 self._host, self._port, wf,
307 timeout=self._timeout, bindAddress=self._bindAddress)
308 return wf._onConnection
314 class SSL4ServerEndpoint(object):
316 SSL secured TCP server endpoint with an IPv4 configuration.
318 @ivar _reactor: An L{IReactorSSL} provider.
321 @ivar _host: The hostname to connect to as a C{str}
324 @ivar _port: The port to connect to as C{int}
326 @type _sslContextFactory: L{OpenSSLCertificateOptions}
327 @var _sslContextFactory: SSL Configuration information as an
328 L{OpenSSLCertificateOptions}
331 @ivar _backlog: size of the listen queue
333 @type _interface: str
334 @ivar _interface: the hostname to bind to, defaults to '' (all)
336 implements(interfaces.IStreamServerEndpoint)
338 def __init__(self, reactor, port, sslContextFactory,
339 backlog=50, interface=''):
341 @param reactor: An L{IReactorSSL} provider.
342 @param port: The port number used listening
343 @param sslContextFactory: An instance of
344 L{twisted.internet._sslverify.OpenSSLCertificateOptions}.
345 @param timeout: number of seconds to wait before assuming the
346 connection has failed.
347 @param bindAddress: a (host, port tuple of local address to bind to,
350 self._reactor = reactor
352 self._sslContextFactory = sslContextFactory
353 self._backlog = backlog
354 self._interface = interface
357 def listen(self, protocolFactory):
359 Implement L{IStreamServerEndpoint.listen} to listen for SSL on a
362 return defer.execute(self._reactor.listenSSL, self._port,
364 contextFactory=self._sslContextFactory,
365 backlog=self._backlog,
366 interface=self._interface)
370 class SSL4ClientEndpoint(object):
372 SSL secured TCP client endpoint with an IPv4 configuration
374 @ivar _reactor: An L{IReactorSSL} provider.
377 @ivar _host: The hostname to connect to as a C{str}
380 @ivar _port: The port to connect to as C{int}
382 @type _sslContextFactory: L{OpenSSLCertificateOptions}
383 @var _sslContextFactory: SSL Configuration information as an
384 L{OpenSSLCertificateOptions}
387 @ivar _timeout: number of seconds to wait before assuming the
388 connection has failed.
390 @type _bindAddress: tuple
391 @ivar _bindAddress: a (host, port) tuple of local address to bind
394 implements(interfaces.IStreamClientEndpoint)
396 def __init__(self, reactor, host, port, sslContextFactory,
397 timeout=30, bindAddress=None):
399 @param reactor: An L{IReactorSSL} provider.
400 @param host: A hostname, used when connecting
401 @param port: The port number, used when connecting
402 @param sslContextFactory: SSL Configuration information as An instance
403 of L{OpenSSLCertificateOptions}.
404 @param timeout: number of seconds to wait before assuming the
405 connection has failed.
406 @param bindAddress: a (host, port tuple of local address to bind to,
409 self._reactor = reactor
412 self._sslContextFactory = sslContextFactory
413 self._timeout = timeout
414 self._bindAddress = bindAddress
417 def connect(self, protocolFactory):
419 Implement L{IStreamClientEndpoint.connect} to connect with SSL over
423 wf = _WrappingFactory(protocolFactory)
424 self._reactor.connectSSL(
425 self._host, self._port, wf, self._sslContextFactory,
426 timeout=self._timeout, bindAddress=self._bindAddress)
427 return wf._onConnection
433 class UNIXServerEndpoint(object):
435 UnixSocket server endpoint.
438 @ivar path: a path to a unix socket on the filesystem.
440 @type _listenArgs: dict
441 @ivar _listenArgs: A C{dict} of keyword args that will be passed
442 to L{IReactorUNIX.listenUNIX}
444 @var _reactor: An L{IReactorTCP} provider.
446 implements(interfaces.IStreamServerEndpoint)
448 def __init__(self, reactor, address, backlog=50, mode=0666, wantPID=0):
450 @param reactor: An L{IReactorUNIX} provider.
451 @param address: The path to the Unix socket file, used when listening
452 @param listenArgs: An optional dict of keyword args that will be
453 passed to L{IReactorUNIX.listenUNIX}
454 @param backlog: number of connections to allow in backlog.
455 @param mode: mode to set on the unix socket. This parameter is
456 deprecated. Permissions should be set on the directory which
457 contains the UNIX socket.
458 @param wantPID: if True, create a pidfile for the socket.
460 self._reactor = reactor
461 self._address = address
462 self._backlog = backlog
464 self._wantPID = wantPID
467 def listen(self, protocolFactory):
469 Implement L{IStreamServerEndpoint.listen} to listen on a UNIX socket.
471 return defer.execute(self._reactor.listenUNIX, self._address,
473 backlog=self._backlog,
475 wantPID=self._wantPID)
479 class UNIXClientEndpoint(object):
481 UnixSocket client endpoint.
484 @ivar _path: a path to a unix socket on the filesystem.
487 @ivar _timeout: number of seconds to wait before assuming the connection
490 @type _checkPID: bool
491 @ivar _checkPID: if True, check for a pid file to verify that a server
494 @var _reactor: An L{IReactorUNIX} provider.
496 implements(interfaces.IStreamClientEndpoint)
498 def __init__(self, reactor, path, timeout=30, checkPID=0):
500 @param reactor: An L{IReactorUNIX} provider.
501 @param path: The path to the Unix socket file, used when connecting
502 @param timeout: number of seconds to wait before assuming the
503 connection has failed.
504 @param checkPID: if True, check for a pid file to verify that a server
507 self._reactor = reactor
509 self._timeout = timeout
510 self._checkPID = checkPID
513 def connect(self, protocolFactory):
515 Implement L{IStreamClientEndpoint.connect} to connect via a
519 wf = _WrappingFactory(protocolFactory)
520 self._reactor.connectUNIX(
522 timeout=self._timeout,
523 checkPID=self._checkPID)
524 return wf._onConnection
530 class AdoptedStreamServerEndpoint(object):
532 An endpoint for listening on a file descriptor initialized outside of
535 @ivar _used: A C{bool} indicating whether this endpoint has been used to
536 listen with a factory yet. C{True} if so.
539 _setNonBlocking = staticmethod(fdesc.setNonBlocking)
541 def __init__(self, reactor, fileno, addressFamily):
543 @param reactor: An L{IReactorSocket} provider.
545 @param fileno: An integer file descriptor corresponding to a listening
546 I{SOCK_STREAM} socket.
548 @param addressFamily: The address family of the socket given by
551 self.reactor = reactor
553 self.addressFamily = addressFamily
557 def listen(self, factory):
559 Implement L{IStreamServerEndpoint.listen} to start listening on, and
560 then close, C{self._fileno}.
563 return defer.fail(error.AlreadyListened())
567 self._setNonBlocking(self.fileno)
568 port = self.reactor.adoptStreamPort(
569 self.fileno, self.addressFamily, factory)
570 self._close(self.fileno)
573 return defer.succeed(port)
577 def _parseTCP(factory, port, interface="", backlog=50):
579 Internal parser function for L{_parseServer} to convert the string
580 arguments for a TCP(IPv4) stream endpoint into the structured arguments.
582 @param factory: the protocol factory being parsed, or C{None}. (This was a
583 leftover argument from when this code was in C{strports}, and is now
584 mostly None and unused.)
586 @type factory: L{IProtocolFactory} or C{NoneType}
588 @param port: the integer port number to bind
591 @param interface: the interface IP to listen on
592 @param backlog: the length of the listen queue
593 @type backlog: C{str}
595 @return: a 2-tuple of (args, kwargs), describing the parameters to
596 L{IReactorTCP.listenTCP} (or, modulo argument 2, the factory, arguments
597 to L{TCP4ServerEndpoint}.
599 return (int(port), factory), {'interface': interface,
600 'backlog': int(backlog)}
604 def _parseUNIX(factory, address, mode='666', backlog=50, lockfile=True):
606 Internal parser function for L{_parseServer} to convert the string
607 arguments for a UNIX (AF_UNIX/SOCK_STREAM) stream endpoint into the
608 structured arguments.
610 @param factory: the protocol factory being parsed, or C{None}. (This was a
611 leftover argument from when this code was in C{strports}, and is now
612 mostly None and unused.)
614 @type factory: L{IProtocolFactory} or C{NoneType}
616 @param address: the pathname of the unix socket
617 @type address: C{str}
619 @param backlog: the length of the listen queue
620 @type backlog: C{str}
622 @param lockfile: A string '0' or '1', mapping to True and False
623 respectively. See the C{wantPID} argument to C{listenUNIX}
625 @return: a 2-tuple of (args, kwargs), describing the parameters to
626 L{IReactorTCP.listenUNIX} (or, modulo argument 2, the factory,
627 arguments to L{UNIXServerEndpoint}.
631 {'mode': int(mode, 8), 'backlog': int(backlog),
632 'wantPID': bool(int(lockfile))})
636 def _parseSSL(factory, port, privateKey="server.pem", certKey=None,
637 sslmethod=None, interface='', backlog=50):
639 Internal parser function for L{_parseServer} to convert the string
640 arguments for an SSL (over TCP/IPv4) stream endpoint into the structured
643 @param factory: the protocol factory being parsed, or C{None}. (This was a
644 leftover argument from when this code was in C{strports}, and is now
645 mostly None and unused.)
647 @type factory: L{IProtocolFactory} or C{NoneType}
649 @param port: the integer port number to bind
652 @param interface: the interface IP to listen on
653 @param backlog: the length of the listen queue
654 @type backlog: C{str}
656 @param privateKey: The file name of a PEM format private key file.
657 @type privateKey: C{str}
659 @param certKey: The file name of a PEM format certificate file.
660 @type certKey: C{str}
662 @param sslmethod: The string name of an SSL method, based on the name of a
663 constant in C{OpenSSL.SSL}. Must be one of: "SSLv23_METHOD",
664 "SSLv2_METHOD", "SSLv3_METHOD", "TLSv1_METHOD".
665 @type sslmethod: C{str}
667 @return: a 2-tuple of (args, kwargs), describing the parameters to
668 L{IReactorSSL.listenSSL} (or, modulo argument 2, the factory, arguments
669 to L{SSL4ServerEndpoint}.
671 from twisted.internet import ssl
675 if sslmethod is not None:
676 kw['sslmethod'] = getattr(ssl.SSL, sslmethod)
677 cf = ssl.DefaultOpenSSLContextFactory(privateKey, certKey, **kw)
678 return ((int(port), factory, cf),
679 {'interface': interface, 'backlog': int(backlog)})
682 class _SystemdParser(object):
684 Stream server endpoint string parser for the I{systemd} endpoint type.
686 @ivar prefix: See L{IStreamClientEndpointStringParser.prefix}.
688 @ivar _sddaemon: A L{ListenFDs} instance used to translate an index into an
689 actual file descriptor.
691 implements(IPlugin, IStreamServerEndpointStringParser)
693 _sddaemon = ListenFDs.fromEnvironment()
697 def _parseServer(self, reactor, domain, index):
699 Internal parser function for L{_parseServer} to convert the string
700 arguments for a systemd server endpoint into structured arguments for
701 L{AdoptedStreamServerEndpoint}.
703 @param reactor: An L{IReactorSocket} provider.
705 @param domain: The domain (or address family) of the socket inherited
706 from systemd. This is a string like C{"INET"} or C{"UNIX"}, ie the
707 name of an address family from the L{socket} module, without the
711 @param index: An offset into the list of file descriptors inherited from
715 @return: A two-tuple of parsed positional arguments and parsed keyword
716 arguments (a tuple and a dictionary). These can be used to
717 construct a L{AdoptedStreamServerEndpoint}.
720 fileno = self._sddaemon.inheritedDescriptors()[index]
721 addressFamily = getattr(socket, 'AF_' + domain)
722 return AdoptedStreamServerEndpoint(reactor, fileno, addressFamily)
725 def parseStreamServer(self, reactor, *args, **kwargs):
726 # Delegate to another function with a sane signature. This function has
727 # an insane signature to trick zope.interface into believing the
728 # interface is correctly implemented.
729 return self._parseServer(reactor, *args, **kwargs)
733 _serverParsers = {"tcp": _parseTCP,
738 _OP, _STRING = range(2)
740 def _tokenize(description):
742 Tokenize a strports string and yield each token.
744 @param description: a string as described by L{serverFromString} or
747 @return: an iterable of 2-tuples of (L{_OP} or L{_STRING}, string). Tuples
748 starting with L{_OP} will contain a second element of either ':' (i.e.
749 'next parameter') or '=' (i.e. 'assign parameter value'). For example,
750 the string 'hello:greet\=ing=world' would result in a generator
751 yielding these values::
761 nextOps = {':': ':=', '=': ':'}
762 description = iter(description)
763 for n in description:
765 yield _STRING, current
770 current += description.next()
773 yield _STRING, current
777 def _parse(description):
779 Convert a description string into a list of positional and keyword
780 parameters, using logic vaguely like what Python does.
782 @param description: a string as described by L{serverFromString} or
785 @return: a 2-tuple of C{(args, kwargs)}, where 'args' is a list of all
786 ':'-separated C{str}s not containing an '=' and 'kwargs' is a map of
787 all C{str}s which do contain an '='. For example, the result of
788 C{_parse('a:b:d=1:c')} would be C{(['a', 'b', 'c'], {'d': '1'})}.
793 args.append(sofar[0])
795 kw[sofar[0]] = sofar[1]
797 for (type, value) in _tokenize(description):
807 # Mappings from description "names" to endpoint constructors.
808 _endpointServerFactories = {
809 'TCP': TCP4ServerEndpoint,
810 'SSL': SSL4ServerEndpoint,
811 'UNIX': UNIXServerEndpoint,
814 _endpointClientFactories = {
815 'TCP': TCP4ClientEndpoint,
816 'SSL': SSL4ClientEndpoint,
817 'UNIX': UNIXClientEndpoint,
821 _NO_DEFAULT = object()
823 def _parseServer(description, factory, default=None):
825 Parse a stports description into a 2-tuple of arguments and keyword values.
827 @param description: A description in the format explained by
829 @type description: C{str}
831 @param factory: A 'factory' argument; this is left-over from
832 twisted.application.strports, it's not really used.
833 @type factory: L{IProtocolFactory} or L{None}
835 @param default: Deprecated argument, specifying the default parser mode to
836 use for unqualified description strings (those which do not have a ':'
838 @type default: C{str} or C{NoneType}
840 @return: a 3-tuple of (plugin or name, arguments, keyword arguments)
842 args, kw = _parse(description)
843 if not args or (len(args) == 1 and not kw):
844 deprecationMessage = (
845 "Unqualified strport description passed to 'service'."
846 "Use qualified endpoint descriptions; for example, 'tcp:%s'."
851 deprecationMessage, category=DeprecationWarning, stacklevel=4)
852 elif default is _NO_DEFAULT:
853 raise ValueError(deprecationMessage)
854 # If the default has been otherwise specified, the user has already
856 args[0:0] = [default]
857 endpointType = args[0]
858 parser = _serverParsers.get(endpointType)
860 for plugin in getPlugins(IStreamServerEndpointStringParser):
861 if plugin.prefix == endpointType:
862 return (plugin, args[1:], kw)
863 raise ValueError("Unknown endpoint type: '%s'" % (endpointType,))
864 return (endpointType.upper(),) + parser(factory, *args[1:], **kw)
868 def _serverFromStringLegacy(reactor, description, default):
870 Underlying implementation of L{serverFromString} which avoids exposing the
871 deprecated 'default' argument to anything but L{strports.service}.
873 nameOrPlugin, args, kw = _parseServer(description, None, default)
874 if type(nameOrPlugin) is not str:
875 plugin = nameOrPlugin
876 return plugin.parseStreamServer(reactor, *args, **kw)
879 # Chop out the factory.
880 args = args[:1] + args[2:]
881 return _endpointServerFactories[name](reactor, *args, **kw)
885 def serverFromString(reactor, description):
887 Construct a stream server endpoint from an endpoint description string.
889 The format for server endpoint descriptions is a simple string. It is a
890 prefix naming the type of endpoint, then a colon, then the arguments for
893 For example, you can call it like this to create an endpoint that will
894 listen on TCP port 80::
896 serverFromString(reactor, "tcp:80")
898 Additional arguments may be specified as keywords, separated with colons.
899 For example, you can specify the interface for a TCP server endpoint to
902 serverFromString(reactor, "tcp:80:interface=127.0.0.1")
904 SSL server endpoints may be specified with the 'ssl' prefix, and the
905 private key and certificate files may be specified by the C{privateKey} and
906 C{certKey} arguments::
908 serverFromString(reactor, "ssl:443:privateKey=key.pem:certKey=crt.pem")
910 If a private key file name (C{privateKey}) isn't provided, a "server.pem"
911 file is assumed to exist which contains the private key. If the certificate
912 file name (C{certKey}) isn't provided, the private key file is assumed to
913 contain the certificate as well.
915 You may escape colons in arguments with a backslash, which you will need to
916 use if you want to specify a full pathname argument on Windows::
918 serverFromString(reactor,
919 "ssl:443:privateKey=C\\:/key.pem:certKey=C\\:/cert.pem")
921 finally, the 'unix' prefix may be used to specify a filesystem UNIX socket,
922 optionally with a 'mode' argument to specify the mode of the socket file
923 created by C{listen}::
925 serverFromString(reactor, "unix:/var/run/finger")
926 serverFromString(reactor, "unix:/var/run/finger:mode=660")
928 This function is also extensible; new endpoint types may be registered as
929 L{IStreamServerEndpointStringParser} plugins. See that interface for more
932 @param reactor: The server endpoint will be constructed with this reactor.
934 @param description: The strports description to parse.
936 @return: A new endpoint which can be used to listen with the parameters
937 given by by C{description}.
939 @rtype: L{IStreamServerEndpoint<twisted.internet.interfaces.IStreamServerEndpoint>}
941 @raise ValueError: when the 'description' string cannot be parsed.
945 return _serverFromStringLegacy(reactor, description, _NO_DEFAULT)
949 def quoteStringArgument(argument):
951 Quote an argument to L{serverFromString} and L{clientFromString}. Since
952 arguments are separated with colons and colons are escaped with
953 backslashes, some care is necessary if, for example, you have a pathname,
954 you may be tempted to interpolate into a string like this::
956 serverFromString("ssl:443:privateKey=%s" % (myPathName,))
958 This may appear to work, but will have portability issues (Windows
959 pathnames, for example). Usually you should just construct the appropriate
960 endpoint type rather than interpolating strings, which in this case would
961 be L{SSL4ServerEndpoint}. There are some use-cases where you may need to
962 generate such a string, though; for example, a tool to manipulate a
963 configuration file which has strports descriptions in it. To be correct in
964 those cases, do this instead::
966 serverFromString("ssl:443:privateKey=%s" %
967 (quoteStringArgument(myPathName),))
969 @param argument: The part of the endpoint description string you want to
972 @type argument: C{str}
974 @return: The quoted argument.
978 return argument.replace('\\', '\\\\').replace(':', '\\:')
982 def _parseClientTCP(*args, **kwargs):
984 Perform any argument value coercion necessary for TCP client parameters.
986 Valid positional arguments to this function are host and port.
988 Valid keyword arguments to this function are all L{IReactorTCP.connectTCP}
991 @return: The coerced values as a C{dict}.
995 kwargs['port'] = int(args[1])
996 kwargs['host'] = args[0]
999 kwargs['port'] = int(args[0])
1001 kwargs['host'] = args[0]
1004 kwargs['port'] = int(kwargs['port'])
1009 kwargs['timeout'] = int(kwargs['timeout'])
1016 def _loadCAsFromDir(directoryPath):
1018 Load certificate-authority certificate objects in a given directory.
1020 @param directoryPath: a L{FilePath} pointing at a directory to load .pem
1023 @return: a C{list} of L{OpenSSL.crypto.X509} objects.
1025 from twisted.internet import ssl
1028 for child in directoryPath.children():
1029 if not child.basename().split('.')[-1].lower() == 'pem':
1032 data = child.getContent()
1034 # Permission denied, corrupt disk, we don't care.
1037 theCert = ssl.Certificate.loadPEM(data)
1038 except ssl.SSL.Error:
1039 # Duplicate certificate, invalid certificate, etc. We don't care.
1042 caCerts[theCert.digest()] = theCert.original
1043 return caCerts.values()
1047 def _parseClientSSL(*args, **kwargs):
1049 Perform any argument value coercion necessary for SSL client parameters.
1051 Valid keyword arguments to this function are all L{IReactorSSL.connectSSL}
1052 arguments except for C{contextFactory}. Instead, C{certKey} (the path name
1053 of the certificate file) C{privateKey} (the path name of the private key
1054 associated with the certificate) are accepted and used to construct a
1057 Valid positional arguments to this function are host and port.
1059 @param caCertsDir: The one parameter which is not part of
1060 L{IReactorSSL.connectSSL}'s signature, this is a path name used to
1061 construct a list of certificate authority certificates. The directory
1062 will be scanned for files ending in C{.pem}, all of which will be
1063 considered valid certificate authorities for this connection.
1065 @type caCertsDir: C{str}
1067 @return: The coerced values as a C{dict}.
1069 from twisted.internet import ssl
1070 kwargs = _parseClientTCP(*args, **kwargs)
1071 certKey = kwargs.pop('certKey', None)
1072 privateKey = kwargs.pop('privateKey', None)
1073 caCertsDir = kwargs.pop('caCertsDir', None)
1074 if certKey is not None:
1075 certx509 = ssl.Certificate.loadPEM(
1076 FilePath(certKey).getContent()).original
1079 if privateKey is not None:
1080 privateKey = ssl.PrivateCertificate.loadPEM(
1081 FilePath(privateKey).getContent()).privateKey.original
1084 if caCertsDir is not None:
1086 caCerts = _loadCAsFromDir(FilePath(caCertsDir))
1090 kwargs['sslContextFactory'] = ssl.CertificateOptions(
1091 method=ssl.SSL.SSLv23_METHOD,
1092 certificate=certx509,
1093 privateKey=privateKey,
1101 def _parseClientUNIX(*args, **kwargs):
1103 Perform any argument value coercion necessary for UNIX client parameters.
1105 Valid keyword arguments to this function are all L{IReactorUNIX.connectUNIX}
1106 keyword arguments except for C{checkPID}. Instead, C{lockfile} is accepted
1107 and has the same meaning. Also C{path} is used instead of C{address}.
1109 Valid positional arguments to this function are C{path}.
1111 @return: The coerced values as a C{dict}.
1114 kwargs['path'] = args[0]
1117 kwargs['checkPID'] = bool(int(kwargs.pop('lockfile')))
1121 kwargs['timeout'] = int(kwargs['timeout'])
1127 'TCP': _parseClientTCP,
1128 'SSL': _parseClientSSL,
1129 'UNIX': _parseClientUNIX,
1134 def clientFromString(reactor, description):
1136 Construct a client endpoint from a description string.
1138 Client description strings are much like server description strings,
1139 although they take all of their arguments as keywords, aside from host and
1142 You can create a TCP client endpoint with the 'host' and 'port' arguments,
1145 clientFromString(reactor, "tcp:host=www.example.com:port=80")
1147 or, without specifying host and port keywords::
1149 clientFromString(reactor, "tcp:www.example.com:80")
1151 Or you can specify only one or the other, as in the following 2 examples::
1153 clientFromString(reactor, "tcp:host=www.example.com:80")
1154 clientFromString(reactor, "tcp:www.example.com:port=80")
1156 or an SSL client endpoint with those arguments, plus the arguments used by
1157 the server SSL, for a client certificate::
1159 clientFromString(reactor, "ssl:web.example.com:443:"
1160 "privateKey=foo.pem:certKey=foo.pem")
1162 to specify your certificate trust roots, you can identify a directory with
1163 PEM files in it with the C{caCertsDir} argument::
1165 clientFromString(reactor, "ssl:host=web.example.com:port=443:"
1166 "caCertsDir=/etc/ssl/certs")
1168 You can create a UNIX client endpoint with the 'path' argument and optional
1169 'lockfile' and 'timeout' arguments::
1171 clientFromString(reactor, "unix:path=/var/foo/bar:lockfile=1:timeout=9")
1173 or, with the path as a positional argument with or without optional
1174 arguments as in the following 2 examples::
1176 clientFromString(reactor, "unix:/var/foo/bar")
1177 clientFromString(reactor, "unix:/var/foo/bar:lockfile=1:timeout=9")
1179 This function is also extensible; new endpoint types may be registered as
1180 L{IStreamClientEndpointStringParser} plugins. See that interface for more
1183 @param reactor: The client endpoint will be constructed with this reactor.
1185 @param description: The strports description to parse.
1187 @return: A new endpoint which can be used to connect with the parameters
1188 given by by C{description}.
1189 @rtype: L{IStreamClientEndpoint<twisted.internet.interfaces.IStreamClientEndpoint>}
1193 args, kwargs = _parse(description)
1195 name = aname.upper()
1196 for plugin in getPlugins(IStreamClientEndpointStringParser):
1197 if plugin.prefix.upper() == name:
1198 return plugin.parseStreamClient(*args, **kwargs)
1199 if name not in _clientParsers:
1200 raise ValueError("Unknown endpoint type: %r" % (aname,))
1201 kwargs = _clientParsers[name](*args, **kwargs)
1202 return _endpointClientFactories[name](reactor, **kwargs)