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())
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)
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)
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])
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"]})
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)
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")
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"}})
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()
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)
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")
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())
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())
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)
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)
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)
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)
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)
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()
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")
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())
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())
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())
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())
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())
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()))
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())
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())
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())
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())
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")
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()
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")
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")
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")