Пример #1
0
    def create_resource(self, path_config):
        """
        Creates child resource to be added to the parent.

        :param path_config: Configuration for the new child resource.
        :type path_config: dict

        :returns: Resource -- the new child resource
        """
        # WAMP-WebSocket resource
        #
        if path_config['type'] == 'websocket':

            ws_factory = WampWebSocketServerFactory(self._router_session_factory, self.config.extra.cbdir, path_config, self._templates)

            # FIXME: Site.start/stopFactory should start/stop factories wrapped as Resources
            ws_factory.startFactory()

            return WebSocketResource(ws_factory)

        # 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 'resource' not in path_config:
                    raise ApplicationError(u"crossbar.error.invalid_configuration", "missing resource")

                try:
                    mod = importlib.import_module(path_config['package'])
                except ImportError as e:
                    emsg = "Could not import resource {} from package {}: {}".format(path_config['resource'], path_config['package'], e)
                    self.log.error(emsg)
                    raise ApplicationError(u"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 = "Could not import resource {} from package {}: {}".format(path_config['resource'], path_config['package'], e)
                        self.log.error(emsg)
                        raise ApplicationError(u"crossbar.error.invalid_configuration", emsg)

            else:

                raise ApplicationError(u"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_class = StaticResource
            else:
                static_resource_class = StaticResourceNoListing

            cache_timeout = static_options.get('cache_timeout', DEFAULT_CACHE_TIMEOUT)

            static_resource = static_resource_class(static_dir, cache_timeout=cache_timeout)

            # 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)

            return static_resource

        # WSGI resource
        #
        elif path_config['type'] == 'wsgi':

            if not _HAS_WSGI:
                raise ApplicationError(u"crossbar.error.invalid_configuration", "WSGI unsupported")

            # wsgi_options = path_config.get('options', {})

            if 'module' not in path_config:
                raise ApplicationError(u"crossbar.error.invalid_configuration", "missing WSGI app module")

            if 'object' not in path_config:
                raise ApplicationError(u"crossbar.error.invalid_configuration", "missing WSGI app object")

            # import WSGI app module and object
            mod_name = path_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 = path_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:
                return wsgi_resource

        # Redirecting resource
        #
        elif path_config['type'] == 'redirect':
            redirect_url = path_config['url'].encode('ascii', 'ignore')
            return RedirectResource(redirect_url)

        # JSON value resource
        #
        elif path_config['type'] == 'json':
            value = path_config['value']

            return JsonResource(value)

        # 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

            return CgiDirectory(cgi_directory, cgi_processor, Resource404(self._templates, cgi_directory))

        # WAMP-Longpoll transport resource
        #
        elif path_config['type'] == 'longpoll':

            path_options = path_config.get('options', {})

            lp_resource = WampLongPollResource(self._router_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

            return lp_resource

        # Publisher resource (part of REST-bridge)
        #
        elif path_config['type'] == 'publisher':

            # create a vanilla session: the publisher will use this to inject events
            #
            publisher_session_config = ComponentConfig(realm=path_config['realm'], extra=None)
            publisher_session = ApplicationSession(publisher_session_config)

            # add the publisher session to the router
            #
            self._router_session_factory.add(publisher_session, authrole=path_config.get('role', 'anonymous'))

            # now create the publisher Twisted Web resource
            #
            return PublisherResource(path_config.get('options', {}), publisher_session)

        # Webhook resource (part of REST-bridge)
        #
        elif path_config['type'] == 'webhook':

            # create a vanilla session: the webhook will use this to inject events
            #
            webhook_session_config = ComponentConfig(realm=path_config['realm'], extra=None)
            webhook_session = ApplicationSession(webhook_session_config)

            # add the webhook session to the router
            #
            self._router_session_factory.add(webhook_session, authrole=path_config.get('role', 'anonymous'))

            # now create the webhook Twisted Web resource
            #
            return WebhookResource(path_config.get('options', {}), webhook_session)

        # Caller resource (part of REST-bridge)
        #
        elif path_config['type'] == 'caller':

            # create a vanilla session: the caller will use this to inject calls
            #
            caller_session_config = ComponentConfig(realm=path_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=path_config.get('role', 'anonymous'))

            # now create the caller Twisted Web resource
            #
            return CallerResource(path_config.get('options', {}), caller_session)

        # File Upload resource
        #
        elif path_config['type'] == 'upload':

            upload_directory = os.path.abspath(os.path.join(self.config.extra.cbdir, path_config['directory']))
            upload_directory = upload_directory.encode('ascii', 'ignore')  # http://stackoverflow.com/a/20433918/884770
            if not os.path.isdir(upload_directory):
                emsg = "configured upload directory '{}' in file upload resource isn't a directory".format(upload_directory)
                self.log.error(emsg)
                raise ApplicationError(u"crossbar.error.invalid_configuration", emsg)

            if 'temp_directory' in path_config:
                temp_directory = os.path.abspath(os.path.join(self.config.extra.cbdir, path_config['temp_directory']))
                temp_directory = temp_directory.encode('ascii', 'ignore')  # http://stackoverflow.com/a/20433918/884770
            else:
                temp_directory = os.path.abspath(tempfile.gettempdir())
                temp_directory = os.path.join(temp_directory, 'crossbar-uploads')
                if not os.path.exists(temp_directory):
                    os.makedirs(temp_directory)

            if not os.path.isdir(temp_directory):
                emsg = "configured temp directory '{}' in file upload resource isn't a directory".format(temp_directory)
                self.log.error(emsg)
                raise ApplicationError(u"crossbar.error.invalid_configuration", emsg)

            # file upload progress and finish events are published via this session
            #
            upload_session_config = ComponentConfig(realm=path_config['realm'], extra=None)
            upload_session = ApplicationSession(upload_session_config)

            self._router_session_factory.add(upload_session, authrole=path_config.get('role', 'anonymous'))

            self.log.info("File upload resource started. Uploads to {upl} using temp folder {tmp}.", upl=upload_directory, tmp=temp_directory)

            return FileUploadResource(upload_directory, temp_directory, path_config['form_fields'], upload_session, path_config.get('options', {}))

        # Generic Twisted Web resource
        #
        elif path_config['type'] == 'resource':

            try:
                klassname = path_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)

                return make(path_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)

        # Schema Docs resource
        #
        elif path_config['type'] == 'schemadoc':

            realm = path_config['realm']

            if realm not in self.realm_to_id:
                raise ApplicationError(u"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

            return SchemaDocResource(self._templates, realm, realm_schemas)

        # Nested subpath resource
        #
        elif path_config['type'] == 'path':

            nested_paths = path_config.get('paths', {})

            if '/' in nested_paths:
                nested_resource = self.create_resource(nested_paths['/'])
            else:
                nested_resource = Resource()

            # nest subpaths under the current entry
            #
            self.add_paths(nested_resource, nested_paths)

            return nested_resource

        else:
            raise ApplicationError(u"crossbar.error.invalid_configuration", "invalid Web path type '{}'".format(path_config['type']))
Пример #2
0
    def create_resource(self, path_config):
        """
      Creates child resource to be added to the parent.

      :param path_config: Configuration for the new child resource.
      :type path_config: dict

      :returns: Resource -- the new child resource
      """
        ## 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()

            return WebSocketResource(ws_factory)

        ## 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)

            return 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:
                return wsgi_resource

        ## Redirecting resource
        ##
        elif path_config['type'] == 'redirect':
            redirect_url = path_config['url'].encode('ascii', 'ignore')
            return RedirectResource(redirect_url)

        ## JSON value resource
        ##
        elif path_config['type'] == 'json':
            value = path_config['value']

            return JsonResource(value)

        ## 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

            return CgiDirectory(cgi_directory, cgi_processor,
                                Resource404(self._templates, cgi_directory))

        ## 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

            return 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
            ##
            return PusherResource(path_config.get('options', {}),
                                  pusher_session)

        ## 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

            return SchemaDocResource(self._templates, realm, realm_schemas)

        ## Nested subpath resource
        ##
        elif path_config['type'] == 'path':

            nested_paths = path_config.get('paths', {})

            if '/' in nested_paths:
                nested_resource = self.create_resource(nested_paths['/'])
            else:
                nested_resource = Resource()

            ## nest subpaths under the current entry
            ##
            self.add_paths(nested_resource, nested_paths)

            return nested_resource

        else:
            raise ApplicationError(
                "crossbar.error.invalid_configuration",
                "invalid Web path type '{}'".format(path_config['type']))
Пример #3
0
    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:
                        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:
                            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(
                                path_config['resource'],
                                path_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)

            ## 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':

                        log.msg("Web path type 'longpoll' not implemented")

                    ## 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)

                        ## 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)

                    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