1############################################################################### 2# 3# The MIT License (MIT) 4# 5# Copyright (c) Crossbar.io Technologies GmbH 6# 7# Permission is hereby granted, free of charge, to any person obtaining a copy 8# of this software and associated documentation files (the "Software"), to deal 9# in the Software without restriction, including without limitation the rights 10# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11# copies of the Software, and to permit persons to whom the Software is 12# furnished to do so, subject to the following conditions: 13# 14# The above copyright notice and this permission notice shall be included in 15# all copies or substantial portions of the Software. 16# 17# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23# THE SOFTWARE. 24# 25############################################################################### 26 27from __future__ import absolute_import 28 29from zope.interface import implementer 30 31from twisted.plugin import IPlugin 32from twisted.internet.interfaces import IStreamServerEndpointStringParser, \ 33 IStreamServerEndpoint, \ 34 IStreamClientEndpoint 35 36try: 37 from twisted.internet.interfaces import IStreamClientEndpointStringParserWithReactor 38 _HAS_REACTOR_ARG = True 39except ImportError: 40 _HAS_REACTOR_ARG = False 41 from twisted.internet.interfaces import IStreamClientEndpointStringParser as \ 42 IStreamClientEndpointStringParserWithReactor 43 44from twisted.internet.endpoints import serverFromString, clientFromString 45 46from autobahn.twisted.websocket import WrappingWebSocketServerFactory, \ 47 WrappingWebSocketClientFactory 48 49 50def _parseOptions(options): 51 opts = {} 52 53 if 'url' not in options: 54 raise Exception("URL needed") 55 else: 56 opts['url'] = options['url'] 57 58 if 'compression' in options: 59 value = options['compression'].lower().strip() 60 if value == 'true': 61 opts['enableCompression'] = True 62 elif value == 'false': 63 opts['enableCompression'] = False 64 else: 65 raise Exception("invalid value '{0}' for compression".format(value)) 66 67 if 'autofrag' in options: 68 try: 69 value = int(options['autofrag']) 70 except: 71 raise Exception("invalid value '{0}' for autofrag".format(options['autofrag'])) 72 73 if value < 0: 74 raise Exception("negative value '{0}' for autofrag".format(value)) 75 76 opts['autoFragmentSize'] = value 77 78 if 'subprotocol' in options: 79 value = options['subprotocol'].lower().strip() 80 opts['subprotocol'] = value 81 82 if 'debug' in options: 83 value = options['debug'].lower().strip() 84 if value == 'true': 85 opts['debug'] = True 86 elif value == 'false': 87 opts['debug'] = False 88 else: 89 raise Exception("invalid value '{0}' for debug".format(value)) 90 91 return opts 92 93 94@implementer(IPlugin) 95@implementer(IStreamServerEndpointStringParser) 96class AutobahnServerParser(object): 97 98 prefix = "autobahn" 99 100 def parseStreamServer(self, reactor, description, **options): 101 102 # The present endpoint plugin is intended to be used as in the 103 # following for running a streaming protocol over WebSocket over 104 # an underlying stream transport. 105 # 106 # endpoint = serverFromString(reactor, 107 # "autobahn:tcp\:9000\:interface\=0.0.0.0:url=ws\://localhost\:9000:compress=false" 108 # 109 # This will result in `parseStreamServer` to be called will 110 # 111 # description == tcp:9000:interface=0.0.0.0 112 # 113 # and 114 # 115 # options == {'url': 'ws://localhost:9000', 'compress': 'false'} 116 # 117 # Essentially, we are using the `\:` escape to coerce the endpoint descriptor 118 # of the underlying stream transport into one (first) positional argument. 119 # 120 # Note that the `\:` within "url" is another form of escaping! 121 # 122 opts = _parseOptions(options) 123 endpoint = serverFromString(reactor, description) 124 return AutobahnServerEndpoint(reactor, endpoint, opts) 125 126 127@implementer(IPlugin) 128@implementer(IStreamServerEndpoint) 129class AutobahnServerEndpoint(object): 130 131 def __init__(self, reactor, endpoint, options): 132 self._reactor = reactor 133 self._endpoint = endpoint 134 self._options = options 135 136 def listen(self, protocolFactory): 137 return self._endpoint.listen(WrappingWebSocketServerFactory(protocolFactory, reactor=self._reactor, **self._options)) 138 139 140# note that for older Twisted before the WithReactor variant, we 141# import it under that name so we can share (most of) this 142# implementation... 143@implementer(IPlugin) 144@implementer(IStreamClientEndpointStringParserWithReactor) 145class AutobahnClientParser(object): 146 prefix = "autobahn" 147 148 def parseStreamClient(self, *args, **options): 149 if _HAS_REACTOR_ARG: 150 reactor = args[0] 151 if len(args) != 2: 152 raise RuntimeError("autobahn: client plugin takes exactly one positional argument") 153 description = args[1] 154 else: 155 from twisted.internet import reactor 156 if len(args) != 1: 157 raise RuntimeError("autobahn: client plugin takes exactly one positional argument") 158 description = args[0] 159 opts = _parseOptions(options) 160 endpoint = clientFromString(reactor, description) 161 return AutobahnClientEndpoint(reactor, endpoint, opts) 162 163 164@implementer(IPlugin) 165@implementer(IStreamClientEndpoint) 166class AutobahnClientEndpoint(object): 167 168 def __init__(self, reactor, endpoint, options): 169 self._reactor = reactor 170 self._endpoint = endpoint 171 self._options = options 172 173 def connect(self, protocolFactory): 174 return self._endpoint.connect(WrappingWebSocketClientFactory(protocolFactory, reactor=self._reactor, **self._options)) 175 176 177autobahnServerParser = AutobahnServerParser() 178autobahnClientParser = AutobahnClientParser() 179