示例#1
0
    def test_renderIProtocol(self):
        """
        If the protocol returned by C{lookupProtocol} isn't a
        C{WebSocketsProtocol}, L{WebSocketsResource} wraps it automatically
        with L{WebSocketsProtocolWrapper}.
        """
        def lookupProtocol(names, otherRequest):
            return AccumulatingProtocol(), None

        self.resource = WebSocketsResource(lookupProtocol)

        request = DummyRequest(b"/")
        request.requestHeaders = Headers({
            b"user-agent": [b"user-agent"],
            b"host": [b"host"],
        })
        transport = StringTransportWithDisconnection()
        transport.protocol = Protocol()
        request.transport = transport
        self.update_headers(request,
                            headers={
                                b"upgrade": b"Websocket",
                                b"connection": b"Upgrade",
                                b"sec-websocket-key": b"secure",
                                b"sec-websocket-version": b"13"
                            })
        result = self.resource.render(request)
        self.assertEqual(NOT_DONE_YET, result)
        self.assertIsInstance(transport.protocol, WebSocketsProtocolWrapper)
        self.assertIsInstance(transport.protocol.wrappedProtocol,
                              AccumulatingProtocol)
示例#2
0
    def setUp(self):
        super(WebSocketsResourceTest, self).setUp()

        class SavingEchoFactory(Factory):
            def buildProtocol(oself, addr):
                return self.echoProtocol

        factory = SavingEchoFactory()
        self.echoProtocol = WebSocketsProtocol(SavingEchoReceiver())

        self.resource = WebSocketsResource(lookupProtocolForFactory(factory))
示例#3
0
    def test_renderProtocol(self):
        """
        If protocols are specified via the C{Sec-WebSocket-Protocol} header,
        L{WebSocketsResource} passes them to its C{lookupProtocol} argument,
        which can decide which protocol to return, and which is accepted.
        """

        def lookupProtocol(names, otherRequest):
            self.assertEqual([b"foo", b"bar"], names)
            self.assertIs(request, otherRequest)
            return self.echoProtocol, b"bar"

        self.resource = WebSocketsResource(lookupProtocol)

        request = DummyRequest(b"/")
        request.requestHeaders = Headers(
            {
                b"sec-websocket-protocol": [b"foo", b"bar"],
                b"user-agent": [b"user-agent"],
                b"host": [b"host"],
            }
        )
        transport = StringTransportWithDisconnection()
        transport.protocol = Protocol()
        request.transport = transport
        self.update_headers(
            request,
            headers={
                b"upgrade": b"Websocket",
                b"connection": b"Upgrade",
                b"sec-websocket-key": b"secure",
                b"sec-websocket-version": b"13",
            },
        )
        result = self.resource.render(request)
        self.assertEqual(NOT_DONE_YET, result)
        self.assertEqual(
            {
                b"Connection": [b"Upgrade"],
                b"Upgrade": [b"WebSocket"],
                b"Sec-Websocket-Protocol": [b"bar"],
                b"Sec-Websocket-Accept": [b"oYBv54i42V5dw6KnZqOFroecUTc="],
            },
            {
                name: value
                for name, value in request.responseHeaders.getAllRawHeaders()
            },
        )
        self.assertEqual([b""], request.written)
        self.assertEqual(101, request.responseCode)
示例#4
0
    def installApplication(self, application):
        """Install the WSGI application into the Twisted site.

        It's installed as a child with path "MAAS". This matches the default
        front-end configuration (i.e. Apache) so that there's no need to force
        script names.
        """
        # Setup resources to process paths that twisted handles.
        metadata = Resource()
        metadata.putChild(b'status', StatusHandlerResource(self.status_worker))

        maas = Resource()
        maas.putChild(b'metadata', metadata)
        maas.putChild(b'static', File(settings.STATIC_ROOT))
        maas.putChild(
            b'ws',
            WebSocketsResource(lookupProtocolForFactory(self.websocket)))

        root = Resource()
        root.putChild(b'', Redirect(b"MAAS/"))
        root.putChild(b'MAAS', maas)

        # Setup the resources to process paths that django handles.
        underlay_maas = ResourceOverlay(
            WSGIResource(reactor, self.threadpool, application))
        underlay_root = Resource()
        underlay_root.putChild(b'MAAS', underlay_maas)
        underlay_site = Site(underlay_root,
                             logFormatter=reducedWebLogFormatter)
        underlay_site.requestFactory = CleanPathRequest

        # Setup the main resource as the twisted handler and the underlay
        # resource as the django handler.
        self.site.resource = root
        self.site.underlay = underlay_site
示例#5
0
    def installApplication(self, application):
        """Install the WSGI application into the Twisted site.

        It's installed as a child with path "MAAS". This matches the default
        front-end configuration (i.e. Apache) so that there's no need to force
        script names.
        """
        # Setup resources to process paths that twisted handles.
        metadata = Resource()
        metadata.putChild(b"status", StatusHandlerResource(self.status_worker))

        maas = Resource()
        maas.putChild(b"metadata", metadata)
        maas.putChild(
            b"ws", WebSocketsResource(lookupProtocolForFactory(self.websocket))
        )

        # /MAAS/r/{path} and /MAAS/l/{path} are all resolved by the new MAAS UI
        # react app, and legacy angularjs app respectively.
        # If any paths do not match then its routed to index.html in the new
        # UI code as it uses HTML 5 routing.
        maas.putChild(b"r", DefaultFallbackFile(settings.STATIC_ROOT))

        maas.putChild(b"l", DefaultFallbackFile(settings.STATIC_ROOT))

        # Redirect /MAAS to react app
        maas.putChild(b"", Redirect(b"/MAAS/r/"))

        # Setup static resources
        maas.putChild(
            b"assets",
            NoListingFile(os.path.join(settings.STATIC_ROOT, "assets")),
        )

        # Setup static docs
        maas.putChild(
            b"docs",
            DocsFallbackFile(os.path.join(settings.STATIC_ROOT, "docs")),
        )

        root = Resource()
        root.putChild(b"", Redirect(b"MAAS/"))
        root.putChild(b"MAAS", maas)

        # Setup the resources to process paths that django handles.
        underlay_maas = ResourceOverlay(
            WSGIResource(reactor, self.threadpool, application)
        )
        underlay_root = Resource()
        underlay_root.putChild(b"MAAS", underlay_maas)
        underlay_site = Site(
            underlay_root, logFormatter=reducedWebLogFormatter
        )
        underlay_site.requestFactory = CleanPathRequest

        # Setup the main resource as the twisted handler and the underlay
        # resource as the django handler.
        self.site.resource = root
        self.site.underlay = underlay_site
示例#6
0
class WebSocketsResourceTest(MAASTestCase):
    """
    Tests for L{WebSocketsResource}.
    """
    def setUp(self):
        super(WebSocketsResourceTest, self).setUp()

        class SavingEchoFactory(Factory):
            def buildProtocol(oself, addr):
                return self.echoProtocol

        factory = SavingEchoFactory()
        self.echoProtocol = WebSocketsProtocol(SavingEchoReceiver())

        self.resource = WebSocketsResource(lookupProtocolForFactory(factory))

    def assertRequestFail(self, request):
        """
        Helper method checking that the provided C{request} fails with a I{400}
        request code, without data or headers.

        @param request: The request to render.
        @type request: L{DummyRequest}
        """
        result = self.resource.render(request)
        self.assertEqual(b"", result)
        self.assertEqual({}, {
            name: value
            for name, value in request.responseHeaders.getAllRawHeaders()
        })
        self.assertEqual([], request.written)
        self.assertEqual(400, request.responseCode)

    def update_headers(self, request, headers):
        for name, value in headers.items():
            request.requestHeaders.addRawHeader(name=name, value=value)

    def test_getChildWithDefault(self):
        """
        L{WebSocketsResource.getChildWithDefault} raises a C{RuntimeError} when
        called.
        """
        self.assertRaises(RuntimeError, self.resource.getChildWithDefault,
                          b"foo", DummyRequest(b"/"))

    def test_putChild(self):
        """
        L{WebSocketsResource.putChild} raises C{RuntimeError} when called.
        """
        self.assertRaises(RuntimeError, self.resource.putChild, b"foo",
                          Resource())

    def test_IResource(self):
        """
        L{WebSocketsResource} implements L{IResource}.
        """
        self.assertTrue(verifyObject(IResource, self.resource))

    def test_render(self):
        """
        When rendering a request, L{WebSocketsResource} uses the
        C{Sec-WebSocket-Key} header to generate a C{Sec-WebSocket-Accept}
        value. It creates a L{WebSocketsProtocol} instance connected to the
        protocol provided by the user factory.
        """
        request = DummyRequest(b"/")
        request.requestHeaders = Headers({
            b"user-agent": [b"user-agent"],
            b"host": [b"host"],
        })
        transport = StringTransportWithDisconnection()
        transport.protocol = Protocol()
        request.transport = transport
        self.update_headers(request,
                            headers={
                                b"upgrade": b"Websocket",
                                b"connection": b"Upgrade",
                                b"sec-websocket-key": b"secure",
                                b"sec-websocket-version": b"13"
                            })
        result = self.resource.render(request)
        self.assertEqual(NOT_DONE_YET, result)
        self.assertEqual(
            {
                b"Connection": [b"Upgrade"],
                b"Upgrade": [b"WebSocket"],
                b"Sec-Websocket-Accept": [b"oYBv54i42V5dw6KnZqOFroecUTc="]
            }, {
                name: value
                for name, value in request.responseHeaders.getAllRawHeaders()
            })
        self.assertEqual([b""], request.written)
        self.assertEqual(101, request.responseCode)
        self.assertIdentical(None, request.transport)
        self.assertIsInstance(transport.protocol._receiver, SavingEchoReceiver)
        self.assertEqual(request.getHeader(b"cookie"), transport.cookies)
        self.assertEqual(request.uri, transport.uri)

    def test_renderProtocol(self):
        """
        If protocols are specified via the C{Sec-WebSocket-Protocol} header,
        L{WebSocketsResource} passes them to its C{lookupProtocol} argument,
        which can decide which protocol to return, and which is accepted.
        """
        def lookupProtocol(names, otherRequest):
            self.assertEqual([b"foo", b"bar"], names)
            self.assertIdentical(request, otherRequest)
            return self.echoProtocol, b"bar"

        self.resource = WebSocketsResource(lookupProtocol)

        request = DummyRequest(b"/")
        request.requestHeaders = Headers({
            b"sec-websocket-protocol": [b"foo", b"bar"],
            b"user-agent": [b"user-agent"],
            b"host": [b"host"],
        })
        transport = StringTransportWithDisconnection()
        transport.protocol = Protocol()
        request.transport = transport
        self.update_headers(request,
                            headers={
                                b"upgrade": b"Websocket",
                                b"connection": b"Upgrade",
                                b"sec-websocket-key": b"secure",
                                b"sec-websocket-version": b"13"
                            })
        result = self.resource.render(request)
        self.assertEqual(NOT_DONE_YET, result)
        self.assertEqual(
            {
                b"Connection": [b"Upgrade"],
                b"Upgrade": [b"WebSocket"],
                b"Sec-Websocket-Protocol": [b"bar"],
                b"Sec-Websocket-Accept": [b"oYBv54i42V5dw6KnZqOFroecUTc="]
            }, {
                name: value
                for name, value in request.responseHeaders.getAllRawHeaders()
            })
        self.assertEqual([b""], request.written)
        self.assertEqual(101, request.responseCode)

    def test_renderWrongUpgrade(self):
        """
        If the C{Upgrade} header contains an invalid value,
        L{WebSocketsResource} returns a failed request.
        """
        request = DummyRequest(b"/")
        self.update_headers(request,
                            headers={
                                b"upgrade": b"wrong",
                                b"connection": b"Upgrade",
                                b"sec-websocket-key": b"secure",
                                b"sec-websocket-version": b"13"
                            })
        self.assertRequestFail(request)

    def test_renderNoUpgrade(self):
        """
        If the C{Upgrade} header is not set, L{WebSocketsResource} returns a
        failed request.
        """
        request = DummyRequest(b"/")
        self.update_headers(request,
                            headers={
                                b"connection": b"Upgrade",
                                b"sec-websocket-key": b"secure",
                                b"sec-websocket-version": b"13"
                            })
        self.assertRequestFail(request)

    def test_renderPOST(self):
        """
        If the method is not C{GET}, L{WebSocketsResource} returns a failed
        request.
        """
        request = DummyRequest(b"/")
        request.method = b"POST"
        self.update_headers(request,
                            headers={
                                b"upgrade": b"Websocket",
                                b"connection": b"Upgrade",
                                b"sec-websocket-key": b"secure",
                                b"sec-websocket-version": b"13"
                            })
        self.assertRequestFail(request)

    def test_renderWrongConnection(self):
        """
        If the C{Connection} header contains an invalid value,
        L{WebSocketsResource} returns a failed request.
        """
        request = DummyRequest(b"/")
        self.update_headers(request,
                            headers={
                                b"upgrade": b"Websocket",
                                b"connection": b"Wrong",
                                b"sec-websocket-key": b"secure",
                                b"sec-websocket-version": b"13"
                            })
        self.assertRequestFail(request)

    def test_renderNoConnection(self):
        """
        If the C{Connection} header is not set, L{WebSocketsResource} returns a
        failed request.
        """
        request = DummyRequest(b"/")
        self.update_headers(request,
                            headers={
                                b"upgrade": b"Websocket",
                                b"sec-websocket-key": b"secure",
                                b"sec-websocket-version": b"13"
                            })
        self.assertRequestFail(request)

    def test_renderNoKey(self):
        """
        If the C{Sec-WebSocket-Key} header is not set, L{WebSocketsResource}
        returns a failed request.
        """
        request = DummyRequest(b"/")
        self.update_headers(request,
                            headers={
                                b"upgrade": b"Websocket",
                                b"connection": b"Upgrade",
                                b"sec-websocket-version": b"13"
                            })
        self.assertRequestFail(request)

    def test_renderWrongVersion(self):
        """
        If the value of the C{Sec-WebSocket-Version} is not 13,
        L{WebSocketsResource} returns a failed request.
        """
        request = DummyRequest(b"/")

        self.update_headers(request,
                            headers={
                                b"upgrade": b"Websocket",
                                b"connection": b"Upgrade",
                                b"sec-websocket-key": b"secure",
                                b"sec-websocket-version": b"11"
                            })
        result = self.resource.render(request)
        self.assertEqual(b"", result)
        self.assertEqual(
            ['13'],
            request.responseHeaders.getRawHeaders("sec-websocket-version"))
        self.assertEqual([], request.written)
        self.assertEqual(400, request.responseCode)

    def test_renderNoProtocol(self):
        """
        If the underlying factory doesn't return any protocol,
        L{WebSocketsResource} returns a failed request with a C{502} code.
        """
        request = DummyRequest(b"/")
        request.requestHeaders = Headers({
            b"user-agent": [b"user-agent"],
            b"host": [b"host"],
        })
        request.transport = StringTransportWithDisconnection()
        self.echoProtocol = None
        self.update_headers(request,
                            headers={
                                b"upgrade": b"Websocket",
                                b"connection": b"Upgrade",
                                b"sec-websocket-key": b"secure",
                                b"sec-websocket-version": b"13"
                            })
        result = self.resource.render(request)
        self.assertEqual(b"", result)
        self.assertEqual({}, {
            name: value
            for name, value in request.responseHeaders.getAllRawHeaders()
        })
        self.assertEqual([], request.written)
        self.assertEqual(502, request.responseCode)

    def test_renderSecureRequest(self):
        """
        When the rendered request is over HTTPS, L{WebSocketsResource} wraps
        the protocol of the C{TLSMemoryBIOProtocol} instance.
        """
        request = DummyRequest(b"/")
        request.requestHeaders = Headers({
            b"user-agent": [b"user-agent"],
            b"host": [b"host"],
        })
        transport = StringTransportWithDisconnection()
        secureProtocol = TLSMemoryBIOProtocol(Factory(), Protocol())
        transport.protocol = secureProtocol
        request.transport = transport
        self.update_headers(request,
                            headers={
                                b"upgrade": b"Websocket",
                                b"connection": b"Upgrade",
                                b"sec-websocket-key": b"secure",
                                b"sec-websocket-version": b"13"
                            })
        result = self.resource.render(request)
        self.assertEqual(NOT_DONE_YET, result)
        self.assertEqual(
            {
                b"Connection": [b"Upgrade"],
                b"Upgrade": [b"WebSocket"],
                b"Sec-Websocket-Accept": [b"oYBv54i42V5dw6KnZqOFroecUTc="]
            }, {
                name: value
                for name, value in request.responseHeaders.getAllRawHeaders()
            })
        self.assertEqual([b""], request.written)
        self.assertEqual(101, request.responseCode)
        self.assertIdentical(None, request.transport)
        self.assertIsInstance(transport.protocol.wrappedProtocol,
                              WebSocketsProtocol)
        self.assertIsInstance(transport.protocol.wrappedProtocol._receiver,
                              SavingEchoReceiver)

    def test_renderRealRequest(self):
        """
        The request managed by L{WebSocketsResource.render} doesn't contain
        unnecessary HTTP headers like I{Content-Type}.
        """
        channel = DummyChannel()
        channel.transport = StringTransportWithDisconnection()
        channel.transport.protocol = channel
        request = Request(channel, False)
        headers = {
            b"upgrade": b"Websocket",
            b"connection": b"Upgrade",
            b"sec-websocket-key": b"secure",
            b"sec-websocket-version": b"13",
            b"user-agent": b"user-agent",
            b"client": b"client",
            b"host": b"host"
        }
        for key, value in headers.items():
            request.requestHeaders.setRawHeaders(key, [value])
        request.method = b"GET"
        request.clientproto = b"HTTP/1.1"
        request.client = IPv6Address('TCP', 'fe80::1', '80')
        result = self.resource.render(request)
        self.assertEqual(NOT_DONE_YET, result)
        self.assertEqual(
            [(b"Connection", [b"Upgrade"]),
             (b"Sec-Websocket-Accept", [b"oYBv54i42V5dw6KnZqOFroecUTc="]),
             (b"Upgrade", [b"WebSocket"])],
            sorted(request.responseHeaders.getAllRawHeaders()))
        self.assertThat(
            channel.transport.value(),
            StartsWith(b"HTTP/1.1 101 Switching Protocols\r\n"
                       b"Transfer-Encoding: chunked\r\n"))
        self.assertEqual(101, request.code)
        self.assertIdentical(None, request.transport)

    def test_renderIProtocol(self):
        """
        If the protocol returned by C{lookupProtocol} isn't a
        C{WebSocketsProtocol}, L{WebSocketsResource} wraps it automatically
        with L{WebSocketsProtocolWrapper}.
        """
        def lookupProtocol(names, otherRequest):
            return AccumulatingProtocol(), None

        self.resource = WebSocketsResource(lookupProtocol)

        request = DummyRequest(b"/")
        request.requestHeaders = Headers({
            b"user-agent": [b"user-agent"],
            b"host": [b"host"],
        })
        transport = StringTransportWithDisconnection()
        transport.protocol = Protocol()
        request.transport = transport
        self.update_headers(request,
                            headers={
                                b"upgrade": b"Websocket",
                                b"connection": b"Upgrade",
                                b"sec-websocket-key": b"secure",
                                b"sec-websocket-version": b"13"
                            })
        result = self.resource.render(request)
        self.assertEqual(NOT_DONE_YET, result)
        self.assertIsInstance(transport.protocol, WebSocketsProtocolWrapper)
        self.assertIsInstance(transport.protocol.wrappedProtocol,
                              AccumulatingProtocol)