Source code for autobahn.twisted.websocket

###############################################################################
#
# The MIT License (MIT)
#
# Copyright (c) typedef int GmbH
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
###############################################################################

from base64 import b64decode, b64encode
from typing import Optional

import txaio
from zope.interface import implementer

txaio.use_twisted()

import twisted.internet.protocol
from autobahn.twisted.util import create_transport_details, transport_channel_id
from autobahn.util import _is_tls_error, _maybe_tls_reason, hltype, hlval, public
from autobahn.wamp import websocket
from autobahn.wamp.types import TransportDetails
from autobahn.websocket import protocol
from autobahn.websocket.compress import (
    PerMessageDeflateOffer,
    PerMessageDeflateOfferAccept,
    PerMessageDeflateResponse,
    PerMessageDeflateResponseAccept,
)
from autobahn.websocket.interfaces import IWebSocketClientAgent
from autobahn.websocket.types import (
    ConnectionDeny,
    ConnectionRequest,
    ConnectionResponse,
)
from twisted.internet import endpoints
from twisted.internet.defer import Deferred
from twisted.internet.error import ConnectionAborted, ConnectionDone, ConnectionLost
from twisted.internet.interfaces import ITransport
from twisted.internet.protocol import connectionDone
from twisted.python.failure import Failure

__all__ = (
    "WampWebSocketClientFactory",
    "WampWebSocketClientProtocol",
    "WampWebSocketServerFactory",
    "WampWebSocketServerProtocol",
    "WebSocketAdapterFactory",
    "WebSocketAdapterProtocol",
    "WebSocketClientFactory",
    "WebSocketClientProtocol",
    "WebSocketServerFactory",
    "WebSocketServerProtocol",
    "WrappingWebSocketAdapter",
    "WrappingWebSocketClientFactory",
    "WrappingWebSocketClientProtocol",
    "WrappingWebSocketServerFactory",
    "WrappingWebSocketServerProtocol",
    "connectWS",
    "create_client_agent",
    "listenWS",
)


[docs] def create_client_agent(reactor): """ :returns: an instance implementing IWebSocketClientAgent """ return _TwistedWebSocketClientAgent(reactor)
def check_transport_config(transport_config): """ raises a ValueError if `transport_config` is invalid """ # XXX move me to "autobahn.websocket.util" if not isinstance(transport_config, str): raise ValueError( "'transport_config' must be a string, found {}".format( type(transport_config) ) ) # XXX also accept everything Crossbar has in client transport configs? e.g like: # { "type": "websocket", "endpoint": {"type": "tcp", "host": "example.com", ...}} # XXX what about TLS options? (the above point would address that too) if not transport_config.startswith("ws://") and not transport_config.startswith( "wss://" ): raise ValueError("'transport_config' must start with 'ws://' or 'wss://'") return None def check_client_options(options): """ raises a ValueError if `options` is invalid """ # XXX move me to "autobahn.websocket.util" if not isinstance(options, dict): raise ValueError("'options' must be a dict") # anything that WebSocketClientFactory accepts (at least) valid_keys = [ "origin", "protocols", "useragent", "headers", "proxy", ] for actual_k in options.keys(): if actual_k not in valid_keys: raise ValueError("'options' may not contain '{}'".format(actual_k)) def _endpoint_from_config(reactor, factory, transport_config, options): # XXX might want some Crossbar code here? e.g. if we allow # "transport_config" to be a dict etc. # ... passing in the Factory is weird, but that's what parses all # the options and the URL currently if factory.isSecure: # create default client SSL context factory when none given from twisted.internet import ssl context_factory = ssl.optionsForClientTLS(factory.host) if factory.proxy is not None: factory.contextFactory = context_factory endpoint = endpoints.HostnameEndpoint( reactor, factory.proxy["host"], factory.proxy["port"], # timeout, option? ) else: if factory.isSecure: from twisted.internet import ssl endpoint = endpoints.SSL4ClientEndpoint( reactor, factory.host, factory.port, context_factory, # timeout, option? ) else: endpoint = endpoints.HostnameEndpoint( # XXX right? not TCP4ClientEndpoint reactor, factory.host, factory.port, # timeout, option? # attemptDelay, option? ) return endpoint class _TwistedWebSocketClientAgent(IWebSocketClientAgent): """ This agent creates connections using Twisted """ def __init__(self, reactor): self._reactor = reactor def open(self, transport_config, options, protocol_class=None): """ Open a new connection. :param dict transport_config: valid transport configuration :param dict options: additional options for the factory :param protocol_class: a callable that returns an instance of the protocol (WebSocketClientProtocol if the default None is passed in) :returns: a Deferred that fires with an instance of `protocol_class` (or WebSocketClientProtocol by default) that has successfully shaken hands (completed the handshake). """ check_transport_config(transport_config) check_client_options(options) factory = WebSocketClientFactory( url=transport_config, reactor=self._reactor, **options ) factory.protocol = ( WebSocketClientProtocol if protocol_class is None else protocol_class ) # XXX might want "contextFactory" for TLS ...? (or e.g. CA etc options?) endpoint = _endpoint_from_config( self._reactor, factory, transport_config, options ) rtn_d = Deferred() proto_d = endpoint.connect(factory) def failed(f): rtn_d.errback(f) def got_proto(proto): def handshake_completed(arg): rtn_d.callback(proto) return arg proto.is_open.addCallbacks(handshake_completed, failed) return proto proto_d.addCallbacks(got_proto, failed) return rtn_d
[docs] class WebSocketAdapterProtocol(twisted.internet.protocol.Protocol): """ Adapter class for Twisted WebSocket client and server protocols. Called from Twisted: * :meth:`autobahn.twisted.websocket.WebSocketAdapterProtocol.connectionMade` * :meth:`autobahn.twisted.websocket.WebSocketAdapterProtocol.connectionLost` * :meth:`autobahn.twisted.websocket.WebSocketAdapterProtocol.dataReceived` Called from Network-independent Code (WebSocket implementation): * :meth:`autobahn.twisted.websocket.WebSocketAdapterProtocol._onOpen` * :meth:`autobahn.twisted.websocket.WebSocketAdapterProtocol._onMessageBegin` * :meth:`autobahn.twisted.websocket.WebSocketAdapterProtocol._onMessageFrameData` * :meth:`autobahn.twisted.websocket.WebSocketAdapterProtocol._onMessageFrameEnd` * :meth:`autobahn.twisted.websocket.WebSocketAdapterProtocol._onMessageEnd` * :meth:`autobahn.twisted.websocket.WebSocketAdapterProtocol._onMessage` * :meth:`autobahn.twisted.websocket.WebSocketAdapterProtocol._onPing` * :meth:`autobahn.twisted.websocket.WebSocketAdapterProtocol._onPong` * :meth:`autobahn.twisted.websocket.WebSocketAdapterProtocol._onClose` FIXME: * :meth:`autobahn.twisted.websocket.WebSocketAdapterProtocol._closeConnection` * :meth:`autobahn.twisted.websocket.WebSocketAdapterProtocol._create_transport_details` * :meth:`autobahn.twisted.websocket.WebSocketAdapterProtocol.registerProducer` * :meth:`autobahn.twisted.websocket.WebSocketAdapterProtocol.unregisterProducer` """
[docs] log = txaio.make_logger()
[docs] peer: Optional[str] = None
[docs] is_server: Optional[bool] = None
[docs] def connectionMade(self): # Twisted networking framework entry point, called by Twisted # when the connection is established (either a client or a server) # determine preliminary transport details (what is know at this point) self._transport_details = create_transport_details( self.transport, self.is_server ) self._transport_details.channel_framing = ( TransportDetails.CHANNEL_FRAMING_WEBSOCKET ) # backward compatibility self.peer = self._transport_details.peer # try to set "Nagle" option for TCP sockets try: self.transport.setTcpNoDelay(self.tcpNoDelay) except: # don't touch this! does not work: AttributeError, OSError # eg Unix Domain sockets throw Errno 22 on this pass # ok, now forward to the networking framework independent code for websocket self._connectionMade() # ok, done! self.log.debug( '{func} connection established for peer="{peer}"', func=hltype(self.connectionMade), peer=hlval(self.peer), )
[docs] def connectionLost(self, reason: Failure = connectionDone): # Twisted networking framework entry point, called by Twisted # when the connection is lost (either a client or a server) was_clean = False if isinstance(reason.value, ConnectionDone): self.log.debug( "Connection to/from {peer} was closed cleanly", peer=self.peer ) was_clean = True elif _is_tls_error(reason.value): self.log.error(_maybe_tls_reason(reason.value)) elif isinstance(reason.value, ConnectionAborted): self.log.debug( "Connection to/from {peer} was aborted locally", peer=self.peer ) elif isinstance(reason.value, ConnectionLost): message = str(reason.value) if hasattr(reason.value, "message"): message = reason.value.message self.log.debug( "Connection to/from {peer} was lost in a non-clean fashion: {message}", peer=self.peer, message=message, ) # at least: FileDescriptorOverrun, ConnectionFdescWentAway - but maybe others as well? else: self.log.debug( "Connection to/from {peer} lost ({error_type}): {error})", peer=self.peer, error_type=type(reason.value), error=reason.value, ) # ok, now forward to the networking framework independent code for websocket self._connectionLost(reason) # ok, done! if was_clean: self.log.debug( '{func} connection lost for peer="{peer}", closed cleanly', func=hltype(self.connectionLost), peer=hlval(self.peer), ) else: self.log.debug( '{func} connection lost for peer="{peer}", closed with error {reason}', func=hltype(self.connectionLost), peer=hlval(self.peer), reason=reason, )
[docs] def dataReceived(self, data: bytes): self.log.debug( '{func} received {data_len} bytes for peer="{peer}"', func=hltype(self.dataReceived), peer=hlval(self.peer), data_len=hlval(len(data)), ) # bytes received from Twisted, forward to the networking framework independent code for websocket self._dataReceived(data)
[docs] def _closeConnection(self, abort=False): if abort and hasattr(self.transport, "abortConnection"): self.transport.abortConnection() else: # e.g. ProcessProtocol lacks abortConnection() self.transport.loseConnection()
[docs] def _onOpen(self): if self._transport_details.is_secure: # now that the TLS opening handshake is complete, the actual TLS channel ID # will be available. make sure to set it! channel_id = { "tls-unique": transport_channel_id( self.transport, self._transport_details.is_server, "tls-unique" ), } self._transport_details.channel_id = channel_id self.onOpen()
[docs] def _onMessageBegin(self, isBinary): self.onMessageBegin(isBinary)
[docs] def _onMessageFrameBegin(self, length): self.onMessageFrameBegin(length)
[docs] def _onMessageFrameData(self, payload): self.onMessageFrameData(payload)
[docs] def _onMessageFrameEnd(self): self.onMessageFrameEnd()
[docs] def _onMessageFrame(self, payload): self.onMessageFrame(payload)
[docs] def _onMessageEnd(self): self.onMessageEnd()
[docs] def _onMessage(self, payload, isBinary): self.onMessage(payload, isBinary)
[docs] def _onPing(self, payload): self.onPing(payload)
[docs] def _onPong(self, payload): self.onPong(payload)
[docs] def _onClose(self, wasClean, code, reason): self.onClose(wasClean, code, reason)
[docs] def registerProducer(self, producer, streaming): """ Register a Twisted producer with this protocol. :param producer: A Twisted push or pull producer. :type producer: object :param streaming: Producer type. :type streaming: bool """ self.transport.registerProducer(producer, streaming)
[docs] def unregisterProducer(self): """ Unregister Twisted producer with this protocol. """ self.transport.unregisterProducer()
@public
[docs] class WebSocketServerProtocol( WebSocketAdapterProtocol, protocol.WebSocketServerProtocol ): """ Base class for Twisted-based WebSocket server protocols. Implements :class:`autobahn.websocket.interfaces.IWebSocketChannel`. """
[docs] log = txaio.make_logger()
[docs] is_server = True
# def onConnect(self, request: ConnectionRequest) -> Union[Optional[str], Tuple[Optional[str], Dict[str, str]]]: # pass @public
[docs] class WebSocketClientProtocol( WebSocketAdapterProtocol, protocol.WebSocketClientProtocol ): """ Base class for Twisted-based WebSocket client protocols. Implements :class:`autobahn.websocket.interfaces.IWebSocketChannel`. """
[docs] log = txaio.make_logger()
[docs] is_server = False
[docs] def _onConnect(self, response: ConnectionResponse): self.log.debug( "{meth}(response={response})", meth=hltype(self._onConnect), response=response, ) return self.onConnect(response)
[docs] def startTLS(self): self.log.debug("Starting TLS upgrade") self.transport.startTLS(self.factory.contextFactory)
[docs] class WebSocketAdapterFactory(object): """ Adapter class for Twisted-based WebSocket client and server factories. """
@public
[docs] class WebSocketServerFactory( WebSocketAdapterFactory, protocol.WebSocketServerFactory, twisted.internet.protocol.ServerFactory, ): """ Base class for Twisted-based WebSocket server factories. Implements :class:`autobahn.websocket.interfaces.IWebSocketServerChannelFactory` """
[docs] log = txaio.make_logger()
def __init__(self, *args, **kwargs): """ .. note:: In addition to all arguments to the constructor of :meth:`autobahn.websocket.interfaces.IWebSocketServerChannelFactory`, you can supply a ``reactor`` keyword argument to specify the Twisted reactor to be used. """ # lazy import to avoid reactor install upon module import reactor = kwargs.pop("reactor", None) if reactor is None: from twisted.internet import reactor
[docs] self.reactor = reactor
protocol.WebSocketServerFactory.__init__(self, *args, **kwargs)
@public
[docs] class WebSocketClientFactory( WebSocketAdapterFactory, protocol.WebSocketClientFactory, twisted.internet.protocol.ClientFactory, ): """ Base class for Twisted-based WebSocket client factories. Implements :class:`autobahn.websocket.interfaces.IWebSocketClientChannelFactory` """
[docs] log = txaio.make_logger()
def __init__(self, *args, **kwargs): """ .. note:: In addition to all arguments to the constructor of :func:`autobahn.websocket.interfaces.IWebSocketClientChannelFactory`, you can supply a ``reactor`` keyword argument to specify the Twisted reactor to be used. """ # lazy import to avoid reactor install upon module import reactor = kwargs.pop("reactor", None) if reactor is None: from twisted.internet import reactor
[docs] self.reactor = reactor
protocol.WebSocketClientFactory.__init__(self, *args, **kwargs) # we must up-call *before* we set up the contextFactory # because we need self.host etc to be set properly. if self.isSecure and self.proxy is not None: # if we have a proxy, then our factory will be used to # create the connection after CONNECT and if it's doing # TLS it needs a contextFactory from twisted.internet import ssl self.contextFactory = ssl.optionsForClientTLS(self.host)
# NOTE: there's thus no way to send in our own # context-factory, nor any TLS options. # Possibly we should allow 'proxy' to contain an actual # IStreamClientEndpoint instance instead of configuration for # how to make one @implementer(ITransport)
[docs] class WrappingWebSocketAdapter(object): """ An adapter for stream-based transport over WebSocket. This follows `websockify <https://github.com/kanaka/websockify>`_ and should be compatible with that. It uses WebSocket subprotocol negotiation and supports the following WebSocket subprotocols: - ``binary`` (or a compatible subprotocol) - ``base64`` Octets are either transmitted as the payload of WebSocket binary messages when using the ``binary`` subprotocol (or an alternative binary compatible subprotocol), or encoded with Base64 and then transmitted as the payload of WebSocket text messages when using the ``base64`` subprotocol. """
[docs] def onConnect(self, requestOrResponse): # Negotiate either the 'binary' or the 'base64' WebSocket subprotocol if isinstance(requestOrResponse, ConnectionRequest): request = requestOrResponse for p in request.protocols: if p in self.factory._subprotocols: self._binaryMode = p != "base64" return p raise ConnectionDeny( ConnectionDeny.NOT_ACCEPTABLE, "this server only speaks {0} WebSocket subprotocols".format( self.factory._subprotocols ), ) elif isinstance(requestOrResponse, ConnectionResponse): response = requestOrResponse if response.protocol not in self.factory._subprotocols: self._fail_connection( protocol.WebSocketProtocol.CLOSE_STATUS_CODE_PROTOCOL_ERROR, "this client only speaks {0} WebSocket subprotocols".format( self.factory._subprotocols ), ) self._binaryMode = response.protocol != "base64" else: # should not arrive here raise Exception("logic error")
[docs] def onOpen(self): self._proto.connectionMade()
[docs] def onMessage(self, payload, isBinary): if isBinary != self._binaryMode: self._fail_connection( protocol.WebSocketProtocol.CLOSE_STATUS_CODE_UNSUPPORTED_DATA, "message payload type does not match the negotiated subprotocol", ) else: if not isBinary: try: payload = b64decode(payload) except Exception as e: self._fail_connection( protocol.WebSocketProtocol.CLOSE_STATUS_CODE_INVALID_PAYLOAD, "message payload base64 decoding error: {0}".format(e), ) self._proto.dataReceived(payload)
# noinspection PyUnusedLocal
[docs] def onClose(self, wasClean, code, reason): self._proto.connectionLost(None)
[docs] def write(self, data): # part of ITransport assert type(data) == bytes if self._binaryMode: self.sendMessage(data, isBinary=True) else: data = b64encode(data) self.sendMessage(data, isBinary=False)
[docs] def writeSequence(self, data): # part of ITransport for d in data: self.write(d)
[docs] def loseConnection(self): # part of ITransport self.sendClose()
[docs] def getPeer(self): # part of ITransport return self.transport.getPeer()
[docs] def getHost(self): # part of ITransport return self.transport.getHost()
[docs] class WrappingWebSocketServerProtocol( WrappingWebSocketAdapter, WebSocketServerProtocol ): """ Server protocol for stream-based transport over WebSocket. """
[docs] class WrappingWebSocketClientProtocol( WrappingWebSocketAdapter, WebSocketClientProtocol ): """ Client protocol for stream-based transport over WebSocket. """
[docs] class WrappingWebSocketServerFactory(WebSocketServerFactory): """ Wrapping server factory for stream-based transport over WebSocket. """ def __init__( self, factory, url, reactor=None, enableCompression=True, autoFragmentSize=0, subprotocol=None, ): """ :param factory: Stream-based factory to be wrapped. :type factory: A subclass of ``twisted.internet.protocol.Factory`` :param url: WebSocket URL of the server this server factory will work for. :type url: unicode """
[docs] self._factory = factory
[docs] self._subprotocols = ["binary", "base64"]
if subprotocol: self._subprotocols.append(subprotocol) WebSocketServerFactory.__init__( self, url=url, reactor=reactor, protocols=self._subprotocols ) # automatically fragment outgoing traffic into WebSocket frames # of this size self.setProtocolOptions(autoFragmentSize=autoFragmentSize) # play nice and perform WS closing handshake self.setProtocolOptions(failByDrop=False) if enableCompression: # Enable WebSocket extension "permessage-deflate". # Function to accept offers from the client .. def accept(offers): for offer in offers: if isinstance(offer, PerMessageDeflateOffer): return PerMessageDeflateOfferAccept(offer) self.setProtocolOptions(perMessageCompressionAccept=accept)
[docs] def buildProtocol(self, addr): proto = WrappingWebSocketServerProtocol() proto.factory = self proto._proto = self._factory.buildProtocol(addr) proto._proto.transport = proto return proto
[docs] def startFactory(self): self._factory.startFactory() WebSocketServerFactory.startFactory(self)
[docs] def stopFactory(self): self._factory.stopFactory() WebSocketServerFactory.stopFactory(self)
[docs] class WrappingWebSocketClientFactory(WebSocketClientFactory): """ Wrapping client factory for stream-based transport over WebSocket. """ def __init__( self, factory, url, reactor=None, enableCompression=True, autoFragmentSize=0, subprotocol=None, ): """ :param factory: Stream-based factory to be wrapped. :type factory: A subclass of ``twisted.internet.protocol.Factory`` :param url: WebSocket URL of the server this client factory will connect to. :type url: unicode """
[docs] self._factory = factory
[docs] self._subprotocols = ["binary", "base64"]
if subprotocol: self._subprotocols.append(subprotocol) WebSocketClientFactory.__init__( self, url=url, reactor=reactor, protocols=self._subprotocols ) # automatically fragment outgoing traffic into WebSocket frames # of this size self.setProtocolOptions(autoFragmentSize=autoFragmentSize) # play nice and perform WS closing handshake self.setProtocolOptions(failByDrop=False) if enableCompression: # Enable WebSocket extension "permessage-deflate". # The extensions offered to the server .. offers = [PerMessageDeflateOffer()] self.setProtocolOptions(perMessageCompressionOffers=offers) # Function to accept responses from the server .. def accept(response): if isinstance(response, PerMessageDeflateResponse): return PerMessageDeflateResponseAccept(response) self.setProtocolOptions(perMessageCompressionAccept=accept)
[docs] def buildProtocol(self, addr): proto = WrappingWebSocketClientProtocol() proto.factory = self proto._proto = self._factory.buildProtocol(addr) proto._proto.transport = proto return proto
@public
[docs] def connectWS(factory, contextFactory=None, timeout=30, bindAddress=None): """ Establish WebSocket connection to a server. The connection parameters like target host, port, resource and others are provided via the factory. :param factory: The WebSocket protocol factory to be used for creating client protocol instances. :type factory: An :class:`autobahn.websocket.WebSocketClientFactory` instance. :param contextFactory: SSL context factory, required for secure WebSocket connections ("wss"). :type contextFactory: A `twisted.internet.ssl.ClientContextFactory <http://twistedmatrix.com/documents/current/api/twisted.internet.ssl.ClientContextFactory.html>`_ instance. :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 :returns: The connector. :rtype: An object which implements `twisted.interface.IConnector <http://twistedmatrix.com/documents/current/api/twisted.internet.interfaces.IConnector.html>`_. """ # lazy import to avoid reactor install upon module import if hasattr(factory, "reactor"): reactor = factory.reactor else: from twisted.internet import reactor if factory.isSecure: if contextFactory is None: # create default client SSL context factory when none given from twisted.internet import ssl contextFactory = ssl.ClientContextFactory() if factory.proxy is not None: factory.contextFactory = contextFactory conn = reactor.connectTCP( factory.proxy["host"], factory.proxy["port"], factory, timeout, bindAddress ) else: if factory.isSecure: conn = reactor.connectSSL( factory.host, factory.port, factory, contextFactory, timeout, bindAddress, ) else: conn = reactor.connectTCP( factory.host, factory.port, factory, timeout, bindAddress ) return conn
@public
[docs] def listenWS(factory, contextFactory=None, backlog=50, interface=""): """ Listen for incoming WebSocket connections from clients. The connection parameters like listening port and others are provided via the factory. :param factory: The WebSocket protocol factory to be used for creating server protocol instances. :type factory: An :class:`autobahn.websocket.WebSocketServerFactory` instance. :param contextFactory: SSL context factory, required for secure WebSocket connections ("wss"). :type contextFactory: A twisted.internet.ssl.ContextFactory. :param backlog: Size of the listen queue. :type backlog: int :param interface: The interface (derived from hostname given) to bind to, defaults to '' (all). :type interface: str :returns: The listening port. :rtype: An object that implements `twisted.interface.IListeningPort <http://twistedmatrix.com/documents/current/api/twisted.internet.interfaces.IListeningPort.html>`_. """ # lazy import to avoid reactor install upon module import if hasattr(factory, "reactor"): reactor = factory.reactor else: from twisted.internet import reactor if factory.isSecure: if contextFactory is None: raise Exception( "Secure WebSocket listen requested, but no SSL context factory given" ) listener = reactor.listenSSL( factory.port, factory, contextFactory, backlog, interface ) else: listener = reactor.listenTCP(factory.port, factory, backlog, interface) return listener
@public
[docs] class WampWebSocketServerProtocol( websocket.WampWebSocketServerProtocol, WebSocketServerProtocol ): """ Twisted-based WAMP-over-WebSocket server protocol. Implements: * :class:`autobahn.wamp.interfaces.ITransport` """
@public
[docs] class WampWebSocketServerFactory( websocket.WampWebSocketServerFactory, WebSocketServerFactory ): """ Twisted-based WAMP-over-WebSocket server protocol factory. """
[docs] protocol = WampWebSocketServerProtocol
def __init__(self, factory, *args, **kwargs): """ :param factory: A callable that produces instances that implement :class:`autobahn.wamp.interfaces.ITransportHandler` :type factory: callable :param serializers: A list of WAMP serializers to use (or ``None`` for all available serializers). :type serializers: list of objects implementing :class:`autobahn.wamp.interfaces.ISerializer` """ serializers = kwargs.pop("serializers", None) websocket.WampWebSocketServerFactory.__init__(self, factory, serializers) kwargs["protocols"] = self._protocols # noinspection PyCallByClass WebSocketServerFactory.__init__(self, *args, **kwargs)
@public
[docs] class WampWebSocketClientProtocol( websocket.WampWebSocketClientProtocol, WebSocketClientProtocol ): """ Twisted-based WAMP-over-WebSocket client protocol. Implements: * :class:`autobahn.wamp.interfaces.ITransport` """
@public
[docs] class WampWebSocketClientFactory( websocket.WampWebSocketClientFactory, WebSocketClientFactory ): """ Twisted-based WAMP-over-WebSocket client protocol factory. """
[docs] protocol = WampWebSocketClientProtocol
def __init__(self, factory, *args, **kwargs): """ :param factory: A callable that produces instances that implement :class:`autobahn.wamp.interfaces.ITransportHandler` :type factory: callable :param serializer: The WAMP serializer to use (or ``None`` for "best" serializer, chosen as the first serializer available from this list: CBOR, MessagePack, UBJSON, JSON). :type serializer: object implementing :class:`autobahn.wamp.interfaces.ISerializer` """ serializers = kwargs.pop("serializers", None) websocket.WampWebSocketClientFactory.__init__(self, factory, serializers) kwargs["protocols"] = self._protocols WebSocketClientFactory.__init__(self, *args, **kwargs) # Reduce the factory logs noise
[docs] self.noisy = False