1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338 |
- # -*- test-case-name: twisted.internet.test.test_endpoints -*-
- # Copyright (c) Twisted Matrix Laboratories.
- # See LICENSE for details.
- """
- Implementations of L{IStreamServerEndpoint} and L{IStreamClientEndpoint} that
- wrap the L{IReactorTCP}, L{IReactorSSL}, and L{IReactorUNIX} interfaces.
- This also implements an extensible mini-language for describing endpoints,
- parsed by the L{clientFromString} and L{serverFromString} functions.
- @since: 10.1
- """
- import os
- import re
- import socket
- import warnings
- from typing import Optional, Sequence, Type
- from unicodedata import normalize
- from zope.interface import directlyProvides, implementer, provider
- from constantly import NamedConstant, Names
- from incremental import Version
- from twisted.internet import defer, error, fdesc, interfaces, threads
- from twisted.internet.abstract import isIPAddress, isIPv6Address
- from twisted.internet.address import (
- HostnameAddress,
- IPv4Address,
- IPv6Address,
- _ProcessAddress,
- )
- from twisted.internet.interfaces import (
- IAddress,
- IHostnameResolver,
- IHostResolution,
- IReactorPluggableNameResolver,
- IReactorSocket,
- IResolutionReceiver,
- IStreamClientEndpointStringParserWithReactor,
- IStreamServerEndpointStringParser,
- )
- from twisted.internet.protocol import ClientFactory, Factory, ProcessProtocol, Protocol
- try:
- from twisted.internet.stdio import PipeAddress, StandardIO
- except ImportError:
- # fallback if pywin32 is not installed
- StandardIO = None # type: ignore[assignment,misc]
- PipeAddress = None # type: ignore[assignment,misc]
- from twisted.internet._resolver import HostResolution
- from twisted.internet.defer import Deferred
- from twisted.internet.task import LoopingCall
- from twisted.logger import Logger
- from twisted.plugin import IPlugin, getPlugins
- from twisted.python import deprecate, log
- from twisted.python.compat import _matchingString, iterbytes, nativeString
- from twisted.python.components import proxyForInterface
- from twisted.python.failure import Failure
- from twisted.python.filepath import FilePath
- from twisted.python.systemd import ListenFDs
- from ._idna import _idnaBytes, _idnaText
- try:
- from OpenSSL.SSL import Error as SSLError
- from twisted.internet.ssl import (
- Certificate,
- CertificateOptions,
- KeyPair,
- PrivateCertificate,
- optionsForClientTLS,
- trustRootFromCertificates,
- )
- from twisted.protocols.tls import TLSMemoryBIOFactory as _TLSMemoryBIOFactory
- except ImportError:
- TLSMemoryBIOFactory = None
- else:
- TLSMemoryBIOFactory = _TLSMemoryBIOFactory
- __all__ = [
- "clientFromString",
- "serverFromString",
- "TCP4ServerEndpoint",
- "TCP6ServerEndpoint",
- "TCP4ClientEndpoint",
- "TCP6ClientEndpoint",
- "UNIXServerEndpoint",
- "UNIXClientEndpoint",
- "SSL4ServerEndpoint",
- "SSL4ClientEndpoint",
- "AdoptedStreamServerEndpoint",
- "StandardIOEndpoint",
- "ProcessEndpoint",
- "HostnameEndpoint",
- "StandardErrorBehavior",
- "connectProtocol",
- "wrapClientTLS",
- ]
- class _WrappingProtocol(Protocol):
- """
- Wrap another protocol in order to notify my user when a connection has
- been made.
- """
- def __init__(self, connectedDeferred, wrappedProtocol):
- """
- @param connectedDeferred: The L{Deferred} that will callback
- with the C{wrappedProtocol} when it is connected.
- @param wrappedProtocol: An L{IProtocol} provider that will be
- connected.
- """
- self._connectedDeferred = connectedDeferred
- self._wrappedProtocol = wrappedProtocol
- for iface in [
- interfaces.IHalfCloseableProtocol,
- interfaces.IFileDescriptorReceiver,
- interfaces.IHandshakeListener,
- ]:
- if iface.providedBy(self._wrappedProtocol):
- directlyProvides(self, iface)
- def logPrefix(self):
- """
- Transparently pass through the wrapped protocol's log prefix.
- """
- if interfaces.ILoggingContext.providedBy(self._wrappedProtocol):
- return self._wrappedProtocol.logPrefix()
- return self._wrappedProtocol.__class__.__name__
- def connectionMade(self):
- """
- Connect the C{self._wrappedProtocol} to our C{self.transport} and
- callback C{self._connectedDeferred} with the C{self._wrappedProtocol}
- """
- self._wrappedProtocol.makeConnection(self.transport)
- self._connectedDeferred.callback(self._wrappedProtocol)
- def dataReceived(self, data):
- """
- Proxy C{dataReceived} calls to our C{self._wrappedProtocol}
- """
- return self._wrappedProtocol.dataReceived(data)
- def fileDescriptorReceived(self, descriptor):
- """
- Proxy C{fileDescriptorReceived} calls to our C{self._wrappedProtocol}
- """
- return self._wrappedProtocol.fileDescriptorReceived(descriptor)
- def connectionLost(self, reason):
- """
- Proxy C{connectionLost} calls to our C{self._wrappedProtocol}
- """
- return self._wrappedProtocol.connectionLost(reason)
- def readConnectionLost(self):
- """
- Proxy L{IHalfCloseableProtocol.readConnectionLost} to our
- C{self._wrappedProtocol}
- """
- self._wrappedProtocol.readConnectionLost()
- def writeConnectionLost(self):
- """
- Proxy L{IHalfCloseableProtocol.writeConnectionLost} to our
- C{self._wrappedProtocol}
- """
- self._wrappedProtocol.writeConnectionLost()
- def handshakeCompleted(self):
- """
- Proxy L{interfaces.IHandshakeListener} to our
- C{self._wrappedProtocol}.
- """
- self._wrappedProtocol.handshakeCompleted()
- class _WrappingFactory(ClientFactory):
- """
- Wrap a factory in order to wrap the protocols it builds.
- @ivar _wrappedFactory: A provider of I{IProtocolFactory} whose buildProtocol
- method will be called and whose resulting protocol will be wrapped.
- @ivar _onConnection: A L{Deferred} that fires when the protocol is
- connected
- @ivar _connector: A L{connector <twisted.internet.interfaces.IConnector>}
- that is managing the current or previous connection attempt.
- """
- # Type is wrong. See https://twistedmatrix.com/trac/ticket/10005#ticket
- protocol = _WrappingProtocol # type: ignore[assignment]
- def __init__(self, wrappedFactory):
- """
- @param wrappedFactory: A provider of I{IProtocolFactory} whose
- buildProtocol method will be called and whose resulting protocol
- will be wrapped.
- """
- self._wrappedFactory = wrappedFactory
- self._onConnection = defer.Deferred(canceller=self._canceller)
- def startedConnecting(self, connector):
- """
- A connection attempt was started. Remember the connector which started
- said attempt, for use later.
- """
- self._connector = connector
- def _canceller(self, deferred):
- """
- The outgoing connection attempt was cancelled. Fail that L{Deferred}
- with an L{error.ConnectingCancelledError}.
- @param deferred: The L{Deferred <defer.Deferred>} that was cancelled;
- should be the same as C{self._onConnection}.
- @type deferred: L{Deferred <defer.Deferred>}
- @note: This relies on startedConnecting having been called, so it may
- seem as though there's a race condition where C{_connector} may not
- have been set. However, using public APIs, this condition is
- impossible to catch, because a connection API
- (C{connectTCP}/C{SSL}/C{UNIX}) is always invoked before a
- L{_WrappingFactory}'s L{Deferred <defer.Deferred>} is returned to
- C{connect()}'s caller.
- @return: L{None}
- """
- deferred.errback(
- error.ConnectingCancelledError(self._connector.getDestination())
- )
- self._connector.stopConnecting()
- def doStart(self):
- """
- Start notifications are passed straight through to the wrapped factory.
- """
- self._wrappedFactory.doStart()
- def doStop(self):
- """
- Stop notifications are passed straight through to the wrapped factory.
- """
- self._wrappedFactory.doStop()
- def buildProtocol(self, addr):
- """
- Proxy C{buildProtocol} to our C{self._wrappedFactory} or errback the
- C{self._onConnection} L{Deferred} if the wrapped factory raises an
- exception or returns L{None}.
- @return: An instance of L{_WrappingProtocol} or L{None}
- """
- try:
- proto = self._wrappedFactory.buildProtocol(addr)
- if proto is None:
- raise error.NoProtocol()
- except BaseException:
- self._onConnection.errback()
- else:
- return self.protocol(self._onConnection, proto)
- def clientConnectionFailed(self, connector, reason):
- """
- Errback the C{self._onConnection} L{Deferred} when the
- client connection fails.
- """
- if not self._onConnection.called:
- self._onConnection.errback(reason)
- @implementer(interfaces.IStreamServerEndpoint)
- class StandardIOEndpoint:
- """
- A Standard Input/Output endpoint
- @ivar _stdio: a callable, like L{stdio.StandardIO}, which takes an
- L{IProtocol} provider and a C{reactor} keyword argument (interface
- dependent upon your platform).
- """
- _stdio = StandardIO
- def __init__(self, reactor):
- """
- @param reactor: The reactor for the endpoint.
- """
- self._reactor = reactor
- def listen(self, stdioProtocolFactory):
- """
- Implement L{IStreamServerEndpoint.listen} to listen on stdin/stdout
- """
- return defer.execute(
- self._stdio,
- stdioProtocolFactory.buildProtocol(PipeAddress()),
- reactor=self._reactor,
- )
- class _IProcessTransportWithConsumerAndProducer(
- interfaces.IProcessTransport, interfaces.IConsumer, interfaces.IPushProducer
- ):
- """
- An L{_IProcessTransportWithConsumerAndProducer} combines various interfaces
- to work around the issue that L{interfaces.IProcessTransport} is
- incompletely defined and doesn't specify flow-control interfaces, and that
- L{proxyForInterface} doesn't allow for multiple interfaces.
- """
- class _ProcessEndpointTransport(
- proxyForInterface( # type: ignore[misc]
- _IProcessTransportWithConsumerAndProducer,
- "_process",
- )
- ):
- """
- An L{ITransport}, L{IProcessTransport}, L{IConsumer}, and L{IPushProducer}
- provider for the L{IProtocol} instance passed to the process endpoint.
- @ivar _process: An active process transport which will be used by write
- methods on this object to write data to a child process.
- @type _process: L{interfaces.IProcessTransport} provider
- """
- class _WrapIProtocol(ProcessProtocol):
- """
- An L{IProcessProtocol} provider that wraps an L{IProtocol}.
- @ivar transport: A L{_ProcessEndpointTransport} provider that is hooked to
- the wrapped L{IProtocol} provider.
- @see: L{protocol.ProcessProtocol}
- """
- def __init__(self, proto, executable, errFlag):
- """
- @param proto: An L{IProtocol} provider.
- @param errFlag: A constant belonging to L{StandardErrorBehavior}
- that determines if stderr is logged or dropped.
- @param executable: The file name (full path) to spawn.
- """
- self.protocol = proto
- self.errFlag = errFlag
- self.executable = executable
- def makeConnection(self, process):
- """
- Call L{IProtocol} provider's makeConnection method with an
- L{ITransport} provider.
- @param process: An L{IProcessTransport} provider.
- """
- self.transport = _ProcessEndpointTransport(process)
- return self.protocol.makeConnection(self.transport)
- def childDataReceived(self, childFD, data):
- """
- This is called with data from the process's stdout or stderr pipes. It
- checks the status of the errFlag to setermine if stderr should be
- logged (default) or dropped.
- """
- if childFD == 1:
- return self.protocol.dataReceived(data)
- elif childFD == 2 and self.errFlag == StandardErrorBehavior.LOG:
- log.msg(
- format="Process %(executable)r wrote stderr unhandled by "
- "%(protocol)s: %(data)s",
- executable=self.executable,
- protocol=self.protocol,
- data=data,
- )
- def processEnded(self, reason):
- """
- If the process ends with L{error.ProcessDone}, this method calls the
- L{IProtocol} provider's L{connectionLost} with a
- L{error.ConnectionDone}
- @see: L{ProcessProtocol.processEnded}
- """
- if (reason.check(error.ProcessDone) == error.ProcessDone) and (
- reason.value.status == 0
- ):
- return self.protocol.connectionLost(Failure(error.ConnectionDone()))
- else:
- return self.protocol.connectionLost(reason)
- class StandardErrorBehavior(Names):
- """
- Constants used in ProcessEndpoint to decide what to do with stderr.
- @cvar LOG: Indicates that stderr is to be logged.
- @cvar DROP: Indicates that stderr is to be dropped (and not logged).
- @since: 13.1
- """
- LOG = NamedConstant()
- DROP = NamedConstant()
- @implementer(interfaces.IStreamClientEndpoint)
- class ProcessEndpoint:
- """
- An endpoint for child processes
- @ivar _spawnProcess: A hook used for testing the spawning of child process.
- @since: 13.1
- """
- def __init__(
- self,
- reactor,
- executable,
- args=(),
- env={},
- path=None,
- uid=None,
- gid=None,
- usePTY=0,
- childFDs=None,
- errFlag=StandardErrorBehavior.LOG,
- ):
- """
- See L{IReactorProcess.spawnProcess}.
- @param errFlag: Determines if stderr should be logged.
- @type errFlag: L{endpoints.StandardErrorBehavior}
- """
- self._reactor = reactor
- self._executable = executable
- self._args = args
- self._env = env
- self._path = path
- self._uid = uid
- self._gid = gid
- self._usePTY = usePTY
- self._childFDs = childFDs
- self._errFlag = errFlag
- self._spawnProcess = self._reactor.spawnProcess
- def connect(self, protocolFactory):
- """
- Implement L{IStreamClientEndpoint.connect} to launch a child process
- and connect it to a protocol created by C{protocolFactory}.
- @param protocolFactory: A factory for an L{IProtocol} provider which
- will be notified of all events related to the created process.
- """
- proto = protocolFactory.buildProtocol(_ProcessAddress())
- try:
- self._spawnProcess(
- _WrapIProtocol(proto, self._executable, self._errFlag),
- self._executable,
- self._args,
- self._env,
- self._path,
- self._uid,
- self._gid,
- self._usePTY,
- self._childFDs,
- )
- except BaseException:
- return defer.fail()
- else:
- return defer.succeed(proto)
- @implementer(interfaces.IStreamServerEndpoint)
- class _TCPServerEndpoint:
- """
- A TCP server endpoint interface
- """
- def __init__(self, reactor, port, backlog, interface):
- """
- @param reactor: An L{IReactorTCP} provider.
- @param port: The port number used for listening
- @type port: int
- @param backlog: Size of the listen queue
- @type backlog: int
- @param interface: The hostname to bind to
- @type interface: str
- """
- self._reactor = reactor
- self._port = port
- self._backlog = backlog
- self._interface = interface
- def listen(self, protocolFactory):
- """
- Implement L{IStreamServerEndpoint.listen} to listen on a TCP
- socket
- """
- return defer.execute(
- self._reactor.listenTCP,
- self._port,
- protocolFactory,
- backlog=self._backlog,
- interface=self._interface,
- )
- class TCP4ServerEndpoint(_TCPServerEndpoint):
- """
- Implements TCP server endpoint with an IPv4 configuration
- """
- def __init__(self, reactor, port, backlog=50, interface=""):
- """
- @param reactor: An L{IReactorTCP} provider.
- @param port: The port number used for listening
- @type port: int
- @param backlog: Size of the listen queue
- @type backlog: int
- @param interface: The hostname to bind to, defaults to '' (all)
- @type interface: str
- """
- _TCPServerEndpoint.__init__(self, reactor, port, backlog, interface)
- class TCP6ServerEndpoint(_TCPServerEndpoint):
- """
- Implements TCP server endpoint with an IPv6 configuration
- """
- def __init__(self, reactor, port, backlog=50, interface="::"):
- """
- @param reactor: An L{IReactorTCP} provider.
- @param port: The port number used for listening
- @type port: int
- @param backlog: Size of the listen queue
- @type backlog: int
- @param interface: The hostname to bind to, defaults to C{::} (all)
- @type interface: str
- """
- _TCPServerEndpoint.__init__(self, reactor, port, backlog, interface)
- @implementer(interfaces.IStreamClientEndpoint)
- class TCP4ClientEndpoint:
- """
- TCP client endpoint with an IPv4 configuration.
- """
- def __init__(self, reactor, host, port, timeout=30, bindAddress=None):
- """
- @param reactor: An L{IReactorTCP} provider
- @param host: A hostname, used when connecting
- @type host: str
- @param port: The port number, used when connecting
- @type port: int
- @param timeout: The number of seconds to wait before assuming the
- connection has failed.
- @type timeout: L{float} or L{int}
- @param bindAddress: A (host, port) tuple of local address to bind to,
- or None.
- @type bindAddress: tuple
- """
- self._reactor = reactor
- self._host = host
- self._port = port
- self._timeout = timeout
- self._bindAddress = bindAddress
- def connect(self, protocolFactory):
- """
- Implement L{IStreamClientEndpoint.connect} to connect via TCP.
- """
- try:
- wf = _WrappingFactory(protocolFactory)
- self._reactor.connectTCP(
- self._host,
- self._port,
- wf,
- timeout=self._timeout,
- bindAddress=self._bindAddress,
- )
- return wf._onConnection
- except BaseException:
- return defer.fail()
- @implementer(interfaces.IStreamClientEndpoint)
- class TCP6ClientEndpoint:
- """
- TCP client endpoint with an IPv6 configuration.
- @ivar _getaddrinfo: A hook used for testing name resolution.
- @ivar _deferToThread: A hook used for testing deferToThread.
- @ivar _GAI_ADDRESS: Index of the address portion in result of
- getaddrinfo to be used.
- @ivar _GAI_ADDRESS_HOST: Index of the actual host-address in the
- 5-tuple L{_GAI_ADDRESS}.
- """
- _getaddrinfo = staticmethod(socket.getaddrinfo)
- _deferToThread = staticmethod(threads.deferToThread)
- _GAI_ADDRESS = 4
- _GAI_ADDRESS_HOST = 0
- def __init__(self, reactor, host, port, timeout=30, bindAddress=None):
- """
- @param host: An IPv6 address literal or a hostname with an
- IPv6 address
- @see: L{twisted.internet.interfaces.IReactorTCP.connectTCP}
- """
- self._reactor = reactor
- self._host = host
- self._port = port
- self._timeout = timeout
- self._bindAddress = bindAddress
- def connect(self, protocolFactory):
- """
- Implement L{IStreamClientEndpoint.connect} to connect via TCP,
- once the hostname resolution is done.
- """
- if isIPv6Address(self._host):
- d = self._resolvedHostConnect(self._host, protocolFactory)
- else:
- d = self._nameResolution(self._host)
- d.addCallback(
- lambda result: result[0][self._GAI_ADDRESS][self._GAI_ADDRESS_HOST]
- )
- d.addCallback(self._resolvedHostConnect, protocolFactory)
- return d
- def _nameResolution(self, host):
- """
- Resolve the hostname string into a tuple containing the host
- IPv6 address.
- """
- return self._deferToThread(self._getaddrinfo, host, 0, socket.AF_INET6)
- def _resolvedHostConnect(self, resolvedHost, protocolFactory):
- """
- Connect to the server using the resolved hostname.
- """
- try:
- wf = _WrappingFactory(protocolFactory)
- self._reactor.connectTCP(
- resolvedHost,
- self._port,
- wf,
- timeout=self._timeout,
- bindAddress=self._bindAddress,
- )
- return wf._onConnection
- except BaseException:
- return defer.fail()
- @implementer(IHostnameResolver)
- class _SimpleHostnameResolver:
- """
- An L{IHostnameResolver} provider that invokes a provided callable
- to resolve hostnames.
- @ivar _nameResolution: the callable L{resolveHostName} invokes to
- resolve hostnames.
- @type _nameResolution: A L{callable} that accepts two arguments:
- the host to resolve and the port number to include in the
- result.
- """
- _log = Logger()
- def __init__(self, nameResolution):
- """
- Create a L{_SimpleHostnameResolver} instance.
- """
- self._nameResolution = nameResolution
- def resolveHostName(
- self,
- resolutionReceiver: IResolutionReceiver,
- hostName: str,
- portNumber: int = 0,
- addressTypes: Optional[Sequence[Type[IAddress]]] = None,
- transportSemantics: str = "TCP",
- ) -> IHostResolution:
- """
- Initiate a hostname resolution.
- @param resolutionReceiver: an object that will receive each resolved
- address as it arrives.
- @type resolutionReceiver: L{IResolutionReceiver}
- @param hostName: see interface
- @param portNumber: see interface
- @param addressTypes: Ignored in this implementation.
- @param transportSemantics: Ignored in this implementation.
- @return: The resolution in progress.
- @rtype: L{IResolutionReceiver}
- """
- resolution = HostResolution(hostName)
- resolutionReceiver.resolutionBegan(resolution)
- d = self._nameResolution(hostName, portNumber)
- def cbDeliver(gairesult):
- for family, socktype, proto, canonname, sockaddr in gairesult:
- if family == socket.AF_INET6:
- resolutionReceiver.addressResolved(IPv6Address("TCP", *sockaddr))
- elif family == socket.AF_INET:
- resolutionReceiver.addressResolved(IPv4Address("TCP", *sockaddr))
- def ebLog(error):
- self._log.failure(
- "while looking up {name} with {callable}",
- error,
- name=hostName,
- callable=self._nameResolution,
- )
- d.addCallback(cbDeliver)
- d.addErrback(ebLog)
- d.addBoth(lambda ignored: resolutionReceiver.resolutionComplete())
- return resolution
- @implementer(interfaces.IStreamClientEndpoint)
- class HostnameEndpoint:
- """
- A name-based endpoint that connects to the fastest amongst the resolved
- host addresses.
- @cvar _DEFAULT_ATTEMPT_DELAY: The default time to use between attempts, in
- seconds, when no C{attemptDelay} is given to
- L{HostnameEndpoint.__init__}.
- @ivar _hostText: the textual representation of the hostname passed to the
- constructor. Used to pass to the reactor's hostname resolver.
- @type _hostText: L{unicode}
- @ivar _hostBytes: the encoded bytes-representation of the hostname passed
- to the constructor. Used to construct the L{HostnameAddress}
- associated with this endpoint.
- @type _hostBytes: L{bytes}
- @ivar _hostStr: the native-string representation of the hostname passed to
- the constructor, used for exception construction
- @type _hostStr: native L{str}
- @ivar _badHostname: a flag - hopefully false! - indicating that an invalid
- hostname was passed to the constructor. This might be a textual
- hostname that isn't valid IDNA, or non-ASCII bytes.
- @type _badHostname: L{bool}
- """
- _getaddrinfo = staticmethod(socket.getaddrinfo)
- _deferToThread = staticmethod(threads.deferToThread)
- _DEFAULT_ATTEMPT_DELAY = 0.3
- def __init__(
- self, reactor, host, port, timeout=30, bindAddress=None, attemptDelay=None
- ):
- """
- Create a L{HostnameEndpoint}.
- @param reactor: The reactor to use for connections and delayed calls.
- @type reactor: provider of L{IReactorTCP}, L{IReactorTime} and either
- L{IReactorPluggableNameResolver} or L{IReactorPluggableResolver}.
- @param host: A hostname to connect to.
- @type host: L{bytes} or L{unicode}
- @param port: The port number to connect to.
- @type port: L{int}
- @param timeout: For each individual connection attempt, the number of
- seconds to wait before assuming the connection has failed.
- @type timeout: L{float} or L{int}
- @param bindAddress: the local address of the network interface to make
- the connections from.
- @type bindAddress: L{bytes}
- @param attemptDelay: The number of seconds to delay between connection
- attempts.
- @type attemptDelay: L{float}
- @see: L{twisted.internet.interfaces.IReactorTCP.connectTCP}
- """
- self._reactor = reactor
- self._nameResolver = self._getNameResolverAndMaybeWarn(reactor)
- [self._badHostname, self._hostBytes, self._hostText] = self._hostAsBytesAndText(
- host
- )
- self._hostStr = self._hostBytes if bytes is str else self._hostText
- self._port = port
- self._timeout = timeout
- self._bindAddress = bindAddress
- if attemptDelay is None:
- attemptDelay = self._DEFAULT_ATTEMPT_DELAY
- self._attemptDelay = attemptDelay
- def __repr__(self) -> str:
- """
- Produce a string representation of the L{HostnameEndpoint}.
- @return: A L{str}
- """
- if self._badHostname:
- # Use the backslash-encoded version of the string passed to the
- # constructor, which is already a native string.
- host = self._hostStr
- elif isIPv6Address(self._hostStr):
- host = f"[{self._hostStr}]"
- else:
- # Convert the bytes representation to a native string to ensure
- # that we display the punycoded version of the hostname, which is
- # more useful than any IDN version as it can be easily copy-pasted
- # into debugging tools.
- host = nativeString(self._hostBytes)
- return "".join(["<HostnameEndpoint ", host, ":", str(self._port), ">"])
- def _getNameResolverAndMaybeWarn(self, reactor):
- """
- Retrieve a C{nameResolver} callable and warn the caller's
- caller that using a reactor which doesn't provide
- L{IReactorPluggableNameResolver} is deprecated.
- @param reactor: The reactor to check.
- @return: A L{IHostnameResolver} provider.
- """
- if not IReactorPluggableNameResolver.providedBy(reactor):
- warningString = deprecate.getDeprecationWarningString(
- reactor.__class__,
- Version("Twisted", 17, 5, 0),
- format=(
- "Passing HostnameEndpoint a reactor that does not"
- " provide IReactorPluggableNameResolver (%(fqpn)s)"
- " was deprecated in %(version)s"
- ),
- replacement=(
- "a reactor that provides" " IReactorPluggableNameResolver"
- ),
- )
- warnings.warn(warningString, DeprecationWarning, stacklevel=3)
- return _SimpleHostnameResolver(self._fallbackNameResolution)
- return reactor.nameResolver
- @staticmethod
- def _hostAsBytesAndText(host):
- """
- For various reasons (documented in the C{@ivar}'s in the class
- docstring) we need both a textual and a binary representation of the
- hostname given to the constructor. For compatibility and convenience,
- we accept both textual and binary representations of the hostname, save
- the form that was passed, and convert into the other form. This is
- mostly just because L{HostnameAddress} chose somewhat poorly to define
- its attribute as bytes; hopefully we can find a compatible way to clean
- this up in the future and just operate in terms of text internally.
- @param host: A hostname to convert.
- @type host: L{bytes} or C{str}
- @return: a 3-tuple of C{(invalid, bytes, text)} where C{invalid} is a
- boolean indicating the validity of the hostname, C{bytes} is a
- binary representation of C{host}, and C{text} is a textual
- representation of C{host}.
- """
- if isinstance(host, bytes):
- if isIPAddress(host) or isIPv6Address(host):
- return False, host, host.decode("ascii")
- else:
- try:
- return False, host, _idnaText(host)
- except UnicodeError:
- # Convert the host to _some_ kind of text, to handle below.
- host = host.decode("charmap")
- else:
- host = normalize("NFC", host)
- if isIPAddress(host) or isIPv6Address(host):
- return False, host.encode("ascii"), host
- else:
- try:
- return False, _idnaBytes(host), host
- except UnicodeError:
- pass
- # `host` has been converted to text by this point either way; it's
- # invalid as a hostname, and so may contain unprintable characters and
- # such. escape it with backslashes so the user can get _some_ guess as
- # to what went wrong.
- asciibytes = host.encode("ascii", "backslashreplace")
- return True, asciibytes, asciibytes.decode("ascii")
- def connect(self, protocolFactory):
- """
- Attempts a connection to each resolved address, and returns a
- connection which is established first.
- @param protocolFactory: The protocol factory whose protocol
- will be connected.
- @type protocolFactory:
- L{IProtocolFactory<twisted.internet.interfaces.IProtocolFactory>}
- @return: A L{Deferred} that fires with the connected protocol
- or fails a connection-related error.
- """
- if self._badHostname:
- return defer.fail(ValueError(f"invalid hostname: {self._hostStr}"))
- d = Deferred()
- addresses = []
- @provider(IResolutionReceiver)
- class EndpointReceiver:
- @staticmethod
- def resolutionBegan(resolutionInProgress):
- pass
- @staticmethod
- def addressResolved(address):
- addresses.append(address)
- @staticmethod
- def resolutionComplete():
- d.callback(addresses)
- self._nameResolver.resolveHostName(
- EndpointReceiver, self._hostText, portNumber=self._port
- )
- d.addErrback(
- lambda ignored: defer.fail(
- error.DNSLookupError(f"Couldn't find the hostname '{self._hostStr}'")
- )
- )
- @d.addCallback
- def resolvedAddressesToEndpoints(addresses):
- # Yield an endpoint for every address resolved from the name.
- for eachAddress in addresses:
- if isinstance(eachAddress, IPv6Address):
- yield TCP6ClientEndpoint(
- self._reactor,
- eachAddress.host,
- eachAddress.port,
- self._timeout,
- self._bindAddress,
- )
- if isinstance(eachAddress, IPv4Address):
- yield TCP4ClientEndpoint(
- self._reactor,
- eachAddress.host,
- eachAddress.port,
- self._timeout,
- self._bindAddress,
- )
- d.addCallback(list)
- def _canceller(d):
- # This canceller must remain defined outside of
- # `startConnectionAttempts`, because Deferred should not
- # participate in cycles with their cancellers; that would create a
- # potentially problematic circular reference and possibly
- # gc.garbage.
- d.errback(
- error.ConnectingCancelledError(
- HostnameAddress(self._hostBytes, self._port)
- )
- )
- @d.addCallback
- def startConnectionAttempts(endpoints):
- """
- Given a sequence of endpoints obtained via name resolution, start
- connecting to a new one every C{self._attemptDelay} seconds until
- one of the connections succeeds, all of them fail, or the attempt
- is cancelled.
- @param endpoints: a list of all the endpoints we might try to
- connect to, as determined by name resolution.
- @type endpoints: L{list} of L{IStreamServerEndpoint}
- @return: a Deferred that fires with the result of the
- C{endpoint.connect} method that completes the fastest, or fails
- with the first connection error it encountered if none of them
- succeed.
- @rtype: L{Deferred} failing with L{error.ConnectingCancelledError}
- or firing with L{IProtocol}
- """
- if not endpoints:
- raise error.DNSLookupError(
- f"no results for hostname lookup: {self._hostStr}"
- )
- iterEndpoints = iter(endpoints)
- pending = []
- failures = []
- winner = defer.Deferred(canceller=_canceller)
- def checkDone():
- if pending or checkDone.completed or checkDone.endpointsLeft:
- return
- winner.errback(failures.pop())
- checkDone.completed = False
- checkDone.endpointsLeft = True
- @LoopingCall
- def iterateEndpoint():
- endpoint = next(iterEndpoints, None)
- if endpoint is None:
- # The list of endpoints ends.
- checkDone.endpointsLeft = False
- checkDone()
- return
- eachAttempt = endpoint.connect(protocolFactory)
- pending.append(eachAttempt)
- @eachAttempt.addBoth
- def noLongerPending(result):
- pending.remove(eachAttempt)
- return result
- @eachAttempt.addCallback
- def succeeded(result):
- winner.callback(result)
- @eachAttempt.addErrback
- def failed(reason):
- failures.append(reason)
- checkDone()
- iterateEndpoint.clock = self._reactor
- iterateEndpoint.start(self._attemptDelay)
- @winner.addBoth
- def cancelRemainingPending(result):
- checkDone.completed = True
- for remaining in pending[:]:
- remaining.cancel()
- if iterateEndpoint.running:
- iterateEndpoint.stop()
- return result
- return winner
- return d
- def _fallbackNameResolution(self, host, port):
- """
- Resolve the hostname string into a tuple containing the host
- address. This is method is only used when the reactor does
- not provide L{IReactorPluggableNameResolver}.
- @param host: A unicode hostname to resolve.
- @param port: The port to include in the resolution.
- @return: A L{Deferred} that fires with L{_getaddrinfo}'s
- return value.
- """
- return self._deferToThread(self._getaddrinfo, host, port, 0, socket.SOCK_STREAM)
- @implementer(interfaces.IStreamServerEndpoint)
- class SSL4ServerEndpoint:
- """
- SSL secured TCP server endpoint with an IPv4 configuration.
- """
- def __init__(self, reactor, port, sslContextFactory, backlog=50, interface=""):
- """
- @param reactor: An L{IReactorSSL} provider.
- @param port: The port number used for listening
- @type port: int
- @param sslContextFactory: An instance of
- L{interfaces.IOpenSSLContextFactory}.
- @param backlog: Size of the listen queue
- @type backlog: int
- @param interface: The hostname to bind to, defaults to '' (all)
- @type interface: str
- """
- self._reactor = reactor
- self._port = port
- self._sslContextFactory = sslContextFactory
- self._backlog = backlog
- self._interface = interface
- def listen(self, protocolFactory):
- """
- Implement L{IStreamServerEndpoint.listen} to listen for SSL on a
- TCP socket.
- """
- return defer.execute(
- self._reactor.listenSSL,
- self._port,
- protocolFactory,
- contextFactory=self._sslContextFactory,
- backlog=self._backlog,
- interface=self._interface,
- )
- @implementer(interfaces.IStreamClientEndpoint)
- class SSL4ClientEndpoint:
- """
- SSL secured TCP client endpoint with an IPv4 configuration
- """
- def __init__(
- self, reactor, host, port, sslContextFactory, timeout=30, bindAddress=None
- ):
- """
- @param reactor: An L{IReactorSSL} provider.
- @param host: A hostname, used when connecting
- @type host: str
- @param port: The port number, used when connecting
- @type port: int
- @param sslContextFactory: SSL Configuration information as an instance
- of L{interfaces.IOpenSSLContextFactory}.
- @param timeout: Number of seconds to wait before assuming the
- connection has failed.
- @type timeout: int
- @param bindAddress: A (host, port) tuple of local address to bind to,
- or None.
- @type bindAddress: tuple
- """
- self._reactor = reactor
- self._host = host
- self._port = port
- self._sslContextFactory = sslContextFactory
- self._timeout = timeout
- self._bindAddress = bindAddress
- def connect(self, protocolFactory):
- """
- Implement L{IStreamClientEndpoint.connect} to connect with SSL over
- TCP.
- """
- try:
- wf = _WrappingFactory(protocolFactory)
- self._reactor.connectSSL(
- self._host,
- self._port,
- wf,
- self._sslContextFactory,
- timeout=self._timeout,
- bindAddress=self._bindAddress,
- )
- return wf._onConnection
- except BaseException:
- return defer.fail()
- @implementer(interfaces.IStreamServerEndpoint)
- class UNIXServerEndpoint:
- """
- UnixSocket server endpoint.
- """
- def __init__(self, reactor, address, backlog=50, mode=0o666, wantPID=0):
- """
- @param reactor: An L{IReactorUNIX} provider.
- @param address: The path to the Unix socket file, used when listening
- @param backlog: number of connections to allow in backlog.
- @param mode: mode to set on the unix socket. This parameter is
- deprecated. Permissions should be set on the directory which
- contains the UNIX socket.
- @param wantPID: If True, create a pidfile for the socket.
- """
- self._reactor = reactor
- self._address = address
- self._backlog = backlog
- self._mode = mode
- self._wantPID = wantPID
- def listen(self, protocolFactory):
- """
- Implement L{IStreamServerEndpoint.listen} to listen on a UNIX socket.
- """
- return defer.execute(
- self._reactor.listenUNIX,
- self._address,
- protocolFactory,
- backlog=self._backlog,
- mode=self._mode,
- wantPID=self._wantPID,
- )
- @implementer(interfaces.IStreamClientEndpoint)
- class UNIXClientEndpoint:
- """
- UnixSocket client endpoint.
- """
- def __init__(self, reactor, path, timeout=30, checkPID=0):
- """
- @param reactor: An L{IReactorUNIX} provider.
- @param path: The path to the Unix socket file, used when connecting
- @type path: str
- @param timeout: Number of seconds to wait before assuming the
- connection has failed.
- @type timeout: int
- @param checkPID: If True, check for a pid file to verify that a server
- is listening.
- @type checkPID: bool
- """
- self._reactor = reactor
- self._path = path
- self._timeout = timeout
- self._checkPID = checkPID
- def connect(self, protocolFactory):
- """
- Implement L{IStreamClientEndpoint.connect} to connect via a
- UNIX Socket
- """
- try:
- wf = _WrappingFactory(protocolFactory)
- self._reactor.connectUNIX(
- self._path, wf, timeout=self._timeout, checkPID=self._checkPID
- )
- return wf._onConnection
- except BaseException:
- return defer.fail()
- @implementer(interfaces.IStreamServerEndpoint)
- class AdoptedStreamServerEndpoint:
- """
- An endpoint for listening on a file descriptor initialized outside of
- Twisted.
- @ivar _used: A C{bool} indicating whether this endpoint has been used to
- listen with a factory yet. C{True} if so.
- """
- _close = os.close
- _setNonBlocking = staticmethod(fdesc.setNonBlocking)
- def __init__(self, reactor, fileno, addressFamily):
- """
- @param reactor: An L{IReactorSocket} provider.
- @param fileno: An integer file descriptor corresponding to a listening
- I{SOCK_STREAM} socket.
- @param addressFamily: The address family of the socket given by
- C{fileno}.
- """
- self.reactor = reactor
- self.fileno = fileno
- self.addressFamily = addressFamily
- self._used = False
- def listen(self, factory):
- """
- Implement L{IStreamServerEndpoint.listen} to start listening on, and
- then close, C{self._fileno}.
- """
- if self._used:
- return defer.fail(error.AlreadyListened())
- self._used = True
- try:
- self._setNonBlocking(self.fileno)
- port = self.reactor.adoptStreamPort(
- self.fileno, self.addressFamily, factory
- )
- self._close(self.fileno)
- except BaseException:
- return defer.fail()
- return defer.succeed(port)
- def _parseTCP(factory, port, interface="", backlog=50):
- """
- Internal parser function for L{_parseServer} to convert the string
- arguments for a TCP(IPv4) stream endpoint into the structured arguments.
- @param factory: the protocol factory being parsed, or L{None}. (This was a
- leftover argument from when this code was in C{strports}, and is now
- mostly None and unused.)
- @type factory: L{IProtocolFactory} or L{None}
- @param port: the integer port number to bind
- @type port: C{str}
- @param interface: the interface IP to listen on
- @param backlog: the length of the listen queue
- @type backlog: C{str}
- @return: a 2-tuple of (args, kwargs), describing the parameters to
- L{IReactorTCP.listenTCP} (or, modulo argument 2, the factory, arguments
- to L{TCP4ServerEndpoint}.
- """
- return (int(port), factory), {"interface": interface, "backlog": int(backlog)}
- def _parseUNIX(factory, address, mode="666", backlog=50, lockfile=True):
- """
- Internal parser function for L{_parseServer} to convert the string
- arguments for a UNIX (AF_UNIX/SOCK_STREAM) stream endpoint into the
- structured arguments.
- @param factory: the protocol factory being parsed, or L{None}. (This was a
- leftover argument from when this code was in C{strports}, and is now
- mostly None and unused.)
- @type factory: L{IProtocolFactory} or L{None}
- @param address: the pathname of the unix socket
- @type address: C{str}
- @param backlog: the length of the listen queue
- @type backlog: C{str}
- @param lockfile: A string '0' or '1', mapping to True and False
- respectively. See the C{wantPID} argument to C{listenUNIX}
- @return: a 2-tuple of (args, kwargs), describing the parameters to
- L{twisted.internet.interfaces.IReactorUNIX.listenUNIX} (or,
- modulo argument 2, the factory, arguments to L{UNIXServerEndpoint}.
- """
- return (
- (address, factory),
- {"mode": int(mode, 8), "backlog": int(backlog), "wantPID": bool(int(lockfile))},
- )
- def _parseSSL(
- factory,
- port,
- privateKey="server.pem",
- certKey=None,
- sslmethod=None,
- interface="",
- backlog=50,
- extraCertChain=None,
- dhParameters=None,
- ):
- """
- Internal parser function for L{_parseServer} to convert the string
- arguments for an SSL (over TCP/IPv4) stream endpoint into the structured
- arguments.
- @param factory: the protocol factory being parsed, or L{None}. (This was a
- leftover argument from when this code was in C{strports}, and is now
- mostly None and unused.)
- @type factory: L{IProtocolFactory} or L{None}
- @param port: the integer port number to bind
- @type port: C{str}
- @param interface: the interface IP to listen on
- @param backlog: the length of the listen queue
- @type backlog: C{str}
- @param privateKey: The file name of a PEM format private key file.
- @type privateKey: C{str}
- @param certKey: The file name of a PEM format certificate file.
- @type certKey: C{str}
- @param sslmethod: The string name of an SSL method, based on the name of a
- constant in C{OpenSSL.SSL}.
- @type sslmethod: C{str}
- @param extraCertChain: The path of a file containing one or more
- certificates in PEM format that establish the chain from a root CA to
- the CA that signed your C{certKey}.
- @type extraCertChain: L{str}
- @param dhParameters: The file name of a file containing parameters that are
- required for Diffie-Hellman key exchange. If this is not specified,
- the forward secret C{DHE} ciphers aren't available for servers.
- @type dhParameters: L{str}
- @return: a 2-tuple of (args, kwargs), describing the parameters to
- L{IReactorSSL.listenSSL} (or, modulo argument 2, the factory, arguments
- to L{SSL4ServerEndpoint}.
- """
- from twisted.internet import ssl
- if certKey is None:
- certKey = privateKey
- kw = {}
- if sslmethod is not None:
- kw["method"] = getattr(ssl.SSL, sslmethod)
- certPEM = FilePath(certKey).getContent()
- keyPEM = FilePath(privateKey).getContent()
- privateCertificate = ssl.PrivateCertificate.loadPEM(certPEM + b"\n" + keyPEM)
- if extraCertChain is not None:
- matches = re.findall(
- r"(-----BEGIN CERTIFICATE-----\n.+?\n-----END CERTIFICATE-----)",
- nativeString(FilePath(extraCertChain).getContent()),
- flags=re.DOTALL,
- )
- chainCertificates = [
- ssl.Certificate.loadPEM(chainCertPEM).original for chainCertPEM in matches
- ]
- if not chainCertificates:
- raise ValueError(
- "Specified chain file '%s' doesn't contain any valid "
- "certificates in PEM format." % (extraCertChain,)
- )
- else:
- chainCertificates = None
- if dhParameters is not None:
- dhParameters = ssl.DiffieHellmanParameters.fromFile(
- FilePath(dhParameters),
- )
- cf = ssl.CertificateOptions(
- privateKey=privateCertificate.privateKey.original,
- certificate=privateCertificate.original,
- extraCertChain=chainCertificates,
- dhParameters=dhParameters,
- **kw,
- )
- return ((int(port), factory, cf), {"interface": interface, "backlog": int(backlog)})
- @implementer(IPlugin, IStreamServerEndpointStringParser)
- class _StandardIOParser:
- """
- Stream server endpoint string parser for the Standard I/O type.
- @ivar prefix: See L{IStreamServerEndpointStringParser.prefix}.
- """
- prefix = "stdio"
- def _parseServer(self, reactor):
- """
- Internal parser function for L{_parseServer} to convert the string
- arguments into structured arguments for the L{StandardIOEndpoint}
- @param reactor: Reactor for the endpoint
- """
- return StandardIOEndpoint(reactor)
- def parseStreamServer(self, reactor, *args, **kwargs):
- # Redirects to another function (self._parseServer), tricks zope.interface
- # into believing the interface is correctly implemented.
- return self._parseServer(reactor)
- @implementer(IPlugin, IStreamServerEndpointStringParser)
- class _SystemdParser:
- """
- Stream server endpoint string parser for the I{systemd} endpoint type.
- @ivar prefix: See L{IStreamServerEndpointStringParser.prefix}.
- @ivar _sddaemon: A L{ListenFDs} instance used to translate an index into an
- actual file descriptor.
- """
- _sddaemon = ListenFDs.fromEnvironment()
- prefix = "systemd"
- def _parseServer(
- self,
- reactor: IReactorSocket,
- domain: str,
- index: Optional[str] = None,
- name: Optional[str] = None,
- ) -> AdoptedStreamServerEndpoint:
- """
- Internal parser function for L{_parseServer} to convert the string
- arguments for a systemd server endpoint into structured arguments for
- L{AdoptedStreamServerEndpoint}.
- @param reactor: An L{IReactorSocket} provider.
- @param domain: The domain (or address family) of the socket inherited
- from systemd. This is a string like C{"INET"} or C{"UNIX"}, ie
- the name of an address family from the L{socket} module, without
- the C{"AF_"} prefix.
- @param index: If given, the decimal representation of an integer
- giving the offset into the list of file descriptors inherited from
- systemd. Since the order of descriptors received from systemd is
- hard to predict, this option should only be used if only one
- descriptor is being inherited. Even in that case, C{name} is
- probably a better idea. Either C{index} or C{name} must be given.
- @param name: If given, the name (as defined by C{FileDescriptorName}
- in the C{[Socket]} section of a systemd service definition) of an
- inherited file descriptor. Either C{index} or C{name} must be
- given.
- @return: An L{AdoptedStreamServerEndpoint} which will adopt the
- inherited listening port when it is used to listen.
- """
- if (index is None) == (name is None):
- raise ValueError("Specify exactly one of descriptor index or name")
- if index is not None:
- fileno = self._sddaemon.inheritedDescriptors()[int(index)]
- else:
- assert name is not None
- fileno = self._sddaemon.inheritedNamedDescriptors()[name]
- addressFamily = getattr(socket, "AF_" + domain)
- return AdoptedStreamServerEndpoint(reactor, fileno, addressFamily)
- def parseStreamServer(self, reactor, *args, **kwargs):
- # Delegate to another function with a sane signature. This function has
- # an insane signature to trick zope.interface into believing the
- # interface is correctly implemented.
- return self._parseServer(reactor, *args, **kwargs)
- @implementer(IPlugin, IStreamServerEndpointStringParser)
- class _TCP6ServerParser:
- """
- Stream server endpoint string parser for the TCP6ServerEndpoint type.
- @ivar prefix: See L{IStreamServerEndpointStringParser.prefix}.
- """
- prefix = (
- "tcp6" # Used in _parseServer to identify the plugin with the endpoint type
- )
- def _parseServer(self, reactor, port, backlog=50, interface="::"):
- """
- Internal parser function for L{_parseServer} to convert the string
- arguments into structured arguments for the L{TCP6ServerEndpoint}
- @param reactor: An L{IReactorTCP} provider.
- @param port: The port number used for listening
- @type port: int
- @param backlog: Size of the listen queue
- @type backlog: int
- @param interface: The hostname to bind to
- @type interface: str
- """
- port = int(port)
- backlog = int(backlog)
- return TCP6ServerEndpoint(reactor, port, backlog, interface)
- def parseStreamServer(self, reactor, *args, **kwargs):
- # Redirects to another function (self._parseServer), tricks zope.interface
- # into believing the interface is correctly implemented.
- return self._parseServer(reactor, *args, **kwargs)
- _serverParsers = {
- "tcp": _parseTCP,
- "unix": _parseUNIX,
- "ssl": _parseSSL,
- }
- _OP, _STRING = range(2)
- def _tokenize(description):
- """
- Tokenize a strports string and yield each token.
- @param description: a string as described by L{serverFromString} or
- L{clientFromString}.
- @type description: L{str} or L{bytes}
- @return: an iterable of 2-tuples of (C{_OP} or C{_STRING}, string). Tuples
- starting with C{_OP} will contain a second element of either ':' (i.e.
- 'next parameter') or '=' (i.e. 'assign parameter value'). For example,
- the string 'hello:greeting=world' would result in a generator yielding
- these values::
- _STRING, 'hello'
- _OP, ':'
- _STRING, 'greet=ing'
- _OP, '='
- _STRING, 'world'
- """
- empty = _matchingString("", description)
- colon = _matchingString(":", description)
- equals = _matchingString("=", description)
- backslash = _matchingString("\x5c", description)
- current = empty
- ops = colon + equals
- nextOps = {colon: colon + equals, equals: colon}
- iterdesc = iter(iterbytes(description))
- for n in iterdesc:
- if n in iterbytes(ops):
- yield _STRING, current
- yield _OP, n
- current = empty
- ops = nextOps[n]
- elif n == backslash:
- current += next(iterdesc)
- else:
- current += n
- yield _STRING, current
- def _parse(description):
- """
- Convert a description string into a list of positional and keyword
- parameters, using logic vaguely like what Python does.
- @param description: a string as described by L{serverFromString} or
- L{clientFromString}.
- @return: a 2-tuple of C{(args, kwargs)}, where 'args' is a list of all
- ':'-separated C{str}s not containing an '=' and 'kwargs' is a map of
- all C{str}s which do contain an '='. For example, the result of
- C{_parse('a:b:d=1:c')} would be C{(['a', 'b', 'c'], {'d': '1'})}.
- """
- args, kw = [], {}
- colon = _matchingString(":", description)
- def add(sofar):
- if len(sofar) == 1:
- args.append(sofar[0])
- else:
- kw[nativeString(sofar[0])] = sofar[1]
- sofar = ()
- for type, value in _tokenize(description):
- if type is _STRING:
- sofar += (value,)
- elif value == colon:
- add(sofar)
- sofar = ()
- add(sofar)
- return args, kw
- # Mappings from description "names" to endpoint constructors.
- _endpointServerFactories = {
- "TCP": TCP4ServerEndpoint,
- "SSL": SSL4ServerEndpoint,
- "UNIX": UNIXServerEndpoint,
- }
- _endpointClientFactories = {
- "TCP": TCP4ClientEndpoint,
- "SSL": SSL4ClientEndpoint,
- "UNIX": UNIXClientEndpoint,
- }
- def _parseServer(description, factory):
- """
- Parse a strports description into a 2-tuple of arguments and keyword
- values.
- @param description: A description in the format explained by
- L{serverFromString}.
- @type description: C{str}
- @param factory: A 'factory' argument; this is left-over from
- twisted.application.strports, it's not really used.
- @type factory: L{IProtocolFactory} or L{None}
- @return: a 3-tuple of (plugin or name, arguments, keyword arguments)
- """
- args, kw = _parse(description)
- endpointType = args[0]
- parser = _serverParsers.get(endpointType)
- if parser is None:
- # If the required parser is not found in _server, check if
- # a plugin exists for the endpointType
- plugin = _matchPluginToPrefix(
- getPlugins(IStreamServerEndpointStringParser), endpointType
- )
- return (plugin, args[1:], kw)
- return (endpointType.upper(),) + parser(factory, *args[1:], **kw)
- def _matchPluginToPrefix(plugins, endpointType):
- """
- Match plugin to prefix.
- """
- endpointType = endpointType.lower()
- for plugin in plugins:
- if _matchingString(plugin.prefix.lower(), endpointType) == endpointType:
- return plugin
- raise ValueError(f"Unknown endpoint type: '{endpointType}'")
- def serverFromString(reactor, description):
- """
- Construct a stream server endpoint from an endpoint description string.
- The format for server endpoint descriptions is a simple byte string. It is
- a prefix naming the type of endpoint, then a colon, then the arguments for
- that endpoint.
- For example, you can call it like this to create an endpoint that will
- listen on TCP port 80::
- serverFromString(reactor, "tcp:80")
- Additional arguments may be specified as keywords, separated with colons.
- For example, you can specify the interface for a TCP server endpoint to
- bind to like this::
- serverFromString(reactor, "tcp:80:interface=127.0.0.1")
- SSL server endpoints may be specified with the 'ssl' prefix, and the
- private key and certificate files may be specified by the C{privateKey} and
- C{certKey} arguments::
- serverFromString(
- reactor, "ssl:443:privateKey=key.pem:certKey=crt.pem")
- If a private key file name (C{privateKey}) isn't provided, a "server.pem"
- file is assumed to exist which contains the private key. If the certificate
- file name (C{certKey}) isn't provided, the private key file is assumed to
- contain the certificate as well.
- You may escape colons in arguments with a backslash, which you will need to
- use if you want to specify a full pathname argument on Windows::
- serverFromString(reactor,
- "ssl:443:privateKey=C\\:/key.pem:certKey=C\\:/cert.pem")
- finally, the 'unix' prefix may be used to specify a filesystem UNIX socket,
- optionally with a 'mode' argument to specify the mode of the socket file
- created by C{listen}::
- serverFromString(reactor, "unix:/var/run/finger")
- serverFromString(reactor, "unix:/var/run/finger:mode=660")
- This function is also extensible; new endpoint types may be registered as
- L{IStreamServerEndpointStringParser} plugins. See that interface for more
- information.
- @param reactor: The server endpoint will be constructed with this reactor.
- @param description: The strports description to parse.
- @type description: L{str}
- @return: A new endpoint which can be used to listen with the parameters
- given by C{description}.
- @rtype: L{IStreamServerEndpoint<twisted.internet.interfaces.IStreamServerEndpoint>}
- @raise ValueError: when the 'description' string cannot be parsed.
- @since: 10.2
- """
- nameOrPlugin, args, kw = _parseServer(description, None)
- if type(nameOrPlugin) is not str:
- plugin = nameOrPlugin
- return plugin.parseStreamServer(reactor, *args, **kw)
- else:
- name = nameOrPlugin
- # Chop out the factory.
- args = args[:1] + args[2:]
- return _endpointServerFactories[name](reactor, *args, **kw)
- def quoteStringArgument(argument):
- """
- Quote an argument to L{serverFromString} and L{clientFromString}. Since
- arguments are separated with colons and colons are escaped with
- backslashes, some care is necessary if, for example, you have a pathname,
- you may be tempted to interpolate into a string like this::
- serverFromString(reactor, "ssl:443:privateKey=%s" % (myPathName,))
- This may appear to work, but will have portability issues (Windows
- pathnames, for example). Usually you should just construct the appropriate
- endpoint type rather than interpolating strings, which in this case would
- be L{SSL4ServerEndpoint}. There are some use-cases where you may need to
- generate such a string, though; for example, a tool to manipulate a
- configuration file which has strports descriptions in it. To be correct in
- those cases, do this instead::
- serverFromString(reactor, "ssl:443:privateKey=%s" %
- (quoteStringArgument(myPathName),))
- @param argument: The part of the endpoint description string you want to
- pass through.
- @type argument: C{str}
- @return: The quoted argument.
- @rtype: C{str}
- """
- backslash, colon = "\\:"
- for c in backslash, colon:
- argument = argument.replace(c, backslash + c)
- return argument
- def _parseClientTCP(*args, **kwargs):
- """
- Perform any argument value coercion necessary for TCP client parameters.
- Valid positional arguments to this function are host and port.
- Valid keyword arguments to this function are all L{IReactorTCP.connectTCP}
- arguments.
- @return: The coerced values as a C{dict}.
- """
- if len(args) == 2:
- kwargs["port"] = int(args[1])
- kwargs["host"] = args[0]
- elif len(args) == 1:
- if "host" in kwargs:
- kwargs["port"] = int(args[0])
- else:
- kwargs["host"] = args[0]
- try:
- kwargs["port"] = int(kwargs["port"])
- except KeyError:
- pass
- try:
- kwargs["timeout"] = int(kwargs["timeout"])
- except KeyError:
- pass
- try:
- kwargs["bindAddress"] = (kwargs["bindAddress"], 0)
- except KeyError:
- pass
- return kwargs
- def _loadCAsFromDir(directoryPath):
- """
- Load certificate-authority certificate objects in a given directory.
- @param directoryPath: a L{unicode} or L{bytes} pointing at a directory to
- load .pem files from, or L{None}.
- @return: an L{IOpenSSLTrustRoot} provider.
- """
- caCerts = {}
- for child in directoryPath.children():
- if not child.asTextMode().basename().split(".")[-1].lower() == "pem":
- continue
- try:
- data = child.getContent()
- except OSError:
- # Permission denied, corrupt disk, we don't care.
- continue
- try:
- theCert = Certificate.loadPEM(data)
- except SSLError:
- # Duplicate certificate, invalid certificate, etc. We don't care.
- pass
- else:
- caCerts[theCert.digest()] = theCert
- return trustRootFromCertificates(caCerts.values())
- def _parseTrustRootPath(pathName):
- """
- Parse a string referring to a directory full of certificate authorities
- into a trust root.
- @param pathName: path name
- @type pathName: L{unicode} or L{bytes} or L{None}
- @return: L{None} or L{IOpenSSLTrustRoot}
- """
- if pathName is None:
- return None
- return _loadCAsFromDir(FilePath(pathName))
- def _privateCertFromPaths(certificatePath, keyPath):
- """
- Parse a certificate path and key path, either or both of which might be
- L{None}, into a certificate object.
- @param certificatePath: the certificate path
- @type certificatePath: L{bytes} or L{unicode} or L{None}
- @param keyPath: the private key path
- @type keyPath: L{bytes} or L{unicode} or L{None}
- @return: a L{PrivateCertificate} or L{None}
- """
- if certificatePath is None:
- return None
- certBytes = FilePath(certificatePath).getContent()
- if keyPath is None:
- return PrivateCertificate.loadPEM(certBytes)
- else:
- return PrivateCertificate.fromCertificateAndKeyPair(
- Certificate.loadPEM(certBytes),
- KeyPair.load(FilePath(keyPath).getContent(), 1),
- )
- def _parseClientSSLOptions(kwargs):
- """
- Parse common arguments for SSL endpoints, creating an L{CertificateOptions}
- instance.
- @param kwargs: A dict of keyword arguments to be parsed, potentially
- containing keys C{certKey}, C{privateKey}, C{caCertsDir}, and
- C{hostname}. See L{_parseClientSSL}.
- @type kwargs: L{dict}
- @return: The remaining arguments, including a new key C{sslContextFactory}.
- """
- hostname = kwargs.pop("hostname", None)
- clientCertificate = _privateCertFromPaths(
- kwargs.pop("certKey", None), kwargs.pop("privateKey", None)
- )
- trustRoot = _parseTrustRootPath(kwargs.pop("caCertsDir", None))
- if hostname is not None:
- configuration = optionsForClientTLS(
- _idnaText(hostname),
- trustRoot=trustRoot,
- clientCertificate=clientCertificate,
- )
- else:
- # _really_ though, you should specify a hostname.
- if clientCertificate is not None:
- privateKeyOpenSSL = clientCertificate.privateKey.original
- certificateOpenSSL = clientCertificate.original
- else:
- privateKeyOpenSSL = None
- certificateOpenSSL = None
- configuration = CertificateOptions(
- trustRoot=trustRoot,
- privateKey=privateKeyOpenSSL,
- certificate=certificateOpenSSL,
- )
- kwargs["sslContextFactory"] = configuration
- return kwargs
- def _parseClientSSL(*args, **kwargs):
- """
- Perform any argument value coercion necessary for SSL client parameters.
- Valid keyword arguments to this function are all L{IReactorSSL.connectSSL}
- arguments except for C{contextFactory}. Instead, C{certKey} (the path name
- of the certificate file) C{privateKey} (the path name of the private key
- associated with the certificate) are accepted and used to construct a
- context factory.
- Valid positional arguments to this function are host and port.
- @keyword caCertsDir: The one parameter which is not part of
- L{IReactorSSL.connectSSL}'s signature, this is a path name used to
- construct a list of certificate authority certificates. The directory
- will be scanned for files ending in C{.pem}, all of which will be
- considered valid certificate authorities for this connection.
- @type caCertsDir: L{str}
- @keyword hostname: The hostname to use for validating the server's
- certificate.
- @type hostname: L{unicode}
- @return: The coerced values as a L{dict}.
- """
- kwargs = _parseClientTCP(*args, **kwargs)
- return _parseClientSSLOptions(kwargs)
- def _parseClientUNIX(*args, **kwargs):
- """
- Perform any argument value coercion necessary for UNIX client parameters.
- Valid keyword arguments to this function are all L{IReactorUNIX.connectUNIX}
- keyword arguments except for C{checkPID}. Instead, C{lockfile} is accepted
- and has the same meaning. Also C{path} is used instead of C{address}.
- Valid positional arguments to this function are C{path}.
- @return: The coerced values as a C{dict}.
- """
- if len(args) == 1:
- kwargs["path"] = args[0]
- try:
- kwargs["checkPID"] = bool(int(kwargs.pop("lockfile")))
- except KeyError:
- pass
- try:
- kwargs["timeout"] = int(kwargs["timeout"])
- except KeyError:
- pass
- return kwargs
- _clientParsers = {
- "TCP": _parseClientTCP,
- "SSL": _parseClientSSL,
- "UNIX": _parseClientUNIX,
- }
- def clientFromString(reactor, description):
- """
- Construct a client endpoint from a description string.
- Client description strings are much like server description strings,
- although they take all of their arguments as keywords, aside from host and
- port.
- You can create a TCP client endpoint with the 'host' and 'port' arguments,
- like so::
- clientFromString(reactor, "tcp:host=www.example.com:port=80")
- or, without specifying host and port keywords::
- clientFromString(reactor, "tcp:www.example.com:80")
- Or you can specify only one or the other, as in the following 2 examples::
- clientFromString(reactor, "tcp:host=www.example.com:80")
- clientFromString(reactor, "tcp:www.example.com:port=80")
- or an SSL client endpoint with those arguments, plus the arguments used by
- the server SSL, for a client certificate::
- clientFromString(reactor, "ssl:web.example.com:443:"
- "privateKey=foo.pem:certKey=foo.pem")
- to specify your certificate trust roots, you can identify a directory with
- PEM files in it with the C{caCertsDir} argument::
- clientFromString(reactor, "ssl:host=web.example.com:port=443:"
- "caCertsDir=/etc/ssl/certs")
- Both TCP and SSL client endpoint description strings can include a
- 'bindAddress' keyword argument, whose value should be a local IPv4
- address. This fixes the client socket to that IP address::
- clientFromString(reactor, "tcp:www.example.com:80:"
- "bindAddress=192.0.2.100")
- NB: Fixed client ports are not currently supported in TCP or SSL
- client endpoints. The client socket will always use an ephemeral
- port assigned by the operating system
- You can create a UNIX client endpoint with the 'path' argument and optional
- 'lockfile' and 'timeout' arguments::
- clientFromString(
- reactor, b"unix:path=/var/foo/bar:lockfile=1:timeout=9")
- or, with the path as a positional argument with or without optional
- arguments as in the following 2 examples::
- clientFromString(reactor, "unix:/var/foo/bar")
- clientFromString(reactor, "unix:/var/foo/bar:lockfile=1:timeout=9")
- This function is also extensible; new endpoint types may be registered as
- L{IStreamClientEndpointStringParserWithReactor} plugins. See that
- interface for more information.
- @param reactor: The client endpoint will be constructed with this reactor.
- @param description: The strports description to parse.
- @type description: L{str}
- @return: A new endpoint which can be used to connect with the parameters
- given by C{description}.
- @rtype: L{IStreamClientEndpoint<twisted.internet.interfaces.IStreamClientEndpoint>}
- @since: 10.2
- """
- args, kwargs = _parse(description)
- aname = args.pop(0)
- name = aname.upper()
- if name not in _clientParsers:
- plugin = _matchPluginToPrefix(
- getPlugins(IStreamClientEndpointStringParserWithReactor), name
- )
- return plugin.parseStreamClient(reactor, *args, **kwargs)
- kwargs = _clientParsers[name](*args, **kwargs)
- return _endpointClientFactories[name](reactor, **kwargs)
- def connectProtocol(endpoint, protocol):
- """
- Connect a protocol instance to an endpoint.
- This allows using a client endpoint without having to create a factory.
- @param endpoint: A client endpoint to connect to.
- @param protocol: A protocol instance.
- @return: The result of calling C{connect} on the endpoint, i.e. a
- L{Deferred} that will fire with the protocol when connected, or an
- appropriate error.
- @since: 13.1
- """
- class OneShotFactory(Factory):
- def buildProtocol(self, addr):
- return protocol
- return endpoint.connect(OneShotFactory())
- @implementer(interfaces.IStreamClientEndpoint)
- class _WrapperEndpoint:
- """
- An endpoint that wraps another endpoint.
- """
- def __init__(self, wrappedEndpoint, wrapperFactory):
- """
- Construct a L{_WrapperEndpoint}.
- """
- self._wrappedEndpoint = wrappedEndpoint
- self._wrapperFactory = wrapperFactory
- def connect(self, protocolFactory):
- """
- Connect the given protocol factory and unwrap its result.
- """
- return self._wrappedEndpoint.connect(
- self._wrapperFactory(protocolFactory)
- ).addCallback(lambda protocol: protocol.wrappedProtocol)
- @implementer(interfaces.IStreamServerEndpoint)
- class _WrapperServerEndpoint:
- """
- A server endpoint that wraps another server endpoint.
- """
- def __init__(self, wrappedEndpoint, wrapperFactory):
- """
- Construct a L{_WrapperServerEndpoint}.
- """
- self._wrappedEndpoint = wrappedEndpoint
- self._wrapperFactory = wrapperFactory
- def listen(self, protocolFactory):
- """
- Connect the given protocol factory and unwrap its result.
- """
- return self._wrappedEndpoint.listen(self._wrapperFactory(protocolFactory))
- def wrapClientTLS(connectionCreator, wrappedEndpoint):
- """
- Wrap an endpoint which upgrades to TLS as soon as the connection is
- established.
- @since: 16.0
- @param connectionCreator: The TLS options to use when connecting; see
- L{twisted.internet.ssl.optionsForClientTLS} for how to construct this.
- @type connectionCreator:
- L{twisted.internet.interfaces.IOpenSSLClientConnectionCreator}
- @param wrappedEndpoint: The endpoint to wrap.
- @type wrappedEndpoint: An L{IStreamClientEndpoint} provider.
- @return: an endpoint that provides transport level encryption layered on
- top of C{wrappedEndpoint}
- @rtype: L{twisted.internet.interfaces.IStreamClientEndpoint}
- """
- if TLSMemoryBIOFactory is None:
- raise NotImplementedError(
- "OpenSSL not available. Try `pip install twisted[tls]`."
- )
- return _WrapperEndpoint(
- wrappedEndpoint,
- lambda protocolFactory: TLSMemoryBIOFactory(
- connectionCreator, True, protocolFactory
- ),
- )
- def _parseClientTLS(
- reactor,
- host,
- port,
- timeout=b"30",
- bindAddress=None,
- certificate=None,
- privateKey=None,
- trustRoots=None,
- endpoint=None,
- **kwargs,
- ):
- """
- Internal method to construct an endpoint from string parameters.
- @param reactor: The reactor passed to L{clientFromString}.
- @param host: The hostname to connect to.
- @type host: L{bytes} or L{unicode}
- @param port: The port to connect to.
- @type port: L{bytes} or L{unicode}
- @param timeout: For each individual connection attempt, the number of
- seconds to wait before assuming the connection has failed.
- @type timeout: L{bytes} or L{unicode}
- @param bindAddress: The address to which to bind outgoing connections.
- @type bindAddress: L{bytes} or L{unicode}
- @param certificate: a string representing a filesystem path to a
- PEM-encoded certificate.
- @type certificate: L{bytes} or L{unicode}
- @param privateKey: a string representing a filesystem path to a PEM-encoded
- certificate.
- @type privateKey: L{bytes} or L{unicode}
- @param endpoint: an optional string endpoint description of an endpoint to
- wrap; if this is passed then C{host} is used only for certificate
- verification.
- @type endpoint: L{bytes} or L{unicode}
- @return: a client TLS endpoint
- @rtype: L{IStreamClientEndpoint}
- """
- if kwargs:
- raise TypeError("unrecognized keyword arguments present", list(kwargs.keys()))
- host = host if isinstance(host, str) else host.decode("utf-8")
- bindAddress = (
- bindAddress
- if isinstance(bindAddress, str) or bindAddress is None
- else bindAddress.decode("utf-8")
- )
- port = int(port)
- timeout = int(timeout)
- return wrapClientTLS(
- optionsForClientTLS(
- host,
- trustRoot=_parseTrustRootPath(trustRoots),
- clientCertificate=_privateCertFromPaths(certificate, privateKey),
- ),
- clientFromString(reactor, endpoint)
- if endpoint is not None
- else HostnameEndpoint(reactor, _idnaBytes(host), port, timeout, bindAddress),
- )
- @implementer(IPlugin, IStreamClientEndpointStringParserWithReactor)
- class _TLSClientEndpointParser:
- """
- Stream client endpoint string parser for L{wrapClientTLS} with
- L{HostnameEndpoint}.
- @ivar prefix: See
- L{IStreamClientEndpointStringParserWithReactor.prefix}.
- """
- prefix = "tls"
- @staticmethod
- def parseStreamClient(reactor, *args, **kwargs):
- """
- Redirects to another function L{_parseClientTLS}; tricks zope.interface
- into believing the interface is correctly implemented, since the
- signature is (C{reactor}, C{*args}, C{**kwargs}). See
- L{_parseClientTLS} for the specific signature description for this
- endpoint parser.
- @param reactor: The reactor passed to L{clientFromString}.
- @param args: The positional arguments in the endpoint description.
- @type args: L{tuple}
- @param kwargs: The named arguments in the endpoint description.
- @type kwargs: L{dict}
- @return: a client TLS endpoint
- @rtype: L{IStreamClientEndpoint}
- """
- return _parseClientTLS(reactor, *args, **kwargs)
|