add rawsocket url helpers
This commit is contained in:
28
autobahn/rawsocket/__init__.py
Normal file
28
autobahn/rawsocket/__init__.py
Normal file
@@ -0,0 +1,28 @@
|
||||
###############################################################################
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) Tavendo 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 __future__ import absolute_import
|
25
autobahn/rawsocket/test/__init__.py
Normal file
25
autobahn/rawsocket/test/__init__.py
Normal file
@@ -0,0 +1,25 @@
|
||||
###############################################################################
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) Tavendo 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.
|
||||
#
|
||||
###############################################################################
|
90
autobahn/rawsocket/test/test_rawsocket_url.py
Normal file
90
autobahn/rawsocket/test/test_rawsocket_url.py
Normal file
@@ -0,0 +1,90 @@
|
||||
###############################################################################
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) Tavendo 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 __future__ import absolute_import
|
||||
|
||||
import unittest2 as unittest
|
||||
|
||||
from autobahn.rawsocket.util import create_url, parse_url
|
||||
|
||||
|
||||
class TestCreateRsUrl(unittest.TestCase):
|
||||
|
||||
def test_create_url01(self):
|
||||
self.assertEqual(create_url("localhost"), "rs://localhost:80")
|
||||
|
||||
def test_create_url02(self):
|
||||
self.assertEqual(create_url("localhost", port=8090), "rs://localhost:8090")
|
||||
|
||||
def test_create_url03(self):
|
||||
self.assertEqual(create_url("localhost", isSecure=True), "rss://localhost:443")
|
||||
|
||||
def test_create_url04(self):
|
||||
self.assertEqual(create_url("localhost", isSecure=True, port=443), "rss://localhost:443")
|
||||
|
||||
def test_create_url05(self):
|
||||
self.assertEqual(create_url("localhost", isSecure=True, port=80), "rss://localhost:80")
|
||||
|
||||
|
||||
class TestParseWsUrl(unittest.TestCase):
|
||||
|
||||
# parse_url -> (isSecure, host, port)
|
||||
|
||||
def test_parse_url01(self):
|
||||
self.assertEqual(parse_url("rs://localhost"), (False, 'localhost', 80))
|
||||
|
||||
def test_parse_url02(self):
|
||||
self.assertEqual(parse_url("rss://localhost"), (True, 'localhost', 443))
|
||||
|
||||
def test_parse_url03(self):
|
||||
self.assertEqual(parse_url("rs://localhost:9000"), (False, 'localhost', 9000))
|
||||
|
||||
def test_parse_url04(self):
|
||||
self.assertEqual(parse_url("rss://localhost:9000"), (True, 'localhost', 9000))
|
||||
|
||||
def test_parse_url05(self):
|
||||
self.assertRaises(Exception, parse_url, "ws://localhost")
|
||||
|
||||
def test_parse_url06(self):
|
||||
self.assertRaises(Exception, parse_url, "wss://localhost")
|
||||
|
||||
def test_parse_url07(self):
|
||||
self.assertRaises(Exception, parse_url, "ws://localhost:80")
|
||||
|
||||
def test_parse_url08(self):
|
||||
self.assertRaises(Exception, parse_url, "rs://localhost/somepath")
|
||||
|
||||
def test_parse_url09(self):
|
||||
self.assertRaises(Exception, parse_url, "rs://localhost#somefrag")
|
||||
|
||||
def test_parse_url10(self):
|
||||
self.assertRaises(Exception, parse_url, "rs://localhost?foo=bar")
|
||||
|
||||
def test_parse_url11(self):
|
||||
self.assertRaises(Exception, parse_url, "rss://")
|
||||
|
||||
def test_parse_url12(self):
|
||||
self.assertRaises(Exception, parse_url, "rs://")
|
134
autobahn/rawsocket/util.py
Normal file
134
autobahn/rawsocket/util.py
Normal file
@@ -0,0 +1,134 @@
|
||||
###############################################################################
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) Tavendo 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 __future__ import absolute_import
|
||||
|
||||
import six
|
||||
|
||||
# The Python urlparse module currently does not contain the rs/rss
|
||||
# schemes, so we add those dynamically (which is a hack of course).
|
||||
# Since the urllib from six.moves does not seem to expose the stuff
|
||||
# we monkey patch here, we do it manually.
|
||||
#
|
||||
# Important: if you change this stuff (you shouldn't), make sure
|
||||
# _all_ our unit tests for WS URLs succeed
|
||||
#
|
||||
if not six.PY3:
|
||||
# Python 2
|
||||
import urlparse
|
||||
else:
|
||||
# Python 3
|
||||
from urllib import parse as urlparse
|
||||
|
||||
wsschemes = ["rs", "rss"]
|
||||
urlparse.uses_relative.extend(wsschemes)
|
||||
urlparse.uses_netloc.extend(wsschemes)
|
||||
urlparse.uses_params.extend(wsschemes)
|
||||
urlparse.uses_query.extend(wsschemes)
|
||||
urlparse.uses_fragment.extend(wsschemes)
|
||||
|
||||
__all__ = (
|
||||
"create_url",
|
||||
"parse_url",
|
||||
)
|
||||
|
||||
|
||||
def create_url(hostname, port=None, isSecure=False):
|
||||
"""
|
||||
Create a RawSocket URL from components.
|
||||
|
||||
:param hostname: RawSocket server hostname.
|
||||
:type hostname: str
|
||||
|
||||
:param port: RawSocket service port or None (to select default
|
||||
ports 80/443 depending on isSecure).
|
||||
:type port: int
|
||||
|
||||
:param isSecure: Set True for secure RawSocket ("rss" scheme).
|
||||
:type isSecure: bool
|
||||
|
||||
:returns: str -- Constructed RawSocket URL.
|
||||
"""
|
||||
if port is not None:
|
||||
netloc = "%s:%d" % (hostname, port)
|
||||
else:
|
||||
if isSecure:
|
||||
netloc = u"{}:443".format(hostname)
|
||||
else:
|
||||
netloc = u"{}:80".format(hostname)
|
||||
if isSecure:
|
||||
scheme = u"rss"
|
||||
else:
|
||||
scheme = u"rs"
|
||||
return u"{}://{}".format(scheme, netloc)
|
||||
|
||||
|
||||
def parse_url(url):
|
||||
"""
|
||||
Parses as RawSocket URL into it's components and returns a tuple (isSecure, host, port).
|
||||
|
||||
- ``isSecure`` is a flag which is True for rss URLs.
|
||||
- ``host`` is the hostname or IP from the URL.
|
||||
- ``port`` is the port from the URL or standard port derived from
|
||||
scheme (rs = 80, rss = 443).
|
||||
|
||||
:param url: A valid RawSocket URL, i.e. ``rs://localhost:9000``
|
||||
:type url: str
|
||||
|
||||
:returns: tuple -- A tuple (isSecure, host, port)
|
||||
"""
|
||||
parsed = urlparse.urlparse(url)
|
||||
|
||||
if parsed.scheme not in ["rs", "rss"]:
|
||||
raise Exception("invalid RawSocket URL: protocol scheme '{}' is not for RawSocket".format(parsed.scheme))
|
||||
|
||||
if not parsed.hostname or parsed.hostname == "":
|
||||
raise Exception("invalid RawSocket URL: missing hostname")
|
||||
|
||||
if parsed.path is not None and parsed.path != "":
|
||||
raise Exception("invalid RawSocket URL: non-empty path '{}'".format(parsed.path))
|
||||
|
||||
if parsed.query is not None and parsed.query != "":
|
||||
raise Exception("invalid RawSocket URL: non-empty query '{}'".format(parsed.query))
|
||||
|
||||
if parsed.fragment is not None and parsed.fragment != "":
|
||||
raise Exception("invalid RawSocket URL: non-empty fragment '{}'".format(parsed.fragment))
|
||||
|
||||
if parsed.port is None or parsed.port == "":
|
||||
if parsed.scheme == "rs":
|
||||
port = 80
|
||||
else:
|
||||
port = 443
|
||||
else:
|
||||
port = int(parsed.port)
|
||||
|
||||
if port < 1 or port > 65535:
|
||||
raise Exception("invalid port {}".format(port))
|
||||
|
||||
return parsed.scheme == "rss", parsed.hostname, port
|
||||
|
||||
|
||||
print create_url('crossbar.io', 9000, True)
|
@@ -131,7 +131,7 @@ class ApplicationRunner(object):
|
||||
self.proxy = proxy
|
||||
self.headers = headers
|
||||
|
||||
def run(self, make, start_reactor=True):
|
||||
def run(self, make, start_reactor=True, auto_reconnect=False):
|
||||
"""
|
||||
Run the application component.
|
||||
|
||||
@@ -232,12 +232,14 @@ class ApplicationRunner(object):
|
||||
reactor.addSystemEventTrigger('before', 'shutdown', cleanup, proto)
|
||||
return proto
|
||||
|
||||
try:
|
||||
# since Twisted 16.1.0
|
||||
from twisted.application.internet import ClientService
|
||||
use_service = True
|
||||
except ImportError:
|
||||
use_service = False
|
||||
use_service = False
|
||||
if auto_reconnect:
|
||||
try:
|
||||
# since Twisted 16.1.0
|
||||
from twisted.application.internet import ClientService
|
||||
use_service = True
|
||||
except ImportError:
|
||||
use_service = False
|
||||
|
||||
if use_service:
|
||||
self.log.debug('using t.a.i.ClientService')
|
||||
|
@@ -123,10 +123,10 @@ def parse_url(url):
|
||||
:returns: tuple -- A tuple (isSecure, host, port, resource, path, params)
|
||||
"""
|
||||
parsed = urlparse.urlparse(url)
|
||||
if parsed.scheme not in ["ws", "wss"]:
|
||||
raise Exception("invalid WebSocket URL: protocol scheme '{}' is not for WebSocket".format(parsed.scheme))
|
||||
if not parsed.hostname or parsed.hostname == "":
|
||||
raise Exception("invalid WebSocket URL: missing hostname")
|
||||
if parsed.scheme not in ["ws", "wss"]:
|
||||
raise Exception("invalid WebSocket URL: bogus protocol scheme '%s'" % parsed.scheme)
|
||||
if parsed.port is None or parsed.port == "":
|
||||
if parsed.scheme == "ws":
|
||||
port = 80
|
||||
|
@@ -59,11 +59,11 @@ class MyAppSession(ApplicationSession):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
txaio.start_logging(level='info')
|
||||
txaio.start_logging(level='debug')
|
||||
|
||||
# create a WAMP session object. this is reused across multiple
|
||||
# reconnects (if automatically reconnected)
|
||||
session = MyAppSession(ComponentConfig(u'realm1', {}))
|
||||
|
||||
runner = ApplicationRunner(u'ws://localhost:8080/ws', u'realm1')
|
||||
runner.run(session)
|
||||
runner.run(session, auto_reconnect=True)
|
||||
|
Reference in New Issue
Block a user