Esempio n. 1
0
    def test_broken_contenttype(self):
        """
        Crossbar rejects broken content-types.
        """
        session = MockPublisherSession(self)
        resource = PublisherResource({}, session)

        request = self.successResultOf(renderResource(
            resource, b"/", method=b"POST",
            headers={b"Content-Type": [b"application/json;charset=blarg;charset=boo"]},
            body=b'{"foo": "\xe2\x98\x83"}'))

        self.assertEqual(request.code, 400)
        self.assertEqual(
            b"mangled Content-Type header\n",
            request.getWrittenData())

        request = self.successResultOf(renderResource(
            resource, b"/", method=b"POST",
            headers={b"Content-Type": [b"charset=blarg;application/json"]},
            body=b'{"foo": "\xe2\x98\x83"}'))

        self.assertEqual(request.code, 400)
        self.assertEqual(
            b"bad or missing content type, should be 'application/json'\n",
            request.getWrittenData())
Esempio n. 2
0
    def test_broken_contenttype(self):
        """
        Crossbar rejects broken content-types.
        """
        session = MockPublisherSession(self)
        resource = PublisherResource({}, session)

        with LogCapturer("debug") as l:
            request = self.successResultOf(renderResource(
                resource, b"/", method=b"POST",
                headers={b"Content-Type": [b"application/json;charset=blarg;charset=boo"]},
                body=b'{"foo": "\xe2\x98\x83"}'))

        errors = l.get_category("AR450")
        self.assertEqual(len(errors), 1)
        self.assertEqual(errors[0]["code"], 400)

        del l

        with LogCapturer("debug") as l:
            request = self.successResultOf(renderResource(
                resource, b"/", method=b"POST",
                headers={b"Content-Type": [b"charset=blarg;application/json"]},
                body=b'{"foo": "\xe2\x98\x83"}'))

        self.assertEqual(request.code, 400)

        errors = l.get_category("AR452")
        self.assertEqual(len(errors), 1)
        self.assertEqual(errors[0]["code"], 400)
Esempio n. 3
0
    def test_add2(self):
        """
        Test a very basic call where you add two numbers together. This has two
        args, no kwargs, and no authorisation.
        """
        session = MockSession(self)
        session._addProcedureCall("com.test.add2",
                                  args=(1, 2),
                                  kwargs={},
                                  response=3)

        resource = CallerResource({}, session)

        with LogCapturer() as l:
            request = yield renderResource(
                resource, b"/",
                method=b"POST",
                headers={b"Content-Type": [b"application/json"]},
                body=b'{"procedure": "com.test.add2", "args": [1,2]}')

        self.assertEqual(request.code, 200)
        self.assertEqual(json.loads(native_string(request.get_written_data())),
                         {"args": [3]})

        logs = l.get_category("AR202")
        self.assertEqual(len(logs), 1)
        self.assertEqual(logs[0]["code"], 200)
Esempio n. 4
0
    def test_basic_publish(self):
        """
        Test a very basic publish to a topic.
        """
        session = MockPublisherSession(self)
        resource = PublisherResource({}, session)

        with LogCapturer() as l:
            request = yield renderResource(
                resource, b"/",
                method=b"POST",
                headers={b"Content-Type": [b"application/json"]},
                body=b'{"topic": "com.test.messages", "args": [1]}')

        self.assertEqual(len(session._published_messages), 1)
        self.assertEqual(session._published_messages[0]["args"], (1,))

        self.assertEqual(request.code, 200)

        logs = l.get_category("AR200")
        self.assertEqual(len(logs), 1)
        self.assertEqual(logs[0]["code"], 200)

        self.assertEqual(json.loads(native_string(request.get_written_data())),
                         {"id": session._published_messages[0]["id"]})
        # ensure we have all the format-keys AR200 asks for (can we
        # extract these from the _log_categories string instead?)
        self.assertIn('code', logs[0])
        self.assertIn('reason', logs[0])
Esempio n. 5
0
    def test_basic_publish(self):
        """
        Test a very basic publish to a topic.
        """
        session = MockPublisherSession(self)
        resource = PublisherResource({}, session)

        with LogCapturer() as l:
            request = yield renderResource(
                resource, b"/",
                method=b"POST",
                headers={b"Content-Type": [b"application/json"]},
                body=b'{"topic": "com.test.messages", "args": [1]}')

        self.assertEqual(len(session._published_messages), 1)
        self.assertEqual(session._published_messages[0]["args"], (1,))

        self.assertEqual(request.code, 200)

        logs = l.get_category("AR200")
        self.assertEqual(len(logs), 1)
        self.assertEqual(logs[0]["code"], 200)

        self.assertEqual(json.loads(native_string(request.get_written_data())),
                         {"id": session._published_messages[0]["id"]})
Esempio n. 6
0
    def test_basic(self):
        """
        A message, when a request has gone through to it, publishes a WAMP
        message on the configured topic.
        """
        session = MockPublisherSession(self)
        resource = WebhookResource({u"topic": u"com.test.webhook"}, session)

        with LogCapturer() as l:
            request = yield renderResource(
                resource, b"/", method=b"POST", headers={b"Content-Type": []}, body=b'{"foo": "has happened"}'
            )

        self.assertEqual(len(session._published_messages), 1)
        self.assertEqual(
            {
                u"body": u'{"foo": "has happened"}',
                u"headers": {
                    u"Content-Type": [],
                    u"Date": [u"Sun, 1 Jan 2013 15:21:01 GMT"],
                    u"Host": [u"localhost:8000"],
                },
            },
            session._published_messages[0]["args"][0],
        )

        self.assertEqual(request.code, 202)
        self.assertEqual(request.get_written_data(), b"OK")

        logs = l.get_category("AR201")
        self.assertEqual(len(logs), 1)
        self.assertEqual(logs[0]["code"], 202)
Esempio n. 7
0
    def test_basic(self):
        """
        A message, when a request has gone through to it, publishes a WAMP
        message on the configured topic.
        """
        session = MockPublisherSession(self)
        resource = WebhookResource({u"topic": u"com.test.webhook"}, session)

        request = yield renderResource(
            resource, b"/",
            method=b"POST",
            headers={b"Content-Type": []},
            body=b'{"foo": "has happened"}')

        self.assertEqual(len(session._published_messages), 1)
        self.assertEqual(
            {
                u"body": u'{"foo": "has happened"}',
                u"headers": {
                    u"Content-Type": [],
                    u'Date': [u'Sun, 1 Jan 2013 15:21:01 GMT'],
                    u'Host': [u'localhost:8000']
                }
            },
            session._published_messages[0]["args"][0])

        self.assertEqual(request.code, 202)
        self.assertEqual(native_string(request.get_written_data()),
                         "OK")
Esempio n. 8
0
    def test_publish_error(self):
        """
        A publish that errors will return the error to the client.
        """
        class RejectingPublisherSession(object):
            """
            A mock WAMP session.
            """
            def publish(self, topic, *args, **kwargs):
                return maybeDeferred(self._publish, topic, *args, **kwargs)

            def _publish(self, topic, *args, **kwargs):
                raise ApplicationError(u'wamp.error.not_authorized', foo="bar")

        session = RejectingPublisherSession()
        resource = PublisherResource({}, session)

        with LogCapturer() as l:
            request = yield renderResource(
                resource, b"/",
                method=b"POST",
                headers={b"Content-Type": [b"application/json"]},
                body=b'{"topic": "com.test.messages", "args": [1]}')

        self.assertEqual(request.code, 200)

        logs = l.get_category("AR456")
        self.assertEqual(len(logs), 1)
        self.assertEqual(logs[0]["code"], 200)

        self.assertEqual(json.loads(native_string(request.get_written_data())),
                         {"error": "wamp.error.not_authorized",
                          "args": [], "kwargs": {"foo": "bar"}})
Esempio n. 9
0
    def test_cb_failure(self):
        """
        Test that calls with no procedure in the request body are rejected.
        """
        resource = CallerResource({}, None)

        with LogCapturer() as l:
            request = yield renderResource(
                resource,
                b"/",
                method=b"POST",
                headers={b"Content-Type": [b"application/json"]},
                body=b'{"procedure": "foo"}',
            )

        self.assertEqual(request.code, 500)
        self.assertEqual(
            json.loads(native_string(request.get_written_data())),
            {
                "error": "wamp.error.runtime_error",
                "args": ["Sorry, Crossbar.io has encountered a problem."],
                "kwargs": {},
            },
        )

        errors = l.get_category("AR500")
        self.assertEqual(len(errors), 1)

        # We manually logged the errors; we can flush them from the log
        self.flushLoggedErrors()
Esempio n. 10
0
    def test_add2(self):
        """
        Test a very basic call where you square root a number. This has one
        arg, no kwargs, and no authorisation.
        """
        session = TestSession(types.ComponentConfig(u'realm1'))
        self.session_factory.add(session, authrole=u"test_role")

        session2 = ApplicationSession(types.ComponentConfig(u'realm1'))
        self.session_factory.add(session2, authrole=u"test_role")
        resource = CallerResource({}, session2)

        with LogCapturer() as l:
            request = yield renderResource(
                resource, b"/",
                method=b"POST",
                headers={b"Content-Type": [b"application/json"]},
                body=b'{"procedure": "com.myapp.sqrt", "args": [2]}')

        self.assertEqual(request.code, 200)
        self.assertEqual(json.loads(native_string(request.get_written_data())),
                         {"args": [1.4142135623730951]})

        logs = l.get_category("AR202")
        self.assertEqual(len(logs), 1)
        self.assertEqual(logs[0]["code"], 200)
Esempio n. 11
0
    def test_basic(self):
        """
        Upload a basic file using the FileUploadResource, in just a single chunk.
        """
        upload_dir = FilePath(self.mktemp())
        upload_dir.makedirs()
        temp_dir = FilePath(self.mktemp())
        temp_dir.makedirs()

        fields = {
            "file_name": "resumableFilename",
            "mime_type": "resumableType",
            "total_size": "resumableTotalSize",
            "chunk_number": "resumableChunkNumber",
            "chunk_size": "resumableChunkSize",
            "total_chunks": "resumableTotalChunks",
            "content": "file",
            "on_progress": "on_progress",
            "session": "session"
        }

        mock_session = Mock()

        resource = FileUploadResource(upload_dir.path, temp_dir.path, fields, mock_session)

        multipart_body = b"""-----------------------------478904261175205671481632460\r\nContent-Disposition: form-data; name="resumableChunkNumber"\r\n\r\n1\r\n-----------------------------478904261175205671481632460\r\nContent-Disposition: form-data; name="resumableChunkSize"\r\n\r\n1048576\r\n-----------------------------478904261175205671481632460\r\nContent-Disposition: form-data; name="resumableCurrentChunkSize"\r\n\r\n16\r\n-----------------------------478904261175205671481632460\r\nContent-Disposition: form-data; name="resumableTotalSize"\r\n\r\n16\r\n-----------------------------478904261175205671481632460\r\nContent-Disposition: form-data; name="resumableType"\r\n\r\ntext/plain\r\n-----------------------------478904261175205671481632460\r\nContent-Disposition: form-data; name="resumableIdentifier"\r\n\r\n16-examplefiletxt\r\n-----------------------------478904261175205671481632460\r\nContent-Disposition: form-data; name="resumableFilename"\r\n\r\nexamplefile.txt\r\n-----------------------------478904261175205671481632460\r\nContent-Disposition: form-data; name="resumableRelativePath"\r\n\r\nexamplefile.txt\r\n-----------------------------478904261175205671481632460\r\nContent-Disposition: form-data; name="resumableTotalChunks"\r\n\r\n1\r\n-----------------------------478904261175205671481632460\r\nContent-Disposition: form-data; name="on_progress"\r\n\r\ncom.example.upload.on_progress\r\n-----------------------------478904261175205671481632460\r\nContent-Disposition: form-data; name="session"\r\n\r\n6891276359801283\r\n-----------------------------478904261175205671481632460\r\nContent-Disposition: form-data; name="file"; filename="blob"\r\nContent-Type: application/octet-stream\r\n\r\nhello Crossbar!\n\r\n-----------------------------478904261175205671481632460--\r\n"""

        d = renderResource(
            resource, b"/", method="POST",
            headers={
                b"content-type": [b"multipart/form-data; boundary=---------------------------478904261175205671481632460"],
                b"Content-Length": [b"1678"]
            },
            body=multipart_body
        )

        res = self.successResultOf(d)
        res.setResponseCode.assert_called_once_with(200)

        self.assertEqual(len(mock_session.method_calls), 2)

        # Starting the upload
        self.assertEqual(mock_session.method_calls[0][1][0], u"com.example.upload.on_progress")
        self.assertEqual(mock_session.method_calls[0][1][1]["status"], "started")
        self.assertEqual(mock_session.method_calls[0][1][1]["id"], "examplefile.txt")

        # Upload complete
        self.assertEqual(mock_session.method_calls[1][1][0], u"com.example.upload.on_progress")
        self.assertEqual(mock_session.method_calls[1][1][1]["status"], "finished")
        self.assertEqual(mock_session.method_calls[1][1][1]["id"], "examplefile.txt")

        # Nothing in the temp dir, one file in the upload
        self.assertEqual(len(temp_dir.listdir()), 0)
        self.assertEqual(len(upload_dir.listdir()), 1)
        with upload_dir.child("examplefile.txt").open("rb") as f:
            self.assertEqual(f.read(), b"hello Crossbar!\n")
Esempio n. 12
0
    def test_no_body(self):
        """
        Test that calls with no body are rejected.
        """
        resource = CallerResource({}, None)

        request = yield renderResource(resource, b"/", method=b"POST", headers={b"Content-Type": [b"application/json"]})

        self.assertEqual(request.code, 400)
        self.assertIn(b"invalid request event - HTTP/POST body must be valid JSON: ", request.get_written_data())
Esempio n. 13
0
    def test_no_procedure(self):
        """
        Test that calls with no procedure in the request body are rejected.
        """
        resource = CallerResource({}, None)

        request = yield renderResource(
            resource, b"/", method=b"POST", headers={b"Content-Type": [b"application/json"]}, body=b"{}"
        )

        self.assertEqual(request.code, 400)
        self.assertEqual(b"invalid request event - missing 'procedure' in HTTP/POST body\n", request.get_written_data())
Esempio n. 14
0
    def test_required_tls_without_tls(self):
        """
        Required TLS, plus a request NOT over TLS, will deny the request.
        """
        session = MockPublisherSession(self)
        resource = PublisherResource({"require_tls": True}, session)

        request = yield renderResource(
            resource, b"/", method=b"POST",
            headers={b"Content-Type": [b"application/json"]},
            body=publishBody, isSecure=False)

        self.assertEqual(request.code, 400)
Esempio n. 15
0
    def test_required_tls_with_tls(self):
        """
        Required TLS, plus a request over TLS, will allow the request.
        """
        session = MockPublisherSession(self)
        resource = PublisherResource({"require_tls": True}, session)

        request = self.successResultOf(renderResource(
            resource, b"/", method=b"POST",
            headers={b"Content-Type": [b"application/json"]},
            body=publishBody, isSecure=True))

        self.assertEqual(request.code, 202)
Esempio n. 16
0
    def test_not_required_tls_with_tls(self):
        """
        A request over TLS even when not required, will allow the request.
        """
        session = MockPublisherSession(self)
        resource = PublisherResource({}, session)

        request = yield renderResource(
            resource, b"/", method=b"POST",
            headers={b"Content-Type": [b"application/json"]},
            body=publishBody, isSecure=True)

        self.assertEqual(request.code, 202)
Esempio n. 17
0
    def test_allowed_IP_range(self):
        """
        The client having an IP in an allowed address range allows the request.
        """
        session = MockPublisherSession(self)
        resource = PublisherResource({"require_ip": ["127.0.0.0/8"]}, session)

        request = self.successResultOf(renderResource(
            resource, b"/", method=b"POST",
            headers={b"Content-Type": [b"application/json"]},
            body=publishBody))

        self.assertEqual(request.code, 202)
Esempio n. 18
0
    def test_allowed_IP(self):
        """
        The client having an allowed IP address allows the request.
        """
        session = MockPublisherSession(self)
        resource = PublisherResource({"require_ip": ["127.0.0.1"]}, session)

        request = yield renderResource(
            resource, b"/", method=b"POST",
            headers={b"Content-Type": [b"application/json"]},
            body=publishBody)

        self.assertEqual(request.code, 202)
Esempio n. 19
0
    def test_failure(self):
        """
        A failed call returns the error to the client.
        """
        session = TestSession(types.ComponentConfig(u"realm1"))
        self.session_factory.add(session, authrole=u"test_role")

        session2 = ApplicationSession(types.ComponentConfig(u"realm1"))
        self.session_factory.add(session2, authrole=u"test_role")
        resource = CallerResource({}, session2)

        tests = [
            (
                u"com.myapp.sqrt",
                (0,),
                {u"error": u"wamp.error.runtime_error", u"args": [u"don't ask foolish questions ;)"], u"kwargs": {}},
            ),
            (u"com.myapp.checkname", ("foo",), {u"error": u"com.myapp.error.reserved", u"args": [], u"kwargs": {}}),
            (
                u"com.myapp.checkname",
                ("*",),
                {u"error": u"com.myapp.error.invalid_length", u"args": [], u"kwargs": {"min": 3, "max": 10}},
            ),
            (
                u"com.myapp.checkname",
                ("hello",),
                {u"error": u"com.myapp.error.mixed_case", u"args": ["hello", "HELLO"], u"kwargs": {}},
            ),
            (u"com.myapp.compare", (1, 10), {u"error": u"com.myapp.error1", u"args": [9], u"kwargs": {}}),
        ]

        for procedure, args, err in tests:
            with LogCapturer() as l:
                request = yield renderResource(
                    resource,
                    b"/",
                    method=b"POST",
                    headers={b"Content-Type": [b"application/json"]},
                    body=dump_json({"procedure": procedure, "args": args}).encode("utf8"),
                )

            self.assertEqual(request.code, 200)
            self.assertEqual(json.loads(native_string(request.get_written_data())), err)

            logs = l.get_category("AR458")
            self.assertEqual(len(logs), 1)
            self.assertEqual(logs[0]["code"], 200)

        # We manually logged the errors; we can flush them from the log
        self.flushLoggedErrors()
Esempio n. 20
0
    def test_remains_cleanup(self):
        """
        Upload a basic file using the FileUploadResource, in a single chunk on top of an old upload.
        """

        upload_dir = FilePath(self.mktemp())
        upload_dir.makedirs()
        temp_dir = FilePath(self.mktemp())
        temp_dir.makedirs()
        # create remaining file temp dir of a previous upload
        x = temp_dir.child("examplefile.txt")
        x.makedirs()

        fields = {
            "file_name": "resumableFilename",
            "mime_type": "resumableType",
            "total_size": "resumableTotalSize",
            "chunk_number": "resumableChunkNumber",
            "chunk_size": "resumableChunkSize",
            "total_chunks": "resumableTotalChunks",
            "content": "file",
            "on_progress": "on_progress",
            "session": "session",
        }

        mock_session = Mock()

        resource = FileUploadResource(upload_dir.path, temp_dir.path, fields, mock_session)

        multipart_body = b"""-----------------------------478904261175205671481632460\r\nContent-Disposition: form-data; name="resumableChunkNumber"\r\n\r\n1\r\n-----------------------------478904261175205671481632460\r\nContent-Disposition: form-data; name="resumableChunkSize"\r\n\r\n1048576\r\n-----------------------------478904261175205671481632460\r\nContent-Disposition: form-data; name="resumableCurrentChunkSize"\r\n\r\n16\r\n-----------------------------478904261175205671481632460\r\nContent-Disposition: form-data; name="resumableTotalSize"\r\n\r\n16\r\n-----------------------------478904261175205671481632460\r\nContent-Disposition: form-data; name="resumableType"\r\n\r\ntext/plain\r\n-----------------------------478904261175205671481632460\r\nContent-Disposition: form-data; name="resumableIdentifier"\r\n\r\n16-examplefiletxt\r\n-----------------------------478904261175205671481632460\r\nContent-Disposition: form-data; name="resumableFilename"\r\n\r\nexamplefile.txt\r\n-----------------------------478904261175205671481632460\r\nContent-Disposition: form-data; name="resumableRelativePath"\r\n\r\nexamplefile.txt\r\n-----------------------------478904261175205671481632460\r\nContent-Disposition: form-data; name="resumableTotalChunks"\r\n\r\n1\r\n-----------------------------478904261175205671481632460\r\nContent-Disposition: form-data; name="on_progress"\r\n\r\ncom.example.upload.on_progress\r\n-----------------------------478904261175205671481632460\r\nContent-Disposition: form-data; name="session"\r\n\r\n6891276359801283\r\n-----------------------------478904261175205671481632460\r\nContent-Disposition: form-data; name="file"; filename="blob"\r\nContent-Type: application/octet-stream\r\n\r\nhello Crossbar!\n\r\n-----------------------------478904261175205671481632460--\r\n"""

        d = renderResource(
            resource,
            b"/",
            method="POST",
            headers={
                b"content-type": [
                    b"multipart/form-data; boundary=---------------------------478904261175205671481632460"
                ],
                b"Content-Length": [b"1678"],
            },
            body=multipart_body,
        )

        res = self.successResultOf(d)
        self.assertEqual(res.code, 200)

        with upload_dir.child("examplefile.txt").open("rb") as f:
            self.assertEqual(f.read(), b"hello Crossbar!\n")
Esempio n. 21
0
    def test_UTF8_assumption(self):
        """
        A body, when the Content-Type has no charset, is assumed to be UTF-8.
        """
        session = MockPublisherSession(self)
        resource = PublisherResource({}, session)

        request = self.successResultOf(renderResource(
            resource, b"/", method=b"POST",
            headers={b"Content-Type": [b"application/json"]},
            body=b'{"topic": "com.test.messages", "args": ["\xe2\x98\x83"]}'))

        self.assertEqual(request.code, 202)
        self.assertIn(b'{"id":',
                      request.get_written_data())
Esempio n. 22
0
    def test_allow_charset_in_content_type(self):
        """
        A charset in the content-type will be allowed.
        """
        session = MockPublisherSession(self)
        resource = PublisherResource({}, session)

        request = self.successResultOf(renderResource(
            resource, b"/", method=b"POST",
            headers={b"Content-Type": [b"application/json; charset=utf-8"]},
            body=publishBody))

        self.assertEqual(request.code, 202)
        self.assertIn(b'{"id":',
                      request.get_written_data())
Esempio n. 23
0
    def test_empty_content_type(self):
        """
        A request lacking a content-type header will be rejected.
        """
        session = MockPublisherSession(self)
        resource = PublisherResource({}, session)

        request = self.successResultOf(renderResource(
            resource, b"/", method=b"POST", headers={},
            body=publishBody))

        self.assertEqual(request.code, 400)
        self.assertEqual((b"bad or missing content type (''), "
                          b"should be 'application/json'\n"),
                         request.getWrittenData())
Esempio n. 24
0
    def test_bad_content_type(self):
        """
        An incorrect content type will mean the request is rejected.
        """
        session = MockPublisherSession(self)
        resource = PublisherResource({}, session)

        request = self.successResultOf(renderResource(
            resource, b"/", method=b"POST",
            headers={b"Content-Type": [b"application/text"]},
            body=publishBody))

        self.assertEqual(request.code, 400)
        self.assertIn(b"bad or missing content type ('application/text')",
                      request.getWrittenData())
Esempio n. 25
0
    def test_bad_method(self):
        """
        An incorrect method will mean the request is rejected.
        """
        session = MockPublisherSession(self)
        resource = PublisherResource({}, session)

        request = self.successResultOf(renderResource(
            resource, b"/", method=b"PUT",
            headers={b"Content-Type": [b"application/json"]},
            body=publishBody))

        self.assertEqual(request.code, 405)
        self.assertIn(b"HTTP/PUT not allowed",
                      request.getWrittenData())
Esempio n. 26
0
    def test_too_large_body(self):
        """
        A too large body will mean the request is rejected.
        """
        session = MockPublisherSession(self)
        resource = PublisherResource({"post_body_limit": 1}, session)

        request = self.successResultOf(renderResource(
            resource, b"/", method=b"POST",
            headers={b"Content-Type": [b"application/json"]},
            body=publishBody))

        self.assertEqual(request.code, 400)
        self.assertIn("HTTP/POST body length ({}) exceeds maximum ({})".format(len(publishBody), 1),
                      nativeString(request.getWrittenData()))
Esempio n. 27
0
    def test_JSON_list_body(self):
        """
        A body that is not a JSON dict will be rejected by the server.
        """
        session = MockPublisherSession(self)
        resource = PublisherResource({}, session)

        request = self.successResultOf(renderResource(
            resource, b"/", method=b"POST",
            headers={b"Content-Type": [b"application/json"]},
            body=b"[{},{}]"))

        self.assertEqual(request.code, 400)
        self.assertIn(b"invalid request event - HTTP/POST body must be JSON dict",
                      request.getWrittenData())
Esempio n. 28
0
    def test_invalid_JSON_body(self):
        """
        A body that is not valid JSON will be rejected by the server.
        """
        session = MockPublisherSession(self)
        resource = PublisherResource({}, session)

        request = yield renderResource(
            resource, b"/", method=b"POST",
            headers={b"Content-Type": [b"application/json"]},
            body=b"sometext")

        self.assertEqual(request.code, 400)
        self.assertIn(b"invalid request event - HTTP/POST body must be valid JSON:",
                      request.getWrittenData())
Esempio n. 29
0
    def test_allow_caps_in_content_type(self):
        """
        Differently-capitalised content-type headers will be allowed.
        """
        session = MockPublisherSession(self)
        resource = PublisherResource({}, session)

        request = self.successResultOf(
            renderResource(
                resource, b"/", method=b"POST", headers={b"CONTENT-TYPE": [b"APPLICATION/JSON"]}, body=publishBody
            )
        )

        self.assertEqual(request.code, 202)
        self.assertIn(b'{"id":', request.get_written_data())
Esempio n. 30
0
    def test_disallowed_IP_range(self):
        """
        The client having an IP not in allowed address range denies the request.
        """
        session = MockPublisherSession(self)
        resource = PublisherResource({"require_ip": ["192.168.0.0/16", "10.0.0.0/8"]}, session)

        request = self.successResultOf(
            renderResource(
                resource, b"/", method=b"POST", headers={b"Content-Type": [b"application/json"]}, body=publishBody
            )
        )

        self.assertEqual(request.code, 400)
        self.assertIn(b"request denied based on IP address", request.get_written_data())
Esempio n. 31
0
    def test_multichunk(self):
        """
        Uploading files that are in multiple chunks works.
        """
        upload_dir = FilePath(self.mktemp())
        upload_dir.makedirs()
        temp_dir = FilePath(self.mktemp())
        temp_dir.makedirs()

        fields = {
            "file_name": "resumableFilename",
            "mime_type": "resumableType",
            "total_size": "resumableTotalSize",
            "chunk_number": "resumableChunkNumber",
            "chunk_size": "resumableChunkSize",
            "total_chunks": "resumableTotalChunks",
            "content": "file",
            "on_progress": "on_progress",
            "session": "session"
        }

        mock_session = Mock()

        resource = FileUploadResource(upload_dir.path, temp_dir.path, fields, mock_session)

        #
        # Chunk 1

        mp = Multipart()
        mp.add_part(b"resumableChunkNumber", b"1")
        mp.add_part(b"resumableChunkSize", b"10")
        mp.add_part(b"resumableCurrentChunkSize", b"10")
        mp.add_part(b"resumableTotalSize", b"16")
        mp.add_part(b"resumableType", b"text/plain")
        mp.add_part(b"resumableIdentifier", b"16-examplefiletxt")
        mp.add_part(b"resumableFilename", b"examplefile.txt")
        mp.add_part(b"resumableRelativePath", b"examplefile.txt")
        mp.add_part(b"resumableTotalChunks", b"2")
        mp.add_part(b"on_progress", b"com.example.upload.on_progress")
        mp.add_part(b"session", b"6891276359801283")
        mp.add_part(b"file", b"hello Cros",
                    content_type=b"application/octet-stream",
                    filename=b"blob")
        body, headers = mp.render()

        d = renderResource(
            resource, b"/", method="POST",
            headers=headers,
            body=body
        )

        res = self.successResultOf(d)
        self.assertEqual(res.code, 200)

        # One directory in the temp dir, nothing in the upload dir, temp dir
        # contains one chunk
        self.assertEqual(len(temp_dir.listdir()), 1)
        self.assertEqual(len(temp_dir.child("examplefile.txt").listdir()), 1)
        with temp_dir.child("examplefile.txt").child("chunk_1").open("rb") as f:
            self.assertEqual(f.read(), b"hello Cros")
        self.assertEqual(len(upload_dir.listdir()), 0)

        #
        # Chunk 2

        mp = Multipart()
        mp.add_part(b"resumableChunkNumber", b"2")
        mp.add_part(b"resumableChunkSize", b"10")
        mp.add_part(b"resumableCurrentChunkSize", b"6")
        mp.add_part(b"resumableTotalSize", b"16")
        mp.add_part(b"resumableType", b"text/plain")
        mp.add_part(b"resumableIdentifier", b"16-examplefiletxt")
        mp.add_part(b"resumableFilename", b"examplefile.txt")
        mp.add_part(b"resumableRelativePath", b"examplefile.txt")
        mp.add_part(b"resumableTotalChunks", b"2")
        mp.add_part(b"on_progress", b"com.example.upload.on_progress")
        mp.add_part(b"session", b"6891276359801283")
        mp.add_part(b"file", b"sbar!\n",
                    content_type=b"application/octet-stream",
                    filename=b"blob")
        body, headers = mp.render()

        d = renderResource(
            resource, b"/", method="POST",
            headers=headers,
            body=body
        )

        res = self.successResultOf(d)
        self.assertEqual(res.code, 200)

        self.assertEqual(len(mock_session.method_calls), 4)

        # Starting the upload
        self.assertEqual(mock_session.method_calls[0][1][0], u"com.example.upload.on_progress")
        self.assertEqual(mock_session.method_calls[0][1][1]["status"], "started")
        self.assertEqual(mock_session.method_calls[0][1][1]["id"], "examplefile.txt")
        self.assertEqual(mock_session.method_calls[0][1][1]["chunk"], 1)

        # Progress, first chunk done
        self.assertEqual(mock_session.method_calls[1][1][0], u"com.example.upload.on_progress")
        self.assertEqual(mock_session.method_calls[1][1][1]["status"], "progress")
        self.assertEqual(mock_session.method_calls[1][1][1]["id"], "examplefile.txt")
        self.assertEqual(mock_session.method_calls[1][1][1]["chunk"], 1)

        # Progress, second chunk done
        self.assertEqual(mock_session.method_calls[2][1][0], u"com.example.upload.on_progress")
        self.assertEqual(mock_session.method_calls[2][1][1]["status"], "progress")
        self.assertEqual(mock_session.method_calls[2][1][1]["id"], "examplefile.txt")
        self.assertEqual(mock_session.method_calls[2][1][1]["chunk"], 2)

        # Upload complete
        self.assertEqual(mock_session.method_calls[3][1][0], u"com.example.upload.on_progress")
        self.assertEqual(mock_session.method_calls[3][1][1]["status"], "finished")
        self.assertEqual(mock_session.method_calls[3][1][1]["id"], "examplefile.txt")
        self.assertEqual(mock_session.method_calls[3][1][1]["chunk"], 2)

        # Nothing in the temp dir, one file in the upload
        self.assertEqual(len(temp_dir.listdir()), 0)
        self.assertEqual(len(upload_dir.listdir()), 1)
        with upload_dir.child("examplefile.txt").open("rb") as f:
            self.assertEqual(f.read(), b"hello Crossbar!\n")
Esempio n. 32
0
    def test_failure(self):
        """
        A failed call returns the error to the client.
        """
        session = TestSession(types.ComponentConfig(u'realm1'))
        self.session_factory.add(session, authrole=u"test_role")

        session2 = ApplicationSession(types.ComponentConfig(u'realm1'))
        self.session_factory.add(session2, authrole=u"test_role")
        resource = CallerResource({}, session2)

        tests = [
            (u"com.myapp.sqrt", (0, ), {
                u"error": u"wamp.error.runtime_error",
                u"args": [u"don't ask foolish questions ;)"],
                u"kwargs": {}
            }),
            (u"com.myapp.checkname", ("foo", ), {
                u"error": u"com.myapp.error.reserved",
                u"args": [],
                u"kwargs": {}
            }),
            (u"com.myapp.checkname", ("*", ), {
                u"error": u"com.myapp.error.invalid_length",
                u"args": [],
                u"kwargs": {
                    "min": 3,
                    "max": 10
                }
            }),
            (u"com.myapp.checkname", ("hello", ), {
                u"error": u"com.myapp.error.mixed_case",
                u"args": ["hello", "HELLO"],
                u"kwargs": {}
            }),
            (u"com.myapp.compare", (1, 10), {
                u"error": u"com.myapp.error1",
                u"args": [9],
                u"kwargs": {}
            }),
        ]

        for procedure, args, err in tests:
            with LogCapturer() as l:
                request = yield renderResource(
                    resource,
                    b"/",
                    method=b"POST",
                    headers={b"Content-Type": [b"application/json"]},
                    body=dump_json({
                        "procedure": procedure,
                        "args": args
                    }).encode('utf8'))

            self.assertEqual(request.code, 200)
            self.assertEqual(
                json.loads(native_string(request.get_written_data())), err)

            logs = l.get_category("AR458")
            self.assertEqual(len(logs), 1)
            self.assertEqual(logs[0]["code"], 200)

        # We manually logged the errors; we can flush them from the log
        self.flushLoggedErrors()
Esempio n. 33
0
    def test_resumed_upload(self):
        """
        Uploading part of a file, simulating a Crossbar restart, and continuing
        to upload works.
        """

        upload_dir = FilePath(self.mktemp())
        upload_dir.makedirs()
        temp_dir = FilePath(self.mktemp())
        temp_dir.makedirs()

        fields = {
            "file_name": "resumableFilename",
            "mime_type": "resumableType",
            "total_size": "resumableTotalSize",
            "chunk_number": "resumableChunkNumber",
            "chunk_size": "resumableChunkSize",
            "total_chunks": "resumableTotalChunks",
            "content": "file",
            "on_progress": "on_progress",
            "session": "session"
        }

        mock_session = Mock()

        resource = FileUploadResource(upload_dir.path, temp_dir.path, fields, mock_session)

        # Make some stuff in the temp dir that wasn't there when it started but
        # is put there before a file upload
        temp_dir.child("otherjunk").makedirs()

        #
        # Chunk 1

        mp = Multipart()
        mp.add_part(b"resumableChunkNumber", b"1")
        mp.add_part(b"resumableChunkSize", b"10")
        mp.add_part(b"resumableCurrentChunkSize", b"10")
        mp.add_part(b"resumableTotalSize", b"16")
        mp.add_part(b"resumableType", b"text/plain")
        mp.add_part(b"resumableIdentifier", b"16-examplefiletxt")
        mp.add_part(b"resumableFilename", b"examplefile.txt")
        mp.add_part(b"resumableRelativePath", b"examplefile.txt")
        mp.add_part(b"resumableTotalChunks", b"2")
        mp.add_part(b"on_progress", b"com.example.upload.on_progress")
        mp.add_part(b"session", b"6891276359801283")
        mp.add_part(b"file", b"hello Cros",
                    content_type=b"application/octet-stream",
                    filename=b"blob")
        body, headers = mp.render()

        d = renderResource(
            resource, b"/", method="POST",
            headers=headers,
            body=body
        )

        res = self.successResultOf(d)
        self.assertEqual(res.code, 200)

        # One directory in the temp dir, nothing in the upload dir, temp dir
        # contains one chunk
        self.assertEqual(len(temp_dir.listdir()), 1)
        self.assertEqual(len(temp_dir.child("examplefile.txt").listdir()), 1)
        with temp_dir.child("examplefile.txt").child("chunk_1").open("rb") as f:
            self.assertEqual(f.read(), b"hello Cros")
        self.assertEqual(len(upload_dir.listdir()), 0)

        del resource

        # Add some random junk in there that Crossbar isn't expecting
        temp_dir.child("junk").setContent(b"just some junk")
        temp_dir.child("examplefile.txt").child("hi").setContent(b"what")

        # Simulate restarting Crossbar by reinitialising the FileUploadResource
        resource = FileUploadResource(upload_dir.path, temp_dir.path, fields, mock_session)

        #
        # Chunk 2

        mp = Multipart()
        mp.add_part(b"resumableChunkNumber", b"2")
        mp.add_part(b"resumableChunkSize", b"10")
        mp.add_part(b"resumableCurrentChunkSize", b"6")
        mp.add_part(b"resumableTotalSize", b"16")
        mp.add_part(b"resumableType", b"text/plain")
        mp.add_part(b"resumableIdentifier", b"16-examplefiletxt")
        mp.add_part(b"resumableFilename", b"examplefile.txt")
        mp.add_part(b"resumableRelativePath", b"examplefile.txt")
        mp.add_part(b"resumableTotalChunks", b"2")
        mp.add_part(b"on_progress", b"com.example.upload.on_progress")
        mp.add_part(b"session", b"6891276359801283")
        mp.add_part(b"file", b"sbar!\n",
                    content_type=b"application/octet-stream",
                    filename=b"blob")
        body, headers = mp.render()

        d = renderResource(
            resource, b"/", method="POST",
            headers=headers,
            body=body
        )

        res = self.successResultOf(d)
        self.assertEqual(res.code, 200)

        self.assertEqual(len(mock_session.method_calls), 4)

        # Starting the upload
        self.assertEqual(mock_session.method_calls[0][1][0], u"com.example.upload.on_progress")
        self.assertEqual(mock_session.method_calls[0][1][1]["status"], "started")
        self.assertEqual(mock_session.method_calls[0][1][1]["id"], "examplefile.txt")
        self.assertEqual(mock_session.method_calls[0][1][1]["chunk"], 1)

        # Progress, first chunk done
        self.assertEqual(mock_session.method_calls[1][1][0], u"com.example.upload.on_progress")
        self.assertEqual(mock_session.method_calls[1][1][1]["status"], "progress")
        self.assertEqual(mock_session.method_calls[1][1][1]["id"], "examplefile.txt")
        self.assertEqual(mock_session.method_calls[1][1][1]["chunk"], 1)

        # Progress, second chunk done
        self.assertEqual(mock_session.method_calls[2][1][0], u"com.example.upload.on_progress")
        self.assertEqual(mock_session.method_calls[2][1][1]["status"], "progress")
        self.assertEqual(mock_session.method_calls[2][1][1]["id"], "examplefile.txt")
        self.assertEqual(mock_session.method_calls[2][1][1]["chunk"], 2)

        # Upload complete
        self.assertEqual(mock_session.method_calls[3][1][0], u"com.example.upload.on_progress")
        self.assertEqual(mock_session.method_calls[3][1][1]["status"], "finished")
        self.assertEqual(mock_session.method_calls[3][1][1]["id"], "examplefile.txt")
        self.assertEqual(mock_session.method_calls[3][1][1]["chunk"], 2)

        # No item in the temp dir which we made earlier, one item in the
        # upload dir. Otherjunk is removed because it belongs to no upload.
        self.assertEqual(len(temp_dir.listdir()), 0)
        self.assertEqual(len(upload_dir.listdir()), 1)
        with upload_dir.child("examplefile.txt").open("rb") as f:
            self.assertEqual(f.read(), b"hello Crossbar!\n")
Esempio n. 34
0
    def test_multichunk_shuffle(self):
        """
        Uploading files that are in multiple chunks and are uploaded in different order works.
        """
        upload_dir = FilePath(self.mktemp())
        upload_dir.makedirs()
        temp_dir = FilePath(self.mktemp())
        temp_dir.makedirs()

        fields = {
            "file_name": "resumableFilename",
            "mime_type": "resumableType",
            "total_size": "resumableTotalSize",
            "chunk_number": "resumableChunkNumber",
            "chunk_size": "resumableChunkSize",
            "total_chunks": "resumableTotalChunks",
            "content": "file",
            "on_progress": "on_progress",
            "session": "session"
        }

        mock_session = Mock()

        resource = FileUploadResource(upload_dir.path, temp_dir.path, fields, mock_session)

        #
        # Chunk 2

        multipart_body = b"""-----------------------------42560029919436807832069165364\r\nContent-Disposition: form-data; name="resumableChunkNumber"\r\n\r\n2\r\n-----------------------------42560029919436807832069165364\r\nContent-Disposition: form-data; name="resumableChunkSize"\r\n\r\n10\r\n-----------------------------42560029919436807832069165364\r\nContent-Disposition: form-data; name="resumableCurrentChunkSize"\r\n\r\n6\r\n-----------------------------42560029919436807832069165364\r\nContent-Disposition: form-data; name="resumableTotalSize"\r\n\r\n16\r\n-----------------------------42560029919436807832069165364\r\nContent-Disposition: form-data; name="resumableType"\r\n\r\ntext/plain\r\n-----------------------------42560029919436807832069165364\r\nContent-Disposition: form-data; name="resumableIdentifier"\r\n\r\n16-examplefiletxt\r\n-----------------------------42560029919436807832069165364\r\nContent-Disposition: form-data; name="resumableFilename"\r\n\r\nexamplefile.txt\r\n-----------------------------42560029919436807832069165364\r\nContent-Disposition: form-data; name="resumableRelativePath"\r\n\r\nexamplefile.txt\r\n-----------------------------42560029919436807832069165364\r\nContent-Disposition: form-data; name="resumableTotalChunks"\r\n\r\n2\r\n-----------------------------42560029919436807832069165364\r\nContent-Disposition: form-data; name="on_progress"\r\n\r\ncom.example.upload.on_progress\r\n-----------------------------42560029919436807832069165364\r\nContent-Disposition: form-data; name="session"\r\n\r\n8887465641628580\r\n-----------------------------42560029919436807832069165364\r\nContent-Disposition: form-data; name="file"; filename="blob"\r\nContent-Type: application/octet-stream\r\n\r\nsbar!\n\r\n-----------------------------42560029919436807832069165364--\r\n"""

        d = renderResource(
            resource, b"/", method="POST",
            headers={
                b"content-type": [b"multipart/form-data; boundary=---------------------------42560029919436807832069165364"],
                b"Content-Length": [b"1688"]
            },
            body=multipart_body
        )

        res = self.successResultOf(d)
        self.assertEqual(res.code, 200)

        # One directory in the temp dir, nothing in the upload dir, temp dir
        # contains one chunk
        self.assertEqual(len(temp_dir.listdir()), 1)
        self.assertEqual(len(temp_dir.child("examplefile.txt").listdir()), 1)
        with temp_dir.child("examplefile.txt").child("chunk_2").open("rb") as f:
            self.assertEqual(f.read(), b"sbar!\n")
        self.assertEqual(len(upload_dir.listdir()), 0)
        #
        # Chunk 1

        multipart_body = b"""-----------------------------1311987731215707521443909311\r\nContent-Disposition: form-data; name="resumableChunkNumber"\r\n\r\n1\r\n-----------------------------1311987731215707521443909311\r\nContent-Disposition: form-data; name="resumableChunkSize"\r\n\r\n10\r\n-----------------------------1311987731215707521443909311\r\nContent-Disposition: form-data; name="resumableCurrentChunkSize"\r\n\r\n10\r\n-----------------------------1311987731215707521443909311\r\nContent-Disposition: form-data; name="resumableTotalSize"\r\n\r\n16\r\n-----------------------------1311987731215707521443909311\r\nContent-Disposition: form-data; name="resumableType"\r\n\r\ntext/plain\r\n-----------------------------1311987731215707521443909311\r\nContent-Disposition: form-data; name="resumableIdentifier"\r\n\r\n16-examplefiletxt\r\n-----------------------------1311987731215707521443909311\r\nContent-Disposition: form-data; name="resumableFilename"\r\n\r\nexamplefile.txt\r\n-----------------------------1311987731215707521443909311\r\nContent-Disposition: form-data; name="resumableRelativePath"\r\n\r\nexamplefile.txt\r\n-----------------------------1311987731215707521443909311\r\nContent-Disposition: form-data; name="resumableTotalChunks"\r\n\r\n2\r\n-----------------------------1311987731215707521443909311\r\nContent-Disposition: form-data; name="on_progress"\r\n\r\ncom.example.upload.on_progress\r\n-----------------------------1311987731215707521443909311\r\nContent-Disposition: form-data; name="session"\r\n\r\n8887465641628580\r\n-----------------------------1311987731215707521443909311\r\nContent-Disposition: form-data; name="file"; filename="blob"\r\nContent-Type: application/octet-stream\r\n\r\nhello Cros\r\n-----------------------------1311987731215707521443909311--\r\n"""

        d = renderResource(
            resource, b"/", method="POST",
            headers={
                b"content-type": [b"multipart/form-data; boundary=---------------------------1311987731215707521443909311"],
                b"Content-Length": [b"1680"]
            },
            body=multipart_body
        )

        res = self.successResultOf(d)
        self.assertEqual(res.code, 200)

        self.assertEqual(len(mock_session.method_calls), 4)

        # Starting the upload
        self.assertEqual(mock_session.method_calls[0][1][0], u"com.example.upload.on_progress")
        self.assertEqual(mock_session.method_calls[0][1][1]["status"], "started")
        self.assertEqual(mock_session.method_calls[0][1][1]["id"], "examplefile.txt")
        self.assertEqual(mock_session.method_calls[0][1][1]["chunk"], 2)

        # Progress, first chunk done
        self.assertEqual(mock_session.method_calls[1][1][0], u"com.example.upload.on_progress")
        self.assertEqual(mock_session.method_calls[1][1][1]["status"], "progress")
        self.assertEqual(mock_session.method_calls[1][1][1]["id"], "examplefile.txt")
        self.assertEqual(mock_session.method_calls[1][1][1]["chunk"], 2)

        # Progress, second chunk done
        self.assertEqual(mock_session.method_calls[2][1][0], u"com.example.upload.on_progress")
        self.assertEqual(mock_session.method_calls[2][1][1]["status"], "progress")
        self.assertEqual(mock_session.method_calls[2][1][1]["id"], "examplefile.txt")
        self.assertEqual(mock_session.method_calls[2][1][1]["chunk"], 1)

        # Upload complete
        self.assertEqual(mock_session.method_calls[3][1][0], u"com.example.upload.on_progress")
        self.assertEqual(mock_session.method_calls[3][1][1]["status"], "finished")
        self.assertEqual(mock_session.method_calls[3][1][1]["id"], "examplefile.txt")
        self.assertEqual(mock_session.method_calls[3][1][1]["chunk"], 1)

        # Nothing in the temp dir, one file in the upload
        self.assertEqual(len(temp_dir.listdir()), 0)
        self.assertEqual(len(upload_dir.listdir()), 1)
        with upload_dir.child("examplefile.txt").open("rb") as f:
            self.assertEqual(f.read(), b"hello Crossbar!\n")
Esempio n. 35
0
    def test_basic(self):
        """
        Upload a basic file using the FileUploadResource, in just a single chunk.
        """
        upload_dir = FilePath(self.mktemp())
        upload_dir.makedirs()
        temp_dir = FilePath(self.mktemp())
        temp_dir.makedirs()

        fields = {
            "file_name": "resumableFilename",
            "mime_type": "resumableType",
            "total_size": "resumableTotalSize",
            "chunk_number": "resumableChunkNumber",
            "chunk_size": "resumableChunkSize",
            "total_chunks": "resumableTotalChunks",
            "content": "file",
            "on_progress": "on_progress",
            "session": "session"
        }

        mock_session = Mock()

        resource = FileUploadResource(upload_dir.path, temp_dir.path, fields,
                                      mock_session)

        mp = Multipart()
        mp.add_part(b"resumableChunkNumber", b"1")
        mp.add_part(b"resumableChunkSize", b"1048576")
        mp.add_part(b"resumableCurrentChunkSize", b"16")
        mp.add_part(b"resumableTotalSize", b"16")
        mp.add_part(b"resumableType", b"text/plain")
        mp.add_part(b"resumableIdentifier", b"16-examplefiletxt")
        mp.add_part(b"resumableFilename", b"examplefile.txt")
        mp.add_part(b"resumableRelativePath", b"examplefile.txt")
        mp.add_part(b"resumableTotalChunks", b"1")
        mp.add_part(b"on_progress", b"com.example.upload.on_progress")
        mp.add_part(b"session", b"6891276359801283")
        mp.add_part(b"file",
                    b"hello Crossbar!\n",
                    content_type=b"application/octet-stream",
                    filename=b"blob")
        body, headers = mp.render()

        d = renderResource(resource,
                           b"/",
                           method="POST",
                           headers=headers,
                           body=body)

        res = self.successResultOf(d)
        res.setResponseCode.assert_called_once_with(200)

        self.assertEqual(len(mock_session.method_calls), 2)

        # Starting the upload
        self.assertEqual(mock_session.method_calls[0][1][0],
                         u"com.example.upload.on_progress")
        self.assertEqual(mock_session.method_calls[0][1][1]["status"],
                         "started")
        self.assertEqual(mock_session.method_calls[0][1][1]["id"],
                         "examplefile.txt")

        # Upload complete
        self.assertEqual(mock_session.method_calls[1][1][0],
                         u"com.example.upload.on_progress")
        self.assertEqual(mock_session.method_calls[1][1][1]["status"],
                         "finished")
        self.assertEqual(mock_session.method_calls[1][1][1]["id"],
                         "examplefile.txt")

        # Nothing in the temp dir, one file in the upload
        self.assertEqual(len(temp_dir.listdir()), 0)
        self.assertEqual(len(upload_dir.listdir()), 1)
        with upload_dir.child("examplefile.txt").open("rb") as f:
            self.assertEqual(f.read(), b"hello Crossbar!\n")