예제 #1
0
    def test_webPathname(self):
        # running a t.web.distrib server over a UNIX socket
        if not IReactorUNIX.providedBy(reactor):
            raise unittest.SkipTest("UNIX sockets not supported here")
        config = base_config + "c['status'] = [html.WebStatus(distrib_port='.web-pb')]\n"
        os.mkdir("test_web2")
        self.master = m = ConfiguredMaster("test_web2", config)
        m.startService()

        p = DistribUNIX("test_web2/.web-pb")

        d = client.getPage("http://localhost:%d/remote/waterfall" % p.portnum)

        def _check(page):
            self.failUnless(page)

        d.addCallback(_check)

        def _done(res):
            d1 = p.shutdown()
            d1.addCallback(lambda x: res)
            return d1

        d.addBoth(_done)
        return d
예제 #2
0
    def test_webPathname(self):
        # running a t.web.distrib server over a UNIX socket
        if not IReactorUNIX.providedBy(reactor):
            raise unittest.SkipTest("UNIX sockets not supported here")
        config = (base_config +
                  "c['status'] = [html.WebStatus(distrib_port='.web-pb')]\n")
        os.mkdir("test_web2")
        self.master = m = ConfiguredMaster("test_web2", config)
        m.startService()

        p = DistribUNIX("test_web2/.web-pb")

        d = client.getPage("http://localhost:%d/remote/waterfall" % p.portnum)

        def _check(page):
            self.failUnless(page)

        d.addCallback(_check)

        def _done(res):
            d1 = p.shutdown()
            d1.addCallback(lambda x: res)
            return d1

        d.addBoth(_done)
        return d
예제 #3
0
class ServiceTests(TestCase):
    """
    Tests for the service creation APIs in L{twisted.web.tap}.
    """
    def _pathOption(self):
        """
        Helper for the I{--path} tests which creates a directory and creates
        an L{Options} object which uses that directory as its static
        filesystem root.

        @return: A two-tuple of a L{FilePath} referring to the directory and
            the value associated with the C{'root'} key in the L{Options}
            instance after parsing a I{--path} option.
        """
        path = FilePath(self.mktemp())
        path.makedirs()
        options = Options()
        options.parseOptions(['--path', path.path])
        root = options['root']
        return path, root

    def test_path(self):
        """
        The I{--path} option causes L{Options} to create a root resource
        which serves responses from the specified path.
        """
        path, root = self._pathOption()
        self.assertIsInstance(root, File)
        self.assertEqual(root.path, path.path)

    def test_pathServer(self):
        """
        The I{--path} option to L{makeService} causes it to return a service
        which will listen on the server address given by the I{--port} option.
        """
        path = FilePath(self.mktemp())
        path.makedirs()
        port = self.mktemp()
        options = Options()
        options.parseOptions(['--port', 'unix:' + port, '--path', path.path])
        service = makeService(options)
        service.startService()
        self.addCleanup(service.stopService)
        self.assertIsInstance(service.services[0].factory.resource, File)
        self.assertEqual(service.services[0].factory.resource.path, path.path)
        self.assertTrue(os.path.exists(port))
        self.assertTrue(stat.S_ISSOCK(os.stat(port).st_mode))

    if not IReactorUNIX.providedBy(reactor):
        test_pathServer.skip = (
            "The reactor does not support UNIX domain sockets")

    def test_cgiProcessor(self):
        """
        The I{--path} option creates a root resource which serves a
        L{CGIScript} instance for any child with the C{".cgi"} extension.
        """
        path, root = self._pathOption()
        path.child("foo.cgi").setContent(b"")
        self.assertIsInstance(root.getChild("foo.cgi", None), CGIScript)

    if _PY3:
        test_cgiProcessor.skip = (
            "Will be ported in https://twistedmatrix.com/trac/ticket/8009")

    def test_epyProcessor(self):
        """
        The I{--path} option creates a root resource which serves a
        L{PythonScript} instance for any child with the C{".epy"} extension.
        """
        path, root = self._pathOption()
        path.child("foo.epy").setContent(b"")
        self.assertIsInstance(root.getChild("foo.epy", None), PythonScript)

    def test_rpyProcessor(self):
        """
        The I{--path} option creates a root resource which serves the
        C{resource} global defined by the Python source in any child with
        the C{".rpy"} extension.
        """
        path, root = self._pathOption()
        path.child("foo.rpy").setContent(
            b"from twisted.web.static import Data\n"
            b"resource = Data('content', 'major/minor')\n")
        child = root.getChild("foo.rpy", None)
        self.assertIsInstance(child, Data)
        self.assertEqual(child.data, 'content')
        self.assertEqual(child.type, 'major/minor')

    def test_makePersonalServerFactory(self):
        """
        L{makePersonalServerFactory} returns a PB server factory which has
        as its root object a L{ResourcePublisher}.
        """
        # The fact that this pile of objects can actually be used somehow is
        # verified by twisted.web.test.test_distrib.
        site = Site(Data(b"foo bar", "text/plain"))
        serverFactory = makePersonalServerFactory(site)
        self.assertIsInstance(serverFactory, PBServerFactory)
        self.assertIsInstance(serverFactory.root, ResourcePublisher)
        self.assertIdentical(serverFactory.root.site, site)

    def test_personalServer(self):
        """
        The I{--personal} option to L{makeService} causes it to return a
        service which will listen on the server address given by the I{--port}
        option.
        """
        port = self.mktemp()
        options = Options()
        options.parseOptions(['--port', 'unix:' + port, '--personal'])
        service = makeService(options)
        service.startService()
        self.addCleanup(service.stopService)
        self.assertTrue(os.path.exists(port))
        self.assertTrue(stat.S_ISSOCK(os.stat(port).st_mode))

    if not IReactorUNIX.providedBy(reactor):
        test_personalServer.skip = (
            "The reactor does not support UNIX domain sockets")

    def test_defaultPersonalPath(self):
        """
        If the I{--port} option not specified but the I{--personal} option is,
        L{Options} defaults the port to C{UserDirectory.userSocketName} in the
        user's home directory.
        """
        options = Options()
        options.parseOptions(['--personal'])
        path = os.path.expanduser(
            os.path.join('~', UserDirectory.userSocketName))
        self.assertEqual(
            strports.parse(options['port'], None)[:2], ('UNIX', (path, None)))

    if not IReactorUNIX.providedBy(reactor):
        test_defaultPersonalPath.skip = (
            "The reactor does not support UNIX domain sockets")

    if _PY3:
        for i in [
                test_makePersonalServerFactory, test_personalServer,
                test_defaultPersonalPath
        ]:
            i.skip = (
                "Will be ported in https://twistedmatrix.com/trac/ticket/8010")
        del i

    def test_defaultPort(self):
        """
        If the I{--port} option is not specified, L{Options} defaults the port
        to C{8080}.
        """
        options = Options()
        options.parseOptions([])
        self.assertEqual(
            endpoints._parseServer(options['port'], None)[:2],
            ('TCP', (8080, None)))

    def test_wsgi(self):
        """
        The I{--wsgi} option takes the fully-qualifed Python name of a WSGI
        application object and creates a L{WSGIResource} at the root which
        serves that application.
        """
        options = Options()
        options.parseOptions(['--wsgi', __name__ + '.application'])
        root = options['root']
        self.assertTrue(root, WSGIResource)
        self.assertIdentical(root._reactor, reactor)
        self.assertTrue(isinstance(root._threadpool, ThreadPool))
        self.assertIdentical(root._application, application)

        # The threadpool should start and stop with the reactor.
        self.assertFalse(root._threadpool.started)
        reactor.fireSystemEvent('startup')
        self.assertTrue(root._threadpool.started)
        self.assertFalse(root._threadpool.joined)
        reactor.fireSystemEvent('shutdown')
        self.assertTrue(root._threadpool.joined)

    if _PY3:
        test_wsgi.skip = (
            "Will be ported in https://twistedmatrix.com/trac/ticket/7993")

    def test_invalidApplication(self):
        """
        If I{--wsgi} is given an invalid name, L{Options.parseOptions}
        raises L{UsageError}.
        """
        options = Options()
        for name in [__name__ + '.nosuchthing', 'foo.']:
            exc = self.assertRaises(UsageError, options.parseOptions,
                                    ['--wsgi', name])
            self.assertEqual(str(exc),
                             "No such WSGI application: %r" % (name, ))

    def test_HTTPSFailureOnMissingSSL(self):
        """
        An L{UsageError} is raised when C{https} is requested but there is no
        support for SSL.
        """
        options = Options()

        exception = self.assertRaises(UsageError, options.parseOptions,
                                      ['--https=443'])

        self.assertEqual('SSL support not installed', exception.args[0])

    if requireModule('OpenSSL.SSL') is not None:
        test_HTTPSFailureOnMissingSSL.skip = 'SSL module is available.'

    def test_HTTPSAcceptedOnAvailableSSL(self):
        """
        When SSL support is present, it accepts the --https option.
        """
        options = Options()

        options.parseOptions(['--https=443'])

        self.assertEqual('443', options['https'])

    if requireModule('OpenSSL.SSL') is None:
        test_HTTPSAcceptedOnAvailableSSL.skip = 'SSL module is not available.'
예제 #4
0
class ServiceTests(TestCase):
    """
    Tests for the service creation APIs in L{twisted.web.tap}.
    """
    def _pathOption(self):
        """
        Helper for the I{--path} tests which creates a directory and creates
        an L{Options} object which uses that directory as its static
        filesystem root.

        @return: A two-tuple of a L{FilePath} referring to the directory and
            the value associated with the C{'root'} key in the L{Options}
            instance after parsing a I{--path} option.
        """
        path = FilePath(self.mktemp())
        path.makedirs()
        options = Options()
        options.parseOptions(["--path", path.path])
        root = options["root"]
        return path, root

    def test_path(self):
        """
        The I{--path} option causes L{Options} to create a root resource
        which serves responses from the specified path.
        """
        path, root = self._pathOption()
        self.assertIsInstance(root, File)
        self.assertEqual(root.path, path.path)

    @skipIf(
        not IReactorUNIX.providedBy(reactor),
        "The reactor does not support UNIX domain sockets",
    )
    def test_pathServer(self):
        """
        The I{--path} option to L{makeService} causes it to return a service
        which will listen on the server address given by the I{--port} option.
        """
        path = FilePath(self.mktemp())
        path.makedirs()
        port = self.mktemp()
        options = Options()
        options.parseOptions(["--port", "unix:" + port, "--path", path.path])
        service = makeService(options)
        service.startService()
        self.addCleanup(service.stopService)
        self.assertIsInstance(service.services[0].factory.resource, File)
        self.assertEqual(service.services[0].factory.resource.path, path.path)
        self.assertTrue(os.path.exists(port))
        self.assertTrue(stat.S_ISSOCK(os.stat(port).st_mode))

    def test_cgiProcessor(self):
        """
        The I{--path} option creates a root resource which serves a
        L{CGIScript} instance for any child with the C{".cgi"} extension.
        """
        path, root = self._pathOption()
        path.child("foo.cgi").setContent(b"")
        self.assertIsInstance(root.getChild("foo.cgi", None), CGIScript)

    def test_epyProcessor(self):
        """
        The I{--path} option creates a root resource which serves a
        L{PythonScript} instance for any child with the C{".epy"} extension.
        """
        path, root = self._pathOption()
        path.child("foo.epy").setContent(b"")
        self.assertIsInstance(root.getChild("foo.epy", None), PythonScript)

    def test_rpyProcessor(self):
        """
        The I{--path} option creates a root resource which serves the
        C{resource} global defined by the Python source in any child with
        the C{".rpy"} extension.
        """
        path, root = self._pathOption()
        path.child("foo.rpy").setContent(
            b"from twisted.web.static import Data\n"
            b"resource = Data('content', 'major/minor')\n")
        child = root.getChild("foo.rpy", None)
        self.assertIsInstance(child, Data)
        self.assertEqual(child.data, "content")
        self.assertEqual(child.type, "major/minor")

    def test_makePersonalServerFactory(self):
        """
        L{makePersonalServerFactory} returns a PB server factory which has
        as its root object a L{ResourcePublisher}.
        """
        # The fact that this pile of objects can actually be used somehow is
        # verified by twisted.web.test.test_distrib.
        site = Site(Data(b"foo bar", "text/plain"))
        serverFactory = makePersonalServerFactory(site)
        self.assertIsInstance(serverFactory, PBServerFactory)
        self.assertIsInstance(serverFactory.root, ResourcePublisher)
        self.assertIdentical(serverFactory.root.site, site)

    @skipIf(
        not IReactorUNIX.providedBy(reactor),
        "The reactor does not support UNIX domain sockets",
    )
    def test_personalServer(self):
        """
        The I{--personal} option to L{makeService} causes it to return a
        service which will listen on the server address given by the I{--port}
        option.
        """
        port = self.mktemp()
        options = Options()
        options.parseOptions(["--port", "unix:" + port, "--personal"])
        service = makeService(options)
        service.startService()
        self.addCleanup(service.stopService)
        self.assertTrue(os.path.exists(port))
        self.assertTrue(stat.S_ISSOCK(os.stat(port).st_mode))

    @skipIf(
        not IReactorUNIX.providedBy(reactor),
        "The reactor does not support UNIX domain sockets",
    )
    def test_defaultPersonalPath(self):
        """
        If the I{--port} option not specified but the I{--personal} option is,
        L{Options} defaults the port to C{UserDirectory.userSocketName} in the
        user's home directory.
        """
        options = Options()
        options.parseOptions(["--personal"])
        path = os.path.expanduser(
            os.path.join("~", UserDirectory.userSocketName))
        self.assertEqual(options["ports"][0], f"unix:{path}")

    def test_defaultPort(self):
        """
        If the I{--port} option is not specified, L{Options} defaults the port
        to C{8080}.
        """
        options = Options()
        options.parseOptions([])
        self.assertEqual(
            endpoints._parseServer(options["ports"][0], None)[:2],
            ("TCP", (8080, None)))

    def test_twoPorts(self):
        """
        If the I{--http} option is given twice, there are two listeners
        """
        options = Options()
        options.parseOptions(["--listen", "tcp:8001", "--listen", "tcp:8002"])
        self.assertIn("8001", options["ports"][0])
        self.assertIn("8002", options["ports"][1])

    def test_wsgi(self):
        """
        The I{--wsgi} option takes the fully-qualifed Python name of a WSGI
        application object and creates a L{WSGIResource} at the root which
        serves that application.
        """
        options = Options()
        options.parseOptions(["--wsgi", __name__ + ".application"])
        root = options["root"]
        self.assertTrue(root, WSGIResource)
        self.assertIdentical(root._reactor, reactor)
        self.assertTrue(isinstance(root._threadpool, ThreadPool))
        self.assertIdentical(root._application, application)

        # The threadpool should start and stop with the reactor.
        self.assertFalse(root._threadpool.started)
        reactor.fireSystemEvent("startup")
        self.assertTrue(root._threadpool.started)
        self.assertFalse(root._threadpool.joined)
        reactor.fireSystemEvent("shutdown")
        self.assertTrue(root._threadpool.joined)

    def test_invalidApplication(self):
        """
        If I{--wsgi} is given an invalid name, L{Options.parseOptions}
        raises L{UsageError}.
        """
        options = Options()
        for name in [__name__ + ".nosuchthing", "foo."]:
            exc = self.assertRaises(UsageError, options.parseOptions,
                                    ["--wsgi", name])
            self.assertEqual(str(exc), f"No such WSGI application: {name!r}")

    @skipIf(
        requireModule("OpenSSL.SSL") is not None, "SSL module is available.")
    def test_HTTPSFailureOnMissingSSL(self):
        """
        An L{UsageError} is raised when C{https} is requested but there is no
        support for SSL.
        """
        options = Options()

        exception = self.assertRaises(UsageError, options.parseOptions,
                                      ["--https=443"])

        self.assertEqual("SSL support not installed", exception.args[0])

    @skipIf(
        requireModule("OpenSSL.SSL") is None, "SSL module is not available.")
    def test_HTTPSAcceptedOnAvailableSSL(self):
        """
        When SSL support is present, it accepts the --https option.
        """
        options = Options()

        options.parseOptions(["--https=443"])

        self.assertIn("ssl", options["ports"][0])
        self.assertIn("443", options["ports"][0])

    def test_add_header_parsing(self):
        """
        When --add-header is specific, the value is parsed.
        """
        options = Options()
        options.parseOptions(
            ["--add-header", "K1: V1", "--add-header", "K2: V2"])
        self.assertEqual(options["extraHeaders"], [("K1", "V1"), ("K2", "V2")])

    def test_add_header_resource(self):
        """
        When --add-header is specified, the resource is a composition that adds
        headers.
        """
        options = Options()
        options.parseOptions(
            ["--add-header", "K1: V1", "--add-header", "K2: V2"])
        service = makeService(options)
        resource = service.services[0].factory.resource
        self.assertIsInstance(resource, _AddHeadersResource)
        self.assertEqual(resource._headers, [("K1", "V1"), ("K2", "V2")])
        self.assertIsInstance(resource._originalResource, demo.Test)

    def test_noTracebacksDeprecation(self):
        """
        Passing --notracebacks is deprecated.
        """
        options = Options()
        options.parseOptions(["--notracebacks"])
        makeService(options)

        warnings = self.flushWarnings([self.test_noTracebacksDeprecation])
        self.assertEqual(warnings[0]["category"], DeprecationWarning)
        self.assertEqual(warnings[0]["message"],
                         "--notracebacks was deprecated in Twisted 19.7.0")
        self.assertEqual(len(warnings), 1)

    def test_displayTracebacks(self):
        """
        Passing --display-tracebacks will enable traceback rendering on the
        generated Site.
        """
        options = Options()
        options.parseOptions(["--display-tracebacks"])
        service = makeService(options)
        self.assertTrue(service.services[0].factory.displayTracebacks)

    def test_displayTracebacksNotGiven(self):
        """
        Not passing --display-tracebacks will leave traceback rendering on the
        generated Site off.
        """
        options = Options()
        options.parseOptions([])
        service = makeService(options)
        self.assertFalse(service.services[0].factory.displayTracebacks)
예제 #5
0
class ServiceTests(TestCase):
    """
    Tests for the service creation APIs in L{twisted.web.tap}.
    """
    def _pathOption(self):
        """
        Helper for the I{--path} tests which creates a directory and creates
        an L{Options} object which uses that directory as its static
        filesystem root.

        @return: A two-tuple of a L{FilePath} referring to the directory and
            the value associated with the C{'root'} key in the L{Options}
            instance after parsing a I{--path} option.
        """
        path = FilePath(self.mktemp())
        path.makedirs()
        options = Options()
        options.parseOptions(['--path', path.path])
        root = options['root']
        return path, root

    def test_path(self):
        """
        The I{--path} option causes L{Options} to create a root resource
        which serves responses from the specified path.
        """
        path, root = self._pathOption()
        self.assertIsInstance(root, File)
        self.assertEqual(root.path, path.path)

    def test_cgiProcessor(self):
        """
        The I{--path} option creates a root resource which serves a
        L{CGIScript} instance for any child with the C{".cgi"} extension.
        """
        path, root = self._pathOption()
        path.child("foo.cgi").setContent("")
        self.assertIsInstance(root.getChild("foo.cgi", None), CGIScript)

    def test_php3Processor(self):
        """
        The I{--path} option creates a root resource which serves a
        L{PHP3Script} instance for any child with the C{".php3"} extension.
        """
        path, root = self._pathOption()
        path.child("foo.php3").setContent("")
        self.assertIsInstance(root.getChild("foo.php3", None), PHP3Script)

    def test_phpProcessor(self):
        """
        The I{--path} option creates a root resource which serves a
        L{PHPScript} instance for any child with the C{".php"} extension.
        """
        path, root = self._pathOption()
        path.child("foo.php").setContent("")
        self.assertIsInstance(root.getChild("foo.php", None), PHPScript)

    def test_epyProcessor(self):
        """
        The I{--path} option creates a root resource which serves a
        L{PythonScript} instance for any child with the C{".epy"} extension.
        """
        path, root = self._pathOption()
        path.child("foo.epy").setContent("")
        self.assertIsInstance(root.getChild("foo.epy", None), PythonScript)

    def test_rpyProcessor(self):
        """
        The I{--path} option creates a root resource which serves the
        C{resource} global defined by the Python source in any child with
        the C{".rpy"} extension.
        """
        path, root = self._pathOption()
        path.child("foo.rpy").setContent(
            "from twisted.web.static import Data\n"
            "resource = Data('content', 'major/minor')\n")
        child = root.getChild("foo.rpy", None)
        self.assertIsInstance(child, Data)
        self.assertEqual(child.data, 'content')
        self.assertEqual(child.type, 'major/minor')

    def test_trpProcessor(self):
        """
        The I{--path} option creates a root resource which serves the
        pickled resource out of any child with the C{".rpy"} extension.
        """
        path, root = self._pathOption()
        path.child("foo.trp").setContent(pickle.dumps(Data("foo", "bar")))
        child = root.getChild("foo.trp", None)
        self.assertIsInstance(child, Data)
        self.assertEqual(child.data, 'foo')
        self.assertEqual(child.type, 'bar')

        warnings = self.flushWarnings()

        # If the trp module hadn't been imported before this test ran, there
        # will be two deprecation warnings; one for the module, one for the
        # function.  If the module has already been imported, the
        # module-scope deprecation won't be emitted again.
        if len(warnings) == 2:
            self.assertEqual(warnings[0]['category'], DeprecationWarning)
            self.assertEqual(
                warnings[0]['message'],
                "twisted.web.trp is deprecated as of Twisted 9.0.  Resource "
                "persistence is beyond the scope of Twisted Web.")
            warning = warnings[1]
        else:
            warning = warnings[0]

        self.assertEqual(warning['category'], DeprecationWarning)
        self.assertEqual(
            warning['message'],
            "twisted.web.trp.ResourceUnpickler is deprecated as of Twisted "
            "9.0.  Resource persistence is beyond the scope of Twisted Web.")

    def test_makePersonalServerFactory(self):
        """
        L{makePersonalServerFactory} returns a PB server factory which has
        as its root object a L{ResourcePublisher}.
        """
        # The fact that this pile of objects can actually be used somehow is
        # verified by twisted.web.test.test_distrib.
        site = Site(Data("foo bar", "text/plain"))
        serverFactory = makePersonalServerFactory(site)
        self.assertIsInstance(serverFactory, PBServerFactory)
        self.assertIsInstance(serverFactory.root, ResourcePublisher)
        self.assertIdentical(serverFactory.root.site, site)

    def test_personalServer(self):
        """
        The I{--personal} option to L{makeService} causes it to return a
        service which will listen on the server address given by the I{--port}
        option.
        """
        port = self.mktemp()
        options = Options()
        options.parseOptions(['--port', 'unix:' + port, '--personal'])
        service = makeService(options)
        service.startService()
        self.addCleanup(service.stopService)
        self.assertTrue(os.path.exists(port))
        self.assertTrue(stat.S_ISSOCK(os.stat(port).st_mode))

    if not IReactorUNIX.providedBy(reactor):
        test_personalServer.skip = (
            "The reactor does not support UNIX domain sockets")

    def test_defaultPersonalPath(self):
        """
        If the I{--port} option not specified but the I{--personal} option is,
        L{Options} defaults the port to C{UserDirectory.userSocketName} in the
        user's home directory.
        """
        options = Options()
        options.parseOptions(['--personal'])
        path = os.path.expanduser(
            os.path.join('~', UserDirectory.userSocketName))
        self.assertEqual(
            strports.parse(options['port'], None)[:2], ('UNIX', (path, None)))

    if not IReactorUNIX.providedBy(reactor):
        test_defaultPersonalPath.skip = (
            "The reactor does not support UNIX domain sockets")

    def test_defaultPort(self):
        """
        If the I{--port} option is not specified, L{Options} defaults the port
        to C{8080}.
        """
        options = Options()
        options.parseOptions([])
        self.assertEqual(
            strports.parse(options['port'], None)[:2], ('TCP', (8080, None)))

    def test_wsgi(self):
        """
        The I{--wsgi} option takes the fully-qualifed Python name of a WSGI
        application object and creates a L{WSGIResource} at the root which
        serves that application.
        """
        options = Options()
        options.parseOptions(['--wsgi', __name__ + '.application'])
        root = options['root']
        self.assertTrue(root, WSGIResource)
        self.assertIdentical(root._reactor, reactor)
        self.assertTrue(isinstance(root._threadpool, ThreadPool))
        self.assertIdentical(root._application, application)

        # The threadpool should start and stop with the reactor.
        self.assertFalse(root._threadpool.started)
        reactor.fireSystemEvent('startup')
        self.assertTrue(root._threadpool.started)
        self.assertFalse(root._threadpool.joined)
        reactor.fireSystemEvent('shutdown')
        self.assertTrue(root._threadpool.joined)

    def test_invalidApplication(self):
        """
        If I{--wsgi} is given an invalid name, L{Options.parseOptions}
        raises L{UsageError}.
        """
        options = Options()
        for name in [__name__ + '.nosuchthing', 'foo.']:
            exc = self.assertRaises(UsageError, options.parseOptions,
                                    ['--wsgi', name])
            self.assertEqual(str(exc),
                             "No such WSGI application: %r" % (name, ))
예제 #6
0
class ServiceTests(TestCase):
    """
    Tests for the service creation APIs in L{twisted.web.tap}.
    """
    def test_makePersonalServerFactory(self):
        """
        L{makePersonalServerFactory} returns a PB server factory which has
        as its root object a L{ResourcePublisher}.
        """
        # The fact that this pile of objects can actually be used somehow is
        # verified by twisted.web.test.test_distrib.
        site = Site(Data("foo bar", "text/plain"))
        serverFactory = makePersonalServerFactory(site)
        self.assertIsInstance(serverFactory, PBServerFactory)
        self.assertIsInstance(serverFactory.root, ResourcePublisher)
        self.assertIdentical(serverFactory.root.site, site)

    def test_personalServer(self):
        """
        The I{--personal} option to L{makeService} causes it to return a
        service which will listen on the server address given by the I{--port}
        option.
        """
        port = self.mktemp()
        options = Options()
        options.parseOptions(['--port', 'unix:' + port, '--personal'])
        service = makeService(options)
        service.startService()
        self.addCleanup(service.stopService)
        self.assertTrue(os.path.exists(port))
        self.assertTrue(stat.S_ISSOCK(os.stat(port).st_mode))

    if not IReactorUNIX.providedBy(reactor):
        test_personalServer.skip = (
            "The reactor does not support UNIX domain sockets")

    def test_defaultPersonalPath(self):
        """
        If the I{--port} option not specified but the I{--personal} option is,
        L{Options} defaults the port to C{UserDirectory.userSocketName} in the
        user's home directory.
        """
        options = Options()
        options.parseOptions(['--personal'])
        path = os.path.expanduser(
            os.path.join('~', UserDirectory.userSocketName))
        self.assertEqual(
            strports.parse(options['port'], None)[:2], ('UNIX', (path, None)))

    if not IReactorUNIX.providedBy(reactor):
        test_defaultPersonalPath.skip = (
            "The reactor does not support UNIX domain sockets")

    def test_defaultPort(self):
        """
        If the I{--port} option is not specified, L{Options} defaults the port
        to C{8080}.
        """
        options = Options()
        options.parseOptions([])
        self.assertEqual(
            strports.parse(options['port'], None)[:2], ('TCP', (8080, None)))

    def test_wsgi(self):
        """
        The I{--wsgi} option takes the fully-qualifed Python name of a WSGI
        application object and creates a L{WSGIResource} at the root which
        serves that application.
        """
        options = Options()
        options.parseOptions(['--wsgi', __name__ + '.application'])
        root = options['root']
        self.assertTrue(root, WSGIResource)
        self.assertIdentical(root._reactor, reactor)
        self.assertTrue(isinstance(root._threadpool, ThreadPool))
        self.assertIdentical(root._application, application)

        # The threadpool should start and stop with the reactor.
        self.assertFalse(root._threadpool.started)
        reactor.fireSystemEvent('startup')
        self.assertTrue(root._threadpool.started)
        self.assertFalse(root._threadpool.joined)
        reactor.fireSystemEvent('shutdown')
        self.assertTrue(root._threadpool.joined)

    def test_invalidApplication(self):
        """
        If I{--wsgi} is given an invalid name, L{Options.parseOptions}
        raises L{UsageError}.
        """
        options = Options()
        for name in [__name__ + '.nosuchthing', 'foo.']:
            exc = self.assertRaises(UsageError, options.parseOptions,
                                    ['--wsgi', name])
            self.assertEqual(str(exc),
                             "No such WSGI application: %r" % (name, ))
예제 #7
0
def server(globalOptions: GlobalOptions, anonoper: bool) -> None:
    """Run a Control Center server."""

    globalOptions.apply()

    # Set up Twisted's logging.
    from twisted.logger import globalLogBeginner, textFileLogObserver
    observers = [textFileLogObserver(sys.stderr)]
    globalLogBeginner.beginLoggingTo(observers)

    # This must happen after logging has been initialized.
    from softfab.TwistedRoot import SoftFabRoot
    from softfab.site import ControlCenter, ControlSocket, writePIDFile
    reactor = globalOptions.reactor
    root = SoftFabRoot(globalOptions.path, reactor, anonOperator=anonoper)

    import softfab.config
    site = ControlCenter(root)
    site.secureCookie = not softfab.config.rootURL.startswith('http://')
    site.displayTracebacks = globalOptions.debug

    from twisted.application import strports
    try:
        service = strports.service(softfab.config.endpointDesc, site)
    except ValueError as ex:
        echo(f"Invalid socket specification: {ex}", err=True)
        get_current_context().exit(1)

    try:
        service.startService()
    except Exception as ex:
        echo(f"Failed to listen on socket: {ex}", err=True)
        get_current_context().exit(1)
    else:
        reactor.addSystemEventTrigger('before', 'shutdown',
                                      service.stopService)

    pidfilePath = globalOptions.path / 'cc.pid'
    try:
        writePIDFile(pidfilePath)
    except OSError as ex:
        echo(f"Failed to create PID file '{pidfilePath}': {ex}", err=True)
        get_current_context().exit(1)

    from twisted.internet.interfaces import IReactorUNIX
    if IReactorUNIX.providedBy(reactor):
        socketPath = globalOptions.path / 'ctrl.sock'
        if socketPath.is_socket():
            # Remove stale socket.
            # TODO: Check it is actually stale.
            socketPath.unlink()
        reactor.listenUNIX(str(socketPath),
                           ControlSocket(root.apiRoot),
                           mode=0o600,
                           backlog=50,
                           wantPID=False)
    else:
        echo(
            "Reactor does not support UNIX sockets; "
            "control socket not available",
            err=True)

    from softfab.TwistedUtil import runCoroutine
    reactor.callWhenRunning(runCoroutine, reactor, root.startup())
    reactor.run()

    pidfilePath.unlink()