def start_router_transport(self, id, config, details=None): """ Start a transport on this router. :param id: The ID of the transport to start. :type id: str :param config: The transport configuration. :type config: dict """ self.log.debug("{}.start_router_transport".format(self.__class__.__name__), id=id, config=config) # prohibit starting a transport twice # if id in self.transports: emsg = "Could not start transport: a transport with ID '{}' is already running (or starting)".format(id) self.log.error(emsg) raise ApplicationError(u'crossbar.error.already_running', emsg) # check configuration # try: checkconfig.check_router_transport(config) except Exception as e: emsg = "Invalid router transport configuration: {}".format(e) self.log.error(emsg) raise ApplicationError(u"crossbar.error.invalid_configuration", emsg) else: self.log.debug("Starting {}-transport on router.".format(config['type'])) # standalone WAMP-RawSocket transport # if config['type'] == 'rawsocket': transport_factory = WampRawSocketServerFactory(self._router_session_factory, config) transport_factory.noisy = False # standalone WAMP-WebSocket transport # elif config['type'] == 'websocket': transport_factory = WampWebSocketServerFactory(self._router_session_factory, self.config.extra.cbdir, config, self._templates) transport_factory.noisy = False # Flash-policy file server pseudo transport # elif config['type'] == 'flashpolicy': transport_factory = FlashPolicyFactory(config.get('allowed_domain', None), config.get('allowed_ports', None)) # WebSocket testee pseudo transport # elif config['type'] == 'websocket.testee': transport_factory = WebSocketTesteeServerFactory(config, self._templates) # Stream testee pseudo transport # elif config['type'] == 'stream.testee': transport_factory = StreamTesteeServerFactory() # Twisted Web based transport # elif config['type'] == 'web': options = config.get('options', {}) # create Twisted Web root resource # root_config = config['paths']['/'] root_type = root_config['type'] root_options = root_config.get('options', {}) # Static file hierarchy root resource # if root_type == 'static': if 'directory' in root_config: root_dir = os.path.abspath(os.path.join(self.config.extra.cbdir, root_config['directory'])) elif 'package' in root_config: if 'resource' not in root_config: raise ApplicationError(u"crossbar.error.invalid_configuration", "missing resource") try: mod = importlib.import_module(root_config['package']) except ImportError as e: emsg = "Could not import resource {} from package {}: {}".format(root_config['resource'], root_config['package'], e) self.log.error(emsg) raise ApplicationError(u"crossbar.error.invalid_configuration", emsg) else: try: root_dir = os.path.abspath(pkg_resources.resource_filename(root_config['package'], root_config['resource'])) except Exception as e: emsg = "Could not import resource {} from package {}: {}".format(root_config['resource'], root_config['package'], e) self.log.error(emsg) raise ApplicationError(u"crossbar.error.invalid_configuration", emsg) else: mod_version = getattr(mod, '__version__', '?.?.?') self.log.info("Loaded static Web resource '{}' from package '{} {}' (filesystem path {})".format(root_config['resource'], root_config['package'], mod_version, root_dir)) else: raise ApplicationError(u"crossbar.error.invalid_configuration", "missing web spec") root_dir = root_dir.encode('ascii', 'ignore') # http://stackoverflow.com/a/20433918/884770 self.log.debug("Starting Web service at root directory {}".format(root_dir)) # create resource for file system hierarchy # if root_options.get('enable_directory_listing', False): static_resource_class = StaticResource else: static_resource_class = StaticResourceNoListing cache_timeout = root_options.get('cache_timeout', DEFAULT_CACHE_TIMEOUT) root = static_resource_class(root_dir, cache_timeout=cache_timeout) # set extra MIME types # root.contentTypes.update(EXTRA_MIME_TYPES) if 'mime_types' in root_options: root.contentTypes.update(root_options['mime_types']) patchFileContentTypes(root) # render 404 page on any concrete path not found # root.childNotFound = Resource404(self._templates, root_dir) # WSGI root resource # elif root_type == 'wsgi': if not _HAS_WSGI: raise ApplicationError(u"crossbar.error.invalid_configuration", "WSGI unsupported") # wsgi_options = root_config.get('options', {}) if 'module' not in root_config: raise ApplicationError(u"crossbar.error.invalid_configuration", "missing WSGI app module") if 'object' not in root_config: raise ApplicationError(u"crossbar.error.invalid_configuration", "missing WSGI app object") # import WSGI app module and object mod_name = root_config['module'] try: mod = importlib.import_module(mod_name) except ImportError as e: raise ApplicationError(u"crossbar.error.invalid_configuration", "WSGI app module '{}' import failed: {} - Python search path was {}".format(mod_name, e, sys.path)) else: obj_name = root_config['object'] if obj_name not in mod.__dict__: raise ApplicationError(u"crossbar.error.invalid_configuration", "WSGI app object '{}' not in module '{}'".format(obj_name, mod_name)) else: app = getattr(mod, obj_name) # create a Twisted Web WSGI resource from the user's WSGI application object try: wsgi_resource = WSGIResource(self._reactor, self._reactor.getThreadPool(), app) except Exception as e: raise ApplicationError(u"crossbar.error.invalid_configuration", "could not instantiate WSGI resource: {}".format(e)) else: # create a root resource serving everything via WSGI root = WSGIRootResource(wsgi_resource, {}) # Redirecting root resource # elif root_type == 'redirect': redirect_url = root_config['url'].encode('ascii', 'ignore') root = RedirectResource(redirect_url) # Publisher resource (part of REST-bridge) # elif root_type == 'publisher': # create a vanilla session: the publisher will use this to inject events # publisher_session_config = ComponentConfig(realm=root_config['realm'], extra=None) publisher_session = ApplicationSession(publisher_session_config) # add the publishing session to the router # self._router_session_factory.add(publisher_session, authrole=root_config.get('role', 'anonymous')) # now create the publisher Twisted Web resource and add it to resource tree # root = PublisherResource(root_config.get('options', {}), publisher_session) # Webhook resource (part of REST-bridge) # elif root_type == 'webhook': # create a vanilla session: the webhook will use this to inject events # webhook_session_config = ComponentConfig(realm=root_config['realm'], extra=None) webhook_session = ApplicationSession(webhook_session_config) # add the publishing session to the router # self._router_session_factory.add(webhook_session, authrole=root_config.get('role', 'anonymous')) # now create the webhook Twisted Web resource and add it to resource tree # root = WebhookResource(root_config.get('options', {}), webhook_session) # Caller resource (part of REST-bridge) # elif root_type == 'caller': # create a vanilla session: the caller will use this to inject calls # caller_session_config = ComponentConfig(realm=root_config['realm'], extra=None) caller_session = ApplicationSession(caller_session_config) # add the calling session to the router # self._router_session_factory.add(caller_session, authrole=root_config.get('role', 'anonymous')) # now create the caller Twisted Web resource and add it to resource tree # root = CallerResource(root_config.get('options', {}), caller_session) # Generic Twisted Web resource # elif root_type == 'resource': try: klassname = root_config['classname'] self.log.debug("Starting class '{}'".format(klassname)) c = klassname.split('.') module_name, klass_name = '.'.join(c[:-1]), c[-1] module = importlib.import_module(module_name) make = getattr(module, klass_name) root = make(root_config.get('extra', {})) except Exception as e: emsg = "Failed to import class '{}' - {}".format(klassname, e) self.log.error(emsg) self.log.error("PYTHONPATH: {pythonpath}", pythonpath=sys.path) raise ApplicationError(u"crossbar.error.class_import_failed", emsg) # Invalid root resource # else: raise ApplicationError(u"crossbar.error.invalid_configuration", "invalid Web root path type '{}'".format(root_type)) # create Twisted Web resources on all non-root paths configured # self.add_paths(root, config.get('paths', {})) # create the actual transport factory # transport_factory = Site(root) transport_factory.noisy = False # Web access logging # if not options.get('access_log', False): transport_factory.log = lambda _: None # Traceback rendering # transport_factory.displayTracebacks = options.get('display_tracebacks', False) # HSTS # if options.get('hsts', False): if 'tls' in config['endpoint']: hsts_max_age = int(options.get('hsts_max_age', 31536000)) transport_factory.requestFactory = createHSTSRequestFactory(transport_factory.requestFactory, hsts_max_age) else: self.log.warn("Warning: HSTS requested, but running on non-TLS - skipping HSTS") # enable Hixie-76 on Twisted Web # if options.get('hixie76_aware', False): transport_factory.protocol = HTTPChannelHixie76Aware # needed if Hixie76 is to be supported # Unknown transport type # else: # should not arrive here, since we did check_transport() in the beginning raise Exception("logic error") # create transport endpoint / listening port from transport factory # d = create_listening_port_from_config(config['endpoint'], transport_factory, self.config.extra.cbdir, self._reactor) def ok(port): self.transports[id] = RouterTransport(id, config, transport_factory, port) self.log.debug("Router transport '{}'' started and listening".format(id)) return def fail(err): emsg = "Cannot listen on transport endpoint: {}".format(err.value) self.log.error(emsg) raise ApplicationError(u"crossbar.error.cannot_listen", emsg) d.addCallbacks(ok, fail) return d
def start_router_transport(self, id, config, details=None): """ Start a transport on this router worker. :param id: The ID of the transport to start. :type id: str :param config: The transport configuration. :type config: dict """ self.log.debug("{}.start_router_transport".format(self.__class__.__name__), id=id, config=config) # prohibit starting a transport twice # if id in self.transports: emsg = "Could not start transport: a transport with ID '{}' is already running (or starting)".format(id) self.log.error(emsg) raise ApplicationError(u'crossbar.error.already_running', emsg) # check configuration # try: checkconfig.check_router_transport(config) except Exception as e: emsg = "Invalid router transport configuration: {}".format(e) self.log.error(emsg) raise ApplicationError(u"crossbar.error.invalid_configuration", emsg) else: self.log.debug("Starting {}-transport on router.".format(config['type'])) # standalone WAMP-RawSocket transport # if config['type'] == 'rawsocket': transport_factory = WampRawSocketServerFactory(self._router_session_factory, config) transport_factory.noisy = False # standalone WAMP-WebSocket transport # elif config['type'] == 'websocket': transport_factory = WampWebSocketServerFactory(self._router_session_factory, self.config.extra.cbdir, config, self._templates) transport_factory.noisy = False # Flash-policy file server pseudo transport # elif config['type'] == 'flashpolicy': transport_factory = FlashPolicyFactory(config.get('allowed_domain', None), config.get('allowed_ports', None)) # WebSocket testee pseudo transport # elif config['type'] == 'websocket.testee': transport_factory = WebSocketTesteeServerFactory(config, self._templates) # Stream testee pseudo transport # elif config['type'] == 'stream.testee': transport_factory = StreamTesteeServerFactory() # Twisted Web based transport # elif config['type'] == 'web': options = config.get('options', {}) # create Twisted Web root resource # if '/' in config['paths']: root_config = config['paths']['/'] root = self._create_resource(root_config, nested=False) else: root = Resource404(self._templates, b'') # create Twisted Web resources on all non-root paths configured # self._add_paths(root, config.get('paths', {})) # create the actual transport factory # transport_factory = Site(root) transport_factory.noisy = False # Web access logging # if not options.get('access_log', False): transport_factory.log = lambda _: None # Traceback rendering # transport_factory.displayTracebacks = options.get('display_tracebacks', False) # HSTS # if options.get('hsts', False): if 'tls' in config['endpoint']: hsts_max_age = int(options.get('hsts_max_age', 31536000)) transport_factory.requestFactory = createHSTSRequestFactory(transport_factory.requestFactory, hsts_max_age) else: self.log.warn("Warning: HSTS requested, but running on non-TLS - skipping HSTS") # Unknown transport type # else: # should not arrive here, since we did check_transport() in the beginning raise Exception("logic error") # create transport endpoint / listening port from transport factory # d = create_listening_port_from_config(config['endpoint'], self.config.extra.cbdir, transport_factory, self._reactor, self.log) def ok(port): self.transports[id] = RouterTransport(id, config, transport_factory, port) self.log.debug("Router transport '{}'' started and listening".format(id)) return def fail(err): emsg = "Cannot listen on transport endpoint: {log_failure}" self.log.error(emsg, log_failure=err) raise ApplicationError(u"crossbar.error.cannot_listen", emsg) d.addCallbacks(ok, fail) return d
def start_router_transport(self, id, config, details=None): """ Start a transport on this router and return when the transport has started. **Usage:** This procedure is registered under * ``crossbar.node.<node_id>.worker.<worker_id>.start_router_transport`` The procedure takes a WAMP transport configuration with a listening endpoint, e.g. .. code-block:: javascript { "type": "websocket", "endpoint": { "type": "tcp", "port": 8080 } } **Errors:** The procedure may raise the following errors: * ``crossbar.error.invalid_configuration`` - the provided transport configuration is invalid * ``crossbar.error.already_running`` - a transport with the given ID is already running (or starting) * ``crossbar.error.cannot_listen`` - could not listen on the configured listening endpoint of the transport * ``crossbar.error.class_import_failed`` - a side-by-side component could not be instantiated **Events:** The procedure will publish an event when the transport **is starting** to * ``crossbar.node.<node_id>.worker.<worker_id>.on_router_transport_starting`` and publish an event when the transport **has started** to * ``crossbar.node.<node_id>.worker.<worker_id>.on_router_transport_started`` :param id: The ID of the transport to start. :type id: unicode :param config: The transport configuration. :type config: dict """ self.log.debug("{}.start_router_transport".format(self.__class__.__name__), id=id, config=config) # prohibit starting a transport twice # if id in self.transports: emsg = "Could not start transport: a transport with ID '{}' is already running (or starting)".format(id) self.log.error(emsg) raise ApplicationError(u'crossbar.error.already_running', emsg) # check configuration # try: checkconfig.check_router_transport(config) except Exception as e: emsg = "Invalid router transport configuration: {}".format(e) self.log.error(emsg) raise ApplicationError(u"crossbar.error.invalid_configuration", emsg) else: self.log.debug("Starting {}-transport on router.".format(config['type'])) # standalone WAMP-RawSocket transport # if config['type'] == 'rawsocket': transport_factory = WampRawSocketServerFactory(self._router_session_factory, config) transport_factory.noisy = False # standalone WAMP-WebSocket transport # elif config['type'] == 'websocket': transport_factory = WampWebSocketServerFactory(self._router_session_factory, self.config.extra.cbdir, config, self._templates) transport_factory.noisy = False # Flash-policy file server pseudo transport # elif config['type'] == 'flashpolicy': transport_factory = FlashPolicyFactory(config.get('allowed_domain', None), config.get('allowed_ports', None)) # WebSocket testee pseudo transport # elif config['type'] == 'websocket.testee': transport_factory = WebSocketTesteeServerFactory(config, self._templates) # Stream testee pseudo transport # elif config['type'] == 'stream.testee': transport_factory = StreamTesteeServerFactory() # Twisted Web based transport # elif config['type'] == 'web': options = config.get('options', {}) # create Twisted Web root resource # root_config = config['paths']['/'] root_type = root_config['type'] root_options = root_config.get('options', {}) # Static file hierarchy root resource # if root_type == 'static': if 'directory' in root_config: root_dir = os.path.abspath(os.path.join(self.config.extra.cbdir, root_config['directory'])) elif 'package' in root_config: if 'resource' not in root_config: raise ApplicationError(u"crossbar.error.invalid_configuration", "missing resource") try: mod = importlib.import_module(root_config['package']) except ImportError as e: emsg = "Could not import resource {} from package {}: {}".format(root_config['resource'], root_config['package'], e) self.log.error(emsg) raise ApplicationError(u"crossbar.error.invalid_configuration", emsg) else: try: root_dir = os.path.abspath(pkg_resources.resource_filename(root_config['package'], root_config['resource'])) except Exception as e: emsg = "Could not import resource {} from package {}: {}".format(root_config['resource'], root_config['package'], e) self.log.error(emsg) raise ApplicationError(u"crossbar.error.invalid_configuration", emsg) else: mod_version = getattr(mod, '__version__', '?.?.?') self.log.info("Loaded static Web resource '{}' from package '{} {}' (filesystem path {})".format(root_config['resource'], root_config['package'], mod_version, root_dir)) else: raise ApplicationError(u"crossbar.error.invalid_configuration", "missing web spec") root_dir = root_dir.encode('ascii', 'ignore') # http://stackoverflow.com/a/20433918/884770 self.log.debug("Starting Web service at root directory {}".format(root_dir)) # create resource for file system hierarchy # if root_options.get('enable_directory_listing', False): static_resource_class = StaticResource else: static_resource_class = StaticResourceNoListing cache_timeout = root_options.get('cache_timeout', DEFAULT_CACHE_TIMEOUT) root = static_resource_class(root_dir, cache_timeout=cache_timeout) # set extra MIME types # root.contentTypes.update(EXTRA_MIME_TYPES) if 'mime_types' in root_options: root.contentTypes.update(root_options['mime_types']) patchFileContentTypes(root) # render 404 page on any concrete path not found # root.childNotFound = Resource404(self._templates, root_dir) # WSGI root resource # elif root_type == 'wsgi': if not _HAS_WSGI: raise ApplicationError(u"crossbar.error.invalid_configuration", "WSGI unsupported") # wsgi_options = root_config.get('options', {}) if 'module' not in root_config: raise ApplicationError(u"crossbar.error.invalid_configuration", "missing WSGI app module") if 'object' not in root_config: raise ApplicationError(u"crossbar.error.invalid_configuration", "missing WSGI app object") # import WSGI app module and object mod_name = root_config['module'] try: mod = importlib.import_module(mod_name) except ImportError as e: raise ApplicationError(u"crossbar.error.invalid_configuration", "WSGI app module '{}' import failed: {} - Python search path was {}".format(mod_name, e, sys.path)) else: obj_name = root_config['object'] if obj_name not in mod.__dict__: raise ApplicationError(u"crossbar.error.invalid_configuration", "WSGI app object '{}' not in module '{}'".format(obj_name, mod_name)) else: app = getattr(mod, obj_name) # create a Twisted Web WSGI resource from the user's WSGI application object try: wsgi_resource = WSGIResource(self._reactor, self._reactor.getThreadPool(), app) except Exception as e: raise ApplicationError(u"crossbar.error.invalid_configuration", "could not instantiate WSGI resource: {}".format(e)) else: # create a root resource serving everything via WSGI root = WSGIRootResource(wsgi_resource, {}) # Redirecting root resource # elif root_type == 'redirect': redirect_url = root_config['url'].encode('ascii', 'ignore') root = RedirectResource(redirect_url) # Publisher resource (part of REST-bridge) # elif root_type == 'publisher': # create a vanilla session: the publisher will use this to inject events # publisher_session_config = ComponentConfig(realm=root_config['realm'], extra=None) publisher_session = ApplicationSession(publisher_session_config) # add the publishing session to the router # self._router_session_factory.add(publisher_session, authrole=root_config.get('role', 'anonymous')) # now create the publisher Twisted Web resource and add it to resource tree # root = PublisherResource(root_config.get('options', {}), publisher_session) # Webhook resource (part of REST-bridge) # elif root_type == 'webhook': # create a vanilla session: the webhook will use this to inject events # webhook_session_config = ComponentConfig(realm=root_config['realm'], extra=None) webhook_session = ApplicationSession(webhook_session_config) # add the publishing session to the router # self._router_session_factory.add(webhook_session, authrole=root_config.get('role', 'anonymous')) # now create the webhook Twisted Web resource and add it to resource tree # root = WebhookResource(root_config.get('options', {}), webhook_session) # Caller resource (part of REST-bridge) # elif root_type == 'caller': # create a vanilla session: the caller will use this to inject calls # caller_session_config = ComponentConfig(realm=root_config['realm'], extra=None) caller_session = ApplicationSession(caller_session_config) # add the calling session to the router # self._router_session_factory.add(caller_session, authrole=root_config.get('role', 'anonymous')) # now create the caller Twisted Web resource and add it to resource tree # root = CallerResource(root_config.get('options', {}), caller_session) # Generic Twisted Web resource # elif root_type == 'resource': try: klassname = root_config['classname'] self.log.debug("Starting class '{}'".format(klassname)) c = klassname.split('.') module_name, klass_name = '.'.join(c[:-1]), c[-1] module = importlib.import_module(module_name) make = getattr(module, klass_name) root = make(root_config.get('extra', {})) except Exception as e: emsg = "Failed to import class '{}' - {}".format(klassname, e) self.log.error(emsg) self.log.error("PYTHONPATH: {pythonpath}", pythonpath=sys.path) raise ApplicationError(u"crossbar.error.class_import_failed", emsg) # Invalid root resource # else: raise ApplicationError(u"crossbar.error.invalid_configuration", "invalid Web root path type '{}'".format(root_type)) # create Twisted Web resources on all non-root paths configured # self.add_paths(root, config.get('paths', {})) # create the actual transport factory # transport_factory = Site(root) transport_factory.noisy = False # Web access logging # if not options.get('access_log', False): transport_factory.log = lambda _: None # Traceback rendering # transport_factory.displayTracebacks = options.get('display_tracebacks', False) # HSTS # if options.get('hsts', False): if 'tls' in config['endpoint']: hsts_max_age = int(options.get('hsts_max_age', 31536000)) transport_factory.requestFactory = createHSTSRequestFactory(transport_factory.requestFactory, hsts_max_age) else: self.log.warn("Warning: HSTS requested, but running on non-TLS - skipping HSTS") # Unknown transport type # else: # should not arrive here, since we did check_transport() in the beginning raise Exception("logic error") # create transport endpoint / listening port from transport factory # d = create_listening_port_from_config(config['endpoint'], transport_factory, self.config.extra.cbdir, self._reactor) def ok(port): self.transports[id] = RouterTransport(id, config, transport_factory, port) self.log.debug("Router transport '{}'' started and listening".format(id)) return def fail(err): emsg = "Cannot listen on transport endpoint: {}".format(err.value) self.log.error(emsg) raise ApplicationError(u"crossbar.error.cannot_listen", emsg) d.addCallbacks(ok, fail) return d
def start_router_transport(self, id, config, details=None): """ Start a transport on this router. :param id: The ID of the transport to start. :type id: str :param config: The transport configuration. :type config: dict """ if self.debug: log.msg( "{}.start_router_transport".format(self.__class__.__name__), id, config) ## prohibit starting a transport twice ## if id in self.transports: emsg = "ERROR: could not start transport - a transport with ID '{}'' is already running (or starting)".format( id) log.msg(emsg) raise ApplicationError('crossbar.error.already_running', emsg) ## check configuration ## try: checkconfig.check_router_transport(config) except Exception as e: emsg = "ERROR: invalid router transport configuration ({})".format( e) log.msg(emsg) raise ApplicationError("crossbar.error.invalid_configuration", emsg) else: if self.debug: log.msg("Starting {}-transport on router.".format( config['type'])) ## standalone WAMP-RawSocket transport ## if config['type'] == 'rawsocket': transport_factory = CrossbarWampRawSocketServerFactory( self.session_factory, config) transport_factory.noisy = False ## standalone WAMP-WebSocket transport ## elif config['type'] == 'websocket': transport_factory = CrossbarWampWebSocketServerFactory( self.session_factory, self.config.extra.cbdir, config, self._templates) transport_factory.noisy = False ## Flash-policy file server pseudo transport ## elif config['type'] == 'flashpolicy': transport_factory = FlashPolicyFactory( config.get('allowed_domain', None), config.get('allowed_ports', None)) ## WebSocket testee pseudo transport ## elif config['type'] == 'websocket.testee': transport_factory = WebSocketTesteeServerFactory( config, self._templates) ## Stream testee pseudo transport ## elif config['type'] == 'stream.testee': transport_factory = StreamTesteeServerFactory() ## Twisted Web based transport ## elif config['type'] == 'web': options = config.get('options', {}) ## create Twisted Web root resource ## root_config = config['paths']['/'] root_type = root_config['type'] root_options = root_config.get('options', {}) ## Static file hierarchy root resource ## if root_type == 'static': if 'directory' in root_config: root_dir = os.path.abspath( os.path.join(self.config.extra.cbdir, root_config['directory'])) elif 'package' in root_config: if not 'resource' in root_config: raise ApplicationError( "crossbar.error.invalid_configuration", "missing resource") try: mod = importlib.import_module(root_config['package']) except ImportError as e: emsg = "ERROR: could not import resource '{}' from package '{}' - {}".format( root_config['resource'], root_config['package'], e) log.msg(emsg) raise ApplicationError( "crossbar.error.invalid_configuration", emsg) else: try: root_dir = os.path.abspath( pkg_resources.resource_filename( root_config['package'], root_config['resource'])) except Exception as e: emsg = "ERROR: could not import resource '{}' from package '{}' - {}".format( root_config['resource'], root_config['package'], e) log.msg(emsg) raise ApplicationError( "crossbar.error.invalid_configuration", emsg) else: mod_version = getattr(mod, '__version__', '?.?.?') log.msg( "Loaded static Web resource '{}' from package '{} {}' (filesystem path {})" .format(root_config['resource'], root_config['package'], mod_version, root_dir)) else: raise ApplicationError( "crossbar.error.invalid_configuration", "missing web spec") root_dir = root_dir.encode( 'ascii', 'ignore') # http://stackoverflow.com/a/20433918/884770 if self.debug: log.msg("Starting Web service at root directory {}".format( root_dir)) ## create resource for file system hierarchy ## if root_options.get('enable_directory_listing', False): root = File(root_dir) else: root = FileNoListing(root_dir) ## set extra MIME types ## root.contentTypes.update(EXTRA_MIME_TYPES) if 'mime_types' in root_options: root.contentTypes.update(root_options['mime_types']) patchFileContentTypes(root) ## render 404 page on any concrete path not found ## root.childNotFound = Resource404(self._templates, root_dir) ## WSGI root resource ## elif root_type == 'wsgi': if not _HAS_WSGI: raise ApplicationError( "crossbar.error.invalid_configuration", "WSGI unsupported") wsgi_options = root_config.get('options', {}) if not 'module' in root_config: raise ApplicationError( "crossbar.error.invalid_configuration", "missing module") if not 'object' in root_config: raise ApplicationError( "crossbar.error.invalid_configuration", "missing object") try: mod = importlib.import_module(root_config['module']) except ImportError: raise ApplicationError( "crossbar.error.invalid_configuration", "module import failed") else: if not root_config['object'] in mod.__dict__: raise ApplicationError( "crossbar.error.invalid_configuration", "object not in module") else: app = getattr(mod, root_config['object']) ## create a Twisted Web WSGI resource from the user's WSGI application object try: wsgi_resource = WSGIResource(reactor, reactor.getThreadPool(), app) except Exception as e: raise ApplicationError( "crossbar.error.invalid_configuration", "could not instantiate WSGI resource: {}".format(e)) else: ## create a root resource serving everything via WSGI root = WSGIRootResource(wsgi_resource, {}) ## Redirecting root resource ## elif root_type == 'redirect': redirect_url = root_config['url'].encode('ascii', 'ignore') root = RedirectResource(redirect_url) ## Pusher resource ## elif root_type == 'pusher': ## create a vanilla session: the pusher will use this to inject events ## pusher_session_config = ComponentConfig( realm=root_config['realm'], extra=None) pusher_session = ApplicationSession(pusher_session_config) ## add the pushing session to the router ## self.session_factory.add(pusher_session, authrole=root_config.get( 'role', 'anonymous')) ## now create the pusher Twisted Web resource and add it to resource tree ## root = PusherResource(root_config.get('options', {}), pusher_session) ## Invalid root resource ## else: raise ApplicationError( "crossbar.error.invalid_configuration", "invalid Web root path type '{}'".format(root_type)) ## create Twisted Web resources on all non-root paths configured ## self.add_paths(root, config.get('paths', {})) ## create the actual transport factory ## transport_factory = Site(root) transport_factory.noisy = False ## Web access logging ## if not options.get('access_log', False): transport_factory.log = lambda _: None ## Traceback rendering ## transport_factory.displayTracebacks = options.get( 'display_tracebacks', False) ## HSTS ## if options.get('hsts', False): if 'tls' in config['endpoint']: hsts_max_age = int(options.get('hsts_max_age', 31536000)) transport_factory.requestFactory = createHSTSRequestFactory( transport_factory.requestFactory, hsts_max_age) else: log.msg( "Warning: HSTS requested, but running on non-TLS - skipping HSTS" ) ## enable Hixie-76 on Twisted Web ## if options.get('hixie76_aware', False): transport_factory.protocol = HTTPChannelHixie76Aware # needed if Hixie76 is to be supported ## Unknown transport type ## else: ## should not arrive here, since we did check_transport() in the beginning raise Exception("logic error") ## create transport endpoint / listening port from transport factory ## d = create_listening_port_from_config(config['endpoint'], transport_factory, self.config.extra.cbdir, reactor) def ok(port): self.transports[id] = RouterTransport(id, config, transport_factory, port) if self.debug: log.msg( "Router transport '{}'' started and listening".format(id)) return def fail(err): emsg = "ERROR: cannot listen on transport endpoint ({})".format( err.value) log.msg(emsg) raise ApplicationError("crossbar.error.cannot_listen", emsg) d.addCallbacks(ok, fail) return d
def start_router_transport(self, id, config, details=None): """ Start a transport on this router and return when the transport has started. **Usage:** This procedure is registered under * ``crossbar.node.<node_id>.worker.<worker_id>.start_router_transport`` The procedure takes a WAMP transport configuration with a listening endpoint, e.g. .. code-block:: javascript { "type": "websocket", "endpoint": { "type": "tcp", "port": 8080 } } **Errors:** The procedure may raise the following errors: * ``crossbar.error.invalid_configuration`` - the provided transport configuration is invalid * ``crossbar.error.already_running`` - a transport with the given ID is already running (or starting) * ``crossbar.error.cannot_listen`` - could not listen on the configured listening endpoint of the transport * ``crossbar.error.class_import_failed`` - a side-by-side component could not be instantiated **Events:** The procedure will publish an event when the transport **is starting** to * ``crossbar.node.<node_id>.worker.<worker_id>.on_router_transport_starting`` and publish an event when the transport **has started** to * ``crossbar.node.<node_id>.worker.<worker_id>.on_router_transport_started`` :param id: The ID of the transport to start. :type id: unicode :param config: The transport configuration. :type config: dict """ self.log.debug("{}.start_router_transport".format(self.__class__.__name__), id=id, config=config) # prohibit starting a transport twice # if id in self.transports: emsg = "Could not start transport: a transport with ID '{}' is already running (or starting)".format(id) self.log.error(emsg) raise ApplicationError(u"crossbar.error.already_running", emsg) # check configuration # try: checkconfig.check_router_transport(config) except Exception as e: emsg = "Invalid router transport configuration: {}".format(e) self.log.error(emsg) raise ApplicationError(u"crossbar.error.invalid_configuration", emsg) else: self.log.debug("Starting {}-transport on router.".format(config["type"])) # standalone WAMP-RawSocket transport # if config["type"] == "rawsocket": transport_factory = WampRawSocketServerFactory(self._router_session_factory, config) transport_factory.noisy = False # standalone WAMP-WebSocket transport # elif config["type"] == "websocket": transport_factory = WampWebSocketServerFactory( self._router_session_factory, self.config.extra.cbdir, config, self._templates ) transport_factory.noisy = False # Flash-policy file server pseudo transport # elif config["type"] == "flashpolicy": transport_factory = FlashPolicyFactory( config.get("allowed_domain", None), config.get("allowed_ports", None) ) # WebSocket testee pseudo transport # elif config["type"] == "websocket.testee": transport_factory = WebSocketTesteeServerFactory(config, self._templates) # Stream testee pseudo transport # elif config["type"] == "stream.testee": transport_factory = StreamTesteeServerFactory() # Twisted Web based transport # elif config["type"] == "web": options = config.get("options", {}) # create Twisted Web root resource # if "/" in config["paths"]: root_config = config["paths"]["/"] root = self.create_resource(root_config, nested=False) else: root = Resource404(self._templates, b"") # create Twisted Web resources on all non-root paths configured # self.add_paths(root, config.get("paths", {})) # create the actual transport factory # transport_factory = Site(root) transport_factory.noisy = False # Web access logging # if not options.get("access_log", False): transport_factory.log = lambda _: None # Traceback rendering # transport_factory.displayTracebacks = options.get("display_tracebacks", False) # HSTS # if options.get("hsts", False): if "tls" in config["endpoint"]: hsts_max_age = int(options.get("hsts_max_age", 31536000)) transport_factory.requestFactory = createHSTSRequestFactory( transport_factory.requestFactory, hsts_max_age ) else: self.log.warn("Warning: HSTS requested, but running on non-TLS - skipping HSTS") # Unknown transport type # else: # should not arrive here, since we did check_transport() in the beginning raise Exception("logic error") # create transport endpoint / listening port from transport factory # d = create_listening_port_from_config( config["endpoint"], transport_factory, self.config.extra.cbdir, self._reactor ) def ok(port): self.transports[id] = RouterTransport(id, config, transport_factory, port) self.log.debug("Router transport '{}'' started and listening".format(id)) return def fail(err): emsg = "Cannot listen on transport endpoint: {}".format(err.value) self.log.error(emsg) raise ApplicationError(u"crossbar.error.cannot_listen", emsg) d.addCallbacks(ok, fail) return d
def start_router_transport(self, id, config, details=None): """ Start a transport on this router worker. :param id: The ID of the transport to start. :type id: str :param config: The transport configuration. :type config: dict """ self.log.debug("{}.start_router_transport".format( self.__class__.__name__), id=id, config=config) # prohibit starting a transport twice # if id in self.transports: emsg = "Could not start transport: a transport with ID '{}' is already running (or starting)".format( id) self.log.error(emsg) raise ApplicationError(u'crossbar.error.already_running', emsg) # check configuration # try: checkconfig.check_router_transport(config) except Exception as e: emsg = "Invalid router transport configuration: {}".format(e) self.log.error(emsg) raise ApplicationError(u"crossbar.error.invalid_configuration", emsg) else: self.log.debug("Starting {}-transport on router.".format( config['type'])) # standalone WAMP-RawSocket transport # if config['type'] == 'rawsocket': transport_factory = WampRawSocketServerFactory( self._router_session_factory, config) transport_factory.noisy = False # standalone WAMP-WebSocket transport # elif config['type'] == 'websocket': transport_factory = WampWebSocketServerFactory( self._router_session_factory, self.config.extra.cbdir, config, self._templates) transport_factory.noisy = False # Flash-policy file server pseudo transport # elif config['type'] == 'flashpolicy': transport_factory = FlashPolicyFactory( config.get('allowed_domain', None), config.get('allowed_ports', None)) # WebSocket testee pseudo transport # elif config['type'] == 'websocket.testee': transport_factory = WebSocketTesteeServerFactory( config, self._templates) # Stream testee pseudo transport # elif config['type'] == 'stream.testee': transport_factory = StreamTesteeServerFactory() # Twisted Web based transport # elif config['type'] == 'web': options = config.get('options', {}) # create Twisted Web root resource # if '/' in config['paths']: root_config = config['paths']['/'] root = self._create_resource(root_config, nested=False) else: root = Resource404(self._templates, b'') # create Twisted Web resources on all non-root paths configured # self._add_paths(root, config.get('paths', {})) # create the actual transport factory # transport_factory = Site(root) transport_factory.noisy = False # Web access logging # if not options.get('access_log', False): transport_factory.log = lambda _: None # Traceback rendering # transport_factory.displayTracebacks = options.get( 'display_tracebacks', False) # HSTS # if options.get('hsts', False): if 'tls' in config['endpoint']: hsts_max_age = int(options.get('hsts_max_age', 31536000)) transport_factory.requestFactory = createHSTSRequestFactory( transport_factory.requestFactory, hsts_max_age) else: self.log.warn( "Warning: HSTS requested, but running on non-TLS - skipping HSTS" ) # Unknown transport type # else: # should not arrive here, since we did check_transport() in the beginning raise Exception("logic error") # create transport endpoint / listening port from transport factory # d = create_listening_port_from_config(config['endpoint'], self.config.extra.cbdir, transport_factory, self._reactor, self.log) def ok(port): self.transports[id] = RouterTransport(id, config, transport_factory, port) self.log.debug( "Router transport '{}'' started and listening".format(id)) return def fail(err): emsg = "Cannot listen on transport endpoint: {log_failure}" self.log.error(emsg, log_failure=err) raise ApplicationError(u"crossbar.error.cannot_listen", emsg) d.addCallbacks(ok, fail) return d
def start_router_transport(self, id, config, details=None): """ Start a transport on this router and return when the transport has started. **Usage:** This procedure is registered under * ``crossbar.node.<node_id>.worker.<worker_id>.start_router_transport`` The procedure takes a WAMP transport configuration with a listening endpoint, e.g. .. code-block:: javascript { "type": "websocket", "endpoint": { "type": "tcp", "port": 8080 } } **Errors:** The procedure may raise the following errors: * ``crossbar.error.invalid_configuration`` - the provided transport configuration is invalid * ``crossbar.error.already_running`` - a transport with the given ID is already running (or starting) * ``crossbar.error.cannot_listen`` - could not listen on the configured listening endpoint of the transport * ``crossbar.error.class_import_failed`` - a side-by-side component could not be instantiated **Events:** The procedure will publish an event when the transport **is starting** to * ``crossbar.node.<node_id>.worker.<worker_id>.on_router_transport_starting`` and publish an event when the transport **has started** to * ``crossbar.node.<node_id>.worker.<worker_id>.on_router_transport_started`` :param id: The ID of the transport to start. :type id: unicode :param config: The transport configuration. :type config: dict """ self.log.debug("{}.start_router_transport".format(self.__class__.__name__), id=id, config=config) # prohibit starting a transport twice # if id in self.transports: emsg = "Could not start transport: a transport with ID '{}' is already running (or starting)".format(id) self.log.error(emsg) raise ApplicationError(u"crossbar.error.already_running", emsg) # check configuration # try: checkconfig.check_router_transport(config) except Exception as e: emsg = "Invalid router transport configuration: {}".format(e) self.log.error(emsg) raise ApplicationError(u"crossbar.error.invalid_configuration", emsg) else: self.log.debug("Starting {}-transport on router.".format(config["type"])) # standalone WAMP-RawSocket transport # if config["type"] == "rawsocket": transport_factory = WampRawSocketServerFactory(self._router_session_factory, config) transport_factory.noisy = False # standalone WAMP-WebSocket transport # elif config["type"] == "websocket": transport_factory = WampWebSocketServerFactory( self._router_session_factory, self.config.extra.cbdir, config, self._templates ) transport_factory.noisy = False # Flash-policy file server pseudo transport # elif config["type"] == "flashpolicy": transport_factory = FlashPolicyFactory( config.get("allowed_domain", None), config.get("allowed_ports", None) ) # WebSocket testee pseudo transport # elif config["type"] == "websocket.testee": transport_factory = WebSocketTesteeServerFactory(config, self._templates) # Stream testee pseudo transport # elif config["type"] == "stream.testee": transport_factory = StreamTesteeServerFactory() # Twisted Web based transport # elif config["type"] == "web": options = config.get("options", {}) # create Twisted Web root resource # root_config = config["paths"]["/"] root_type = root_config["type"] root_options = root_config.get("options", {}) # Static file hierarchy root resource # if root_type == "static": if "directory" in root_config: root_dir = os.path.abspath(os.path.join(self.config.extra.cbdir, root_config["directory"])) elif "package" in root_config: if "resource" not in root_config: raise ApplicationError(u"crossbar.error.invalid_configuration", "missing resource") try: mod = importlib.import_module(root_config["package"]) except ImportError as e: emsg = "Could not import resource {} from package {}: {}".format( root_config["resource"], root_config["package"], e ) self.log.error(emsg) raise ApplicationError(u"crossbar.error.invalid_configuration", emsg) else: try: root_dir = os.path.abspath( pkg_resources.resource_filename(root_config["package"], root_config["resource"]) ) except Exception as e: emsg = "Could not import resource {} from package {}: {}".format( root_config["resource"], root_config["package"], e ) self.log.error(emsg) raise ApplicationError(u"crossbar.error.invalid_configuration", emsg) else: mod_version = getattr(mod, "__version__", "?.?.?") self.log.info( "Loaded static Web resource '{}' from package '{} {}' (filesystem path {})".format( root_config["resource"], root_config["package"], mod_version, root_dir ) ) else: raise ApplicationError(u"crossbar.error.invalid_configuration", "missing web spec") root_dir = root_dir.encode("ascii", "ignore") # http://stackoverflow.com/a/20433918/884770 self.log.debug("Starting Web service at root directory {}".format(root_dir)) # create resource for file system hierarchy # if root_options.get("enable_directory_listing", False): static_resource_class = StaticResource else: static_resource_class = StaticResourceNoListing cache_timeout = root_options.get("cache_timeout", DEFAULT_CACHE_TIMEOUT) root = static_resource_class(root_dir, cache_timeout=cache_timeout) # set extra MIME types # root.contentTypes.update(EXTRA_MIME_TYPES) if "mime_types" in root_options: root.contentTypes.update(root_options["mime_types"]) patchFileContentTypes(root) # render 404 page on any concrete path not found # root.childNotFound = Resource404(self._templates, root_dir) # WSGI root resource # elif root_type == "wsgi": if not _HAS_WSGI: raise ApplicationError(u"crossbar.error.invalid_configuration", "WSGI unsupported") # wsgi_options = root_config.get('options', {}) if "module" not in root_config: raise ApplicationError(u"crossbar.error.invalid_configuration", "missing WSGI app module") if "object" not in root_config: raise ApplicationError(u"crossbar.error.invalid_configuration", "missing WSGI app object") # import WSGI app module and object mod_name = root_config["module"] try: mod = importlib.import_module(mod_name) except ImportError as e: raise ApplicationError( u"crossbar.error.invalid_configuration", "WSGI app module '{}' import failed: {} - Python search path was {}".format( mod_name, e, sys.path ), ) else: obj_name = root_config["object"] if obj_name not in mod.__dict__: raise ApplicationError( u"crossbar.error.invalid_configuration", "WSGI app object '{}' not in module '{}'".format(obj_name, mod_name), ) else: app = getattr(mod, obj_name) # create a Twisted Web WSGI resource from the user's WSGI application object try: wsgi_resource = WSGIResource(self._reactor, self._reactor.getThreadPool(), app) except Exception as e: raise ApplicationError( u"crossbar.error.invalid_configuration", "could not instantiate WSGI resource: {}".format(e) ) else: # create a root resource serving everything via WSGI root = WSGIRootResource(wsgi_resource, {}) # Redirecting root resource # elif root_type == "redirect": redirect_url = root_config["url"].encode("ascii", "ignore") root = RedirectResource(redirect_url) # Publisher resource (part of REST-bridge) # elif root_type == "publisher": # create a vanilla session: the publisher will use this to inject events # publisher_session_config = ComponentConfig(realm=root_config["realm"], extra=None) publisher_session = ApplicationSession(publisher_session_config) # add the publishing session to the router # self._router_session_factory.add(publisher_session, authrole=root_config.get("role", "anonymous")) # now create the publisher Twisted Web resource and add it to resource tree # root = PublisherResource(root_config.get("options", {}), publisher_session) # Webhook resource (part of REST-bridge) # elif root_type == "webhook": # create a vanilla session: the webhook will use this to inject events # webhook_session_config = ComponentConfig(realm=root_config["realm"], extra=None) webhook_session = ApplicationSession(webhook_session_config) # add the publishing session to the router # self._router_session_factory.add(webhook_session, authrole=root_config.get("role", "anonymous")) # now create the webhook Twisted Web resource and add it to resource tree # root = WebhookResource(root_config.get("options", {}), webhook_session) # Caller resource (part of REST-bridge) # elif root_type == "caller": # create a vanilla session: the caller will use this to inject calls # caller_session_config = ComponentConfig(realm=root_config["realm"], extra=None) caller_session = ApplicationSession(caller_session_config) # add the calling session to the router # self._router_session_factory.add(caller_session, authrole=root_config.get("role", "anonymous")) # now create the caller Twisted Web resource and add it to resource tree # root = CallerResource(root_config.get("options", {}), caller_session) # Generic Twisted Web resource # elif root_type == "resource": try: klassname = root_config["classname"] self.log.debug("Starting class '{}'".format(klassname)) c = klassname.split(".") module_name, klass_name = ".".join(c[:-1]), c[-1] module = importlib.import_module(module_name) make = getattr(module, klass_name) root = make(root_config.get("extra", {})) except Exception as e: emsg = "Failed to import class '{}' - {}".format(klassname, e) self.log.error(emsg) self.log.error("PYTHONPATH: {pythonpath}", pythonpath=sys.path) raise ApplicationError(u"crossbar.error.class_import_failed", emsg) # Invalid root resource # else: raise ApplicationError( u"crossbar.error.invalid_configuration", "invalid Web root path type '{}'".format(root_type) ) # create Twisted Web resources on all non-root paths configured # self.add_paths(root, config.get("paths", {})) # create the actual transport factory # transport_factory = Site(root) transport_factory.noisy = False # Web access logging # if not options.get("access_log", False): transport_factory.log = lambda _: None # Traceback rendering # transport_factory.displayTracebacks = options.get("display_tracebacks", False) # HSTS # if options.get("hsts", False): if "tls" in config["endpoint"]: hsts_max_age = int(options.get("hsts_max_age", 31536000)) transport_factory.requestFactory = createHSTSRequestFactory( transport_factory.requestFactory, hsts_max_age ) else: self.log.warn("Warning: HSTS requested, but running on non-TLS - skipping HSTS") # Unknown transport type # else: # should not arrive here, since we did check_transport() in the beginning raise Exception("logic error") # create transport endpoint / listening port from transport factory # d = create_listening_port_from_config( config["endpoint"], transport_factory, self.config.extra.cbdir, self._reactor ) def ok(port): self.transports[id] = RouterTransport(id, config, transport_factory, port) self.log.debug("Router transport '{}'' started and listening".format(id)) return def fail(err): emsg = "Cannot listen on transport endpoint: {}".format(err.value) self.log.error(emsg) raise ApplicationError(u"crossbar.error.cannot_listen", emsg) d.addCallbacks(ok, fail) return d
def start_router_transport(self, id, config, details = None): """ Start a transport on this router. :param id: The ID of the transport to start. :type id: str :param config: The transport configuration. :type config: dict """ if self.debug: log.msg("{}.start_router_transport".format(self.__class__.__name__), id, config) ## prohibit starting a transport twice ## if id in self.transports: emsg = "ERROR: could not start transport - a transport with ID '{}'' is already running (or starting)".format(id) log.msg(emsg) raise ApplicationError('crossbar.error.already_running', emsg) ## check configuration ## try: checkconfig.check_router_transport(config) except Exception as e: emsg = "ERROR: invalid router transport configuration ({})".format(e) log.msg(emsg) raise ApplicationError("crossbar.error.invalid_configuration", emsg) else: if self.debug: log.msg("Starting {}-transport on router.".format(config['type'])) ## standalone WAMP-RawSocket transport ## if config['type'] == 'rawsocket': transport_factory = CrossbarWampRawSocketServerFactory(self.session_factory, config) transport_factory.noisy = False ## standalone WAMP-WebSocket transport ## elif config['type'] == 'websocket': transport_factory = CrossbarWampWebSocketServerFactory(self.session_factory, self.config.extra.cbdir, config, self._templates) transport_factory.noisy = False ## Twisted Web based transport ## elif config['type'] == 'web': options = config.get('options', {}) ## create Twisted Web root resource ## root_config = config['paths']['/'] root_type = root_config['type'] root_options = root_config.get('options', {}) ## Static file hierarchy root resource ## if root_type == 'static': if 'directory' in root_config: root_dir = os.path.abspath(os.path.join(self.config.extra.cbdir, root_config['directory'])) elif 'package' in root_config: if not 'resource' in root_config: raise ApplicationError("crossbar.error.invalid_configuration", "missing resource") try: mod = importlib.import_module(root_config['package']) except ImportError as e: emsg = "ERROR: could not import resource '{}' from package '{}' - {}".format(root_config['resource'], root_config['package'], e) log.msg(emsg) raise ApplicationError("crossbar.error.invalid_configuration", emsg) else: try: root_dir = os.path.abspath(pkg_resources.resource_filename(root_config['package'], root_config['resource'])) except Exception as e: emsg = "ERROR: could not import resource '{}' from package '{}' - {}".format(root_config['resource'], root_config['package'], e) log.msg(emsg) raise ApplicationError("crossbar.error.invalid_configuration", emsg) else: mod_version = getattr(mod, '__version__', '?.?.?') log.msg("Loaded static Web resource '{}' from package '{} {}' (filesystem path {})".format(root_config['resource'], root_config['package'], mod_version, root_dir)) else: raise ApplicationError("crossbar.error.invalid_configuration", "missing web spec") root_dir = root_dir.encode('ascii', 'ignore') # http://stackoverflow.com/a/20433918/884770 if self.debug: log.msg("Starting Web service at root directory {}".format(root_dir)) ## create resource for file system hierarchy ## if root_options.get('enable_directory_listing', False): root = File(root_dir) else: root = FileNoListing(root_dir) ## set extra MIME types ## root.contentTypes.update(EXTRA_MIME_TYPES) if 'mime_types' in root_options: root.contentTypes.update(root_options['mime_types']) patchFileContentTypes(root) ## render 404 page on any concrete path not found ## root.childNotFound = Resource404(self._templates, root_dir) ## WSGI root resource ## elif root_type == 'wsgi': if not _HAS_WSGI: raise ApplicationError("crossbar.error.invalid_configuration", "WSGI unsupported") wsgi_options = root_config.get('options', {}) if not 'module' in root_config: raise ApplicationError("crossbar.error.invalid_configuration", "missing module") if not 'object' in root_config: raise ApplicationError("crossbar.error.invalid_configuration", "missing object") try: mod = importlib.import_module(root_config['module']) except ImportError: raise ApplicationError("crossbar.error.invalid_configuration", "module import failed") else: if not root_config['object'] in mod.__dict__: raise ApplicationError("crossbar.error.invalid_configuration", "object not in module") else: app = getattr(mod, root_config['object']) ## create a Twisted Web WSGI resource from the user's WSGI application object try: wsgi_resource = WSGIResource(reactor, reactor.getThreadPool(), app) except Exception as e: raise ApplicationError("crossbar.error.invalid_configuration", "could not instantiate WSGI resource: {}".format(e)) else: ## create a root resource serving everything via WSGI root = WSGIRootResource(wsgi_resource, {}) ## Redirecting root resource ## elif root_type == 'redirect': redirect_url = root_config['url'].encode('ascii', 'ignore') root = RedirectResource(redirect_url) ## Pusher resource ## elif root_type == 'pusher': ## create a vanilla session: the pusher will use this to inject events ## pusher_session_config = ComponentConfig(realm = root_config['realm'], extra = None) pusher_session = ApplicationSession(pusher_session_config) ## add the pushing session to the router ## self.session_factory.add(pusher_session, authrole = root_config.get('role', 'anonymous')) ## now create the pusher Twisted Web resource and add it to resource tree ## root = PusherResource(root_config.get('options', {}), pusher_session) ## Invalid root resource ## else: raise ApplicationError("crossbar.error.invalid_configuration", "invalid Web root path type '{}'".format(root_type)) ## create Twisted Web resources on all non-root paths configured ## for path in sorted(config.get('paths', [])): if path != "/": path_config = config['paths'][path] ## websocket_echo ## websocket_testee ## s3mirror ## websocket_stdio ## ## WAMP-WebSocket resource ## if path_config['type'] == 'websocket': ws_factory = CrossbarWampWebSocketServerFactory(self.session_factory, self.config.extra.cbdir, path_config, self._templates) ## FIXME: Site.start/stopFactory should start/stop factories wrapped as Resources ws_factory.startFactory() ws_resource = WebSocketResource(ws_factory) root.putChild(path, ws_resource) ## Static file hierarchy resource ## elif path_config['type'] == 'static': static_options = path_config.get('options', {}) if 'directory' in path_config: static_dir = os.path.abspath(os.path.join(self.config.extra.cbdir, path_config['directory'])) elif 'package' in path_config: if not 'resource' in path_config: raise ApplicationError("crossbar.error.invalid_configuration", "missing resource") try: mod = importlib.import_module(path_config['package']) except ImportError as e: emsg = "ERROR: could not import resource '{}' from package '{}' - {}".format(path_config['resource'], path_config['package'], e) log.msg(emsg) raise ApplicationError("crossbar.error.invalid_configuration", emsg) else: try: static_dir = os.path.abspath(pkg_resources.resource_filename(path_config['package'], path_config['resource'])) except Exception as e: emsg = "ERROR: could not import resource '{}' from package '{}' - {}".format(path_config['resource'], path_config['package'], e) log.msg(emsg) raise ApplicationError("crossbar.error.invalid_configuration", emsg) else: raise ApplicationError("crossbar.error.invalid_configuration", "missing web spec") static_dir = static_dir.encode('ascii', 'ignore') # http://stackoverflow.com/a/20433918/884770 ## create resource for file system hierarchy ## if static_options.get('enable_directory_listing', False): static_resource = File(static_dir) else: static_resource = FileNoListing(static_dir) ## set extra MIME types ## static_resource.contentTypes.update(EXTRA_MIME_TYPES) if 'mime_types' in static_options: static_resource.contentTypes.update(static_options['mime_types']) patchFileContentTypes(static_resource) ## render 404 page on any concrete path not found ## static_resource.childNotFound = Resource404(self._templates, static_dir) root.putChild(path, static_resource) ## WSGI resource ## elif path_config['type'] == 'wsgi': if not _HAS_WSGI: raise ApplicationError("crossbar.error.invalid_configuration", "WSGI unsupported") wsgi_options = path_config.get('options', {}) if not 'module' in path_config: raise ApplicationError("crossbar.error.invalid_configuration", "missing module") if not 'object' in path_config: raise ApplicationError("crossbar.error.invalid_configuration", "missing object") try: mod = importlib.import_module(path_config['module']) except ImportError as e: raise ApplicationError("crossbar.error.invalid_configuration", "module import failed - {}".format(e)) else: if not path_config['object'] in mod.__dict__: raise ApplicationError("crossbar.error.invalid_configuration", "object not in module") else: app = getattr(mod, path_config['object']) ## create a Twisted Web WSGI resource from the user's WSGI application object try: wsgi_resource = WSGIResource(reactor, reactor.getThreadPool(), app) except Exception as e: raise ApplicationError("crossbar.error.invalid_configuration", "could not instantiate WSGI resource: {}".format(e)) else: root.putChild(path, wsgi_resource) ## Redirecting resource ## elif path_config['type'] == 'redirect': redirect_url = path_config['url'].encode('ascii', 'ignore') redirect_resource = RedirectResource(redirect_url) root.putChild(path, redirect_resource) ## JSON value resource ## elif path_config['type'] == 'json': value = path_config['value'] json_resource = JsonResource(value) root.putChild(path, json_resource) ## CGI script resource ## elif path_config['type'] == 'cgi': cgi_processor = path_config['processor'] cgi_directory = os.path.abspath(os.path.join(self.config.extra.cbdir, path_config['directory'])) cgi_directory = cgi_directory.encode('ascii', 'ignore') # http://stackoverflow.com/a/20433918/884770 cgi_resource = CgiDirectory(cgi_directory, cgi_processor, Resource404(self._templates, cgi_directory)) root.putChild(path, cgi_resource) ## WAMP-Longpoll transport resource ## elif path_config['type'] == 'longpoll': path_options = path_config.get('options', {}) lp_resource = WampLongPollResource(self.session_factory, timeout = path_options.get('request_timeout', 10), killAfter = path_options.get('session_timeout', 30), queueLimitBytes = path_options.get('queue_limit_bytes', 128 * 1024), queueLimitMessages = path_options.get('queue_limit_messages', 100), debug = path_options.get('debug', False), debug_transport_id = path_options.get('debug_transport_id', None) ) lp_resource._templates = self._templates root.putChild(path, lp_resource) ## Pusher resource ## elif path_config['type'] == 'pusher': ## create a vanilla session: the pusher will use this to inject events ## pusher_session_config = ComponentConfig(realm = path_config['realm'], extra = None) pusher_session = ApplicationSession(pusher_session_config) ## add the pushing session to the router ## self.session_factory.add(pusher_session, authrole = path_config.get('role', 'anonymous')) ## now create the pusher Twisted Web resource and add it to resource tree ## pusher_resource = PusherResource(path_config.get('options', {}), pusher_session) root.putChild(path, pusher_resource) ## Schema Docs resource ## elif path_config['type'] == 'schemadoc': realm = path_config['realm'] if not realm in self.realm_to_id: raise ApplicationError("crossbar.error.no_such_object", "No realm with URI '{}' configured".format(realm)) realm_id = self.realm_to_id[realm] realm_schemas = self.realms[realm_id].session._schemas schemadoc_resource = SchemaDocResource(self._templates, realm, realm_schemas) root.putChild(path, schemadoc_resource) else: raise ApplicationError("crossbar.error.invalid_configuration", "invalid Web path type '{}'".format(path_config['type'])) ## create the actual transport factory ## transport_factory = Site(root) transport_factory.noisy = False ## Web access logging ## if not options.get('access_log', False): transport_factory.log = lambda _: None ## Traceback rendering ## transport_factory.displayTracebacks = options.get('display_tracebacks', False) ## HSTS ## if options.get('hsts', False): if 'tls' in config['endpoint']: hsts_max_age = int(options.get('hsts_max_age', 31536000)) transport_factory.requestFactory = createHSTSRequestFactory(transport_factory.requestFactory, hsts_max_age) else: log.msg("Warning: HSTS requested, but running on non-TLS - skipping HSTS") ## enable Hixie-76 on Twisted Web ## if options.get('hixie76_aware', False): transport_factory.protocol = HTTPChannelHixie76Aware # needed if Hixie76 is to be supported else: ## should not arrive here, since we did check_transport() in the beginning raise Exception("logic error") ## create transport endpoint / listening port from transport factory ## d = create_listening_port_from_config(config['endpoint'], transport_factory, self.config.extra.cbdir, reactor) def ok(port): self.transports[id] = RouterTransport(id, config, transport_factory, port) if self.debug: log.msg("Router transport '{}'' started and listening".format(id)) return def fail(err): emsg = "ERROR: cannot listen on transport endpoint ({})".format(err.value) log.msg(emsg) raise ApplicationError("crossbar.error.cannot_listen", emsg) d.addCallbacks(ok, fail) return d
def start_router_transport(self, id, config, details=None): """ Start a transport on this router worker. :param id: The ID of the transport to start. :type id: str :param config: The transport configuration. :type config: dict """ self.log.debug("{}.start_router_transport".format(self.__class__.__name__), id=id, config=config) # prohibit starting a transport twice # if id in self.transports: emsg = "Could not start transport: a transport with ID '{}' is already running (or starting)".format(id) self.log.error(emsg) raise ApplicationError(u'crossbar.error.already_running', emsg) # check configuration # try: checkconfig.check_router_transport(config) except Exception as e: emsg = "Invalid router transport configuration: {}".format(e) self.log.error(emsg) raise ApplicationError(u"crossbar.error.invalid_configuration", emsg) else: self.log.debug("Starting {}-transport on router.".format(config['type'])) # standalone WAMP-RawSocket transport # if config['type'] == 'rawsocket': transport_factory = WampRawSocketServerFactory(self._router_session_factory, config) transport_factory.noisy = False # standalone WAMP-WebSocket transport # elif config['type'] == 'websocket': transport_factory = WampWebSocketServerFactory(self._router_session_factory, self.config.extra.cbdir, config, self._templates) transport_factory.noisy = False # Flash-policy file server pseudo transport # elif config['type'] == 'flashpolicy': transport_factory = FlashPolicyFactory(config.get('allowed_domain', None), config.get('allowed_ports', None)) # WebSocket testee pseudo transport # elif config['type'] == 'websocket.testee': transport_factory = WebSocketTesteeServerFactory(config, self._templates) # Stream testee pseudo transport # elif config['type'] == 'stream.testee': transport_factory = StreamTesteeServerFactory() # Twisted Web based transport # elif config['type'] == 'web': transport_factory = self._create_web_factory(config) # Universal transport # elif config['type'] == 'universal': if 'web' in config: web_factory = self._create_web_factory(config['web']) else: web_factory = None if 'rawsocket' in config: rawsocket_factory = WampRawSocketServerFactory(self._router_session_factory, config['rawsocket']) rawsocket_factory.noisy = False else: rawsocket_factory = None if 'websocket' in config: websocket_factory_map = {} for websocket_url_first_component, websocket_config in config['websocket'].items(): websocket_transport_factory = WampWebSocketServerFactory(self._router_session_factory, self.config.extra.cbdir, websocket_config, self._templates) websocket_transport_factory.noisy = False websocket_factory_map[websocket_url_first_component] = websocket_transport_factory self.log.debug('hooked up websocket factory on request URI {request_uri}', request_uri=websocket_url_first_component) else: websocket_factory_map = None transport_factory = UniSocketServerFactory(web_factory, websocket_factory_map, rawsocket_factory) # Unknown transport type # else: # should not arrive here, since we did check_transport() in the beginning raise Exception("logic error") # create transport endpoint / listening port from transport factory # d = create_listening_port_from_config(config['endpoint'], self.config.extra.cbdir, transport_factory, self._reactor, self.log) def ok(port): self.transports[id] = RouterTransport(id, config, transport_factory, port) self.log.debug("Router transport '{}'' started and listening".format(id)) return def fail(err): emsg = "Cannot listen on transport endpoint: {log_failure}" self.log.error(emsg, log_failure=err) raise ApplicationError(u"crossbar.error.cannot_listen", emsg) d.addCallbacks(ok, fail) return d
def start_router_transport(self, id, config, details=None): """ Start a transport on this router and return when the transport has started. **Usage:** This procedure is registered under * ``crossbar.node.<node_id>.worker.<worker_id>.start_router_transport`` The procedure takes a WAMP transport configuration with a listening endpoint, e.g. .. code-block:: javascript { "type": "websocket", "endpoint": { "type": "tcp", "port": 8080 } } **Errors:** The procedure may raise the following errors: * ``crossbar.error.invalid_configuration`` - the provided transport configuration is invalid * ``crossbar.error.already_running`` - a transport with the given ID is already running (or starting) * ``crossbar.error.cannot_listen`` - could not listen on the configured listening endpoint of the transport * ``crossbar.error.class_import_failed`` - a side-by-side component could not be instantiated **Events:** The procedure will publish an event when the transport **is starting** to * ``crossbar.node.<node_id>.worker.<worker_id>.on_router_transport_starting`` and publish an event when the transport **has started** to * ``crossbar.node.<node_id>.worker.<worker_id>.on_router_transport_started`` :param id: The ID of the transport to start. :type id: unicode :param config: The transport configuration. :type config: dict """ self.log.debug("{}.start_router_transport".format( self.__class__.__name__), id=id, config=config) # prohibit starting a transport twice # if id in self.transports: emsg = "Could not start transport: a transport with ID '{}' is already running (or starting)".format( id) self.log.error(emsg) raise ApplicationError(u'crossbar.error.already_running', emsg) # check configuration # try: checkconfig.check_router_transport(config) except Exception as e: emsg = "Invalid router transport configuration: {}".format(e) self.log.error(emsg) raise ApplicationError(u"crossbar.error.invalid_configuration", emsg) else: self.log.debug("Starting {}-transport on router.".format( config['type'])) # standalone WAMP-RawSocket transport # if config['type'] == 'rawsocket': transport_factory = WampRawSocketServerFactory( self._router_session_factory, config) transport_factory.noisy = False # standalone WAMP-WebSocket transport # elif config['type'] == 'websocket': transport_factory = WampWebSocketServerFactory( self._router_session_factory, self.config.extra.cbdir, config, self._templates) transport_factory.noisy = False # Flash-policy file server pseudo transport # elif config['type'] == 'flashpolicy': transport_factory = FlashPolicyFactory( config.get('allowed_domain', None), config.get('allowed_ports', None)) # WebSocket testee pseudo transport # elif config['type'] == 'websocket.testee': transport_factory = WebSocketTesteeServerFactory( config, self._templates) # Stream testee pseudo transport # elif config['type'] == 'stream.testee': transport_factory = StreamTesteeServerFactory() # Twisted Web based transport # elif config['type'] == 'web': options = config.get('options', {}) # create Twisted Web root resource # if '/' in config['paths']: root_config = config['paths']['/'] root = self.create_resource(root_config, nested=False) else: root = Resource404(self._templates, b'') # create Twisted Web resources on all non-root paths configured # self.add_paths(root, config.get('paths', {})) # create the actual transport factory # transport_factory = Site(root) transport_factory.noisy = False # Web access logging # if not options.get('access_log', False): transport_factory.log = lambda _: None # Traceback rendering # transport_factory.displayTracebacks = options.get( 'display_tracebacks', False) # HSTS # if options.get('hsts', False): if 'tls' in config['endpoint']: hsts_max_age = int(options.get('hsts_max_age', 31536000)) transport_factory.requestFactory = createHSTSRequestFactory( transport_factory.requestFactory, hsts_max_age) else: self.log.warn( "Warning: HSTS requested, but running on non-TLS - skipping HSTS" ) # Unknown transport type # else: # should not arrive here, since we did check_transport() in the beginning raise Exception("logic error") # create transport endpoint / listening port from transport factory # d = create_listening_port_from_config(config['endpoint'], transport_factory, self.config.extra.cbdir, self._reactor) def ok(port): self.transports[id] = RouterTransport(id, config, transport_factory, port) self.log.debug( "Router transport '{}'' started and listening".format(id)) return def fail(err): emsg = "Cannot listen on transport endpoint: {}".format(err.value) self.log.error(emsg) raise ApplicationError(u"crossbar.error.cannot_listen", emsg) d.addCallbacks(ok, fail) return d
def start_router_transport(self, id, config, details=None): """ Start a transport on this router worker. :param id: The ID of the transport to start. :type id: str :param config: The transport configuration. :type config: dict """ self.log.debug("{name}.start_router_transport", name=self.__class__.__name__) # prohibit starting a transport twice # if id in self.transports: emsg = "Could not start transport: a transport with ID '{}' is already running (or starting)".format(id) self.log.error(emsg) raise ApplicationError(u'crossbar.error.already_running', emsg) # check configuration # try: checkconfig.check_router_transport(config) except Exception as e: emsg = "Invalid router transport configuration: {}".format(e) self.log.error(emsg) raise ApplicationError(u"crossbar.error.invalid_configuration", emsg) else: self.log.debug("Starting {ttype}-transport on router.", ttype=config['type']) # standalone WAMP-RawSocket transport # if config['type'] == 'rawsocket': transport_factory = WampRawSocketServerFactory(self._router_session_factory, config) transport_factory.noisy = False # standalone WAMP-WebSocket transport # elif config['type'] == 'websocket': transport_factory = WampWebSocketServerFactory(self._router_session_factory, self.config.extra.cbdir, config, self._templates) transport_factory.noisy = False # Flash-policy file server pseudo transport # elif config['type'] == 'flashpolicy': transport_factory = FlashPolicyFactory(config.get('allowed_domain', None), config.get('allowed_ports', None)) # WebSocket testee pseudo transport # elif config['type'] == 'websocket.testee': transport_factory = WebSocketTesteeServerFactory(config, self._templates) # Stream testee pseudo transport # elif config['type'] == 'stream.testee': transport_factory = StreamTesteeServerFactory() # Twisted Web based transport # elif config['type'] == 'web': transport_factory = self._create_web_factory(config) # Universal transport # elif config['type'] == 'universal': if 'web' in config: web_factory = self._create_web_factory(config['web']) else: web_factory = None if 'rawsocket' in config: rawsocket_factory = WampRawSocketServerFactory(self._router_session_factory, config['rawsocket']) rawsocket_factory.noisy = False else: rawsocket_factory = None if 'websocket' in config: websocket_factory_map = {} for websocket_url_first_component, websocket_config in config['websocket'].items(): websocket_transport_factory = WampWebSocketServerFactory(self._router_session_factory, self.config.extra.cbdir, websocket_config, self._templates) websocket_transport_factory.noisy = False websocket_factory_map[websocket_url_first_component] = websocket_transport_factory self.log.debug('hooked up websocket factory on request URI {request_uri}', request_uri=websocket_url_first_component) else: websocket_factory_map = None transport_factory = UniSocketServerFactory(web_factory, websocket_factory_map, rawsocket_factory) # Unknown transport type # else: # should not arrive here, since we did check_transport() in the beginning raise Exception("logic error") # create transport endpoint / listening port from transport factory # d = create_listening_port_from_config(config['endpoint'], self.config.extra.cbdir, transport_factory, self._reactor, self.log) def ok(port): self.transports[id] = RouterTransport(id, config, transport_factory, port) self.log.debug("Router transport '{id}'' started and listening", id) return def fail(err): emsg = "Cannot listen on transport endpoint: {log_failure}" self.log.error(emsg, log_failure=err) raise ApplicationError(u"crossbar.error.cannot_listen", emsg) d.addCallbacks(ok, fail) return d