def test_invoke_request_id_sequences(self): """ make sure each session independently generates sequential IDs """ handler0 = ApplicationSession() handler1 = ApplicationSession() trans0 = MockTransport(handler0) trans1 = MockTransport(handler1) # the ID sequences for each session should both start at 0 # (the register) and then increment for the call() def verify_seq_id(orig, msg): if isinstance(msg, message.Register): self.assertEqual(msg.request, 1) elif isinstance(msg, message.Call): self.assertEqual(msg.request, 2) return orig(msg) orig0 = trans0.send orig1 = trans1.send trans0.send = lambda msg: verify_seq_id(orig0, msg) trans1.send = lambda msg: verify_seq_id(orig1, msg) def myproc1(): return 23 yield handler0.register(myproc1, u'com.myapp.myproc1') yield handler1.register(myproc1, u'com.myapp.myproc1') d0 = handler0.call(u'com.myapp.myproc1') d1 = handler1.call(u'com.myapp.myproc1') res = yield DeferredList([d0, d1]) self.assertEqual(res, [(True, 23), (True, 23)])
def test_call(self): handler = ApplicationSession() MockTransport(handler) res = yield handler.call(u'com.myapp.procedure1') self.assertEqual(res, 100) res = yield handler.call(u'com.myapp.procedure1', 1, 2, 3) self.assertEqual(res, 100) res = yield handler.call(u'com.myapp.procedure1', 1, 2, 3, foo=23, bar='hello') self.assertEqual(res, 100) res = yield handler.call(u'com.myapp.procedure1', options=types.CallOptions(timeout=10000)) self.assertEqual(res, 100) res = yield handler.call(u'com.myapp.procedure1', 1, 2, 3, foo=23, bar='hello', options=types.CallOptions(timeout=10000)) self.assertEqual(res, 100)
def test_invoke_request_id_sequences(self): """ make sure each session independently generates sequential IDs """ handler0 = ApplicationSession() handler1 = ApplicationSession() trans0 = MockTransport(handler0) trans1 = MockTransport(handler1) # the ID sequences for each session should both start at 0 # (the register) and then increment for the call() def verify_seq_id(orig, msg): if isinstance(msg, message.Register): self.assertEqual(msg.request, 0) elif isinstance(msg, message.Call): self.assertEqual(msg.request, 1) return orig(msg) orig0 = trans0.send orig1 = trans1.send trans0.send = lambda msg: verify_seq_id(orig0, msg) trans1.send = lambda msg: verify_seq_id(orig1, msg) def myproc1(): return 23 yield handler0.register(myproc1, u'com.myapp.myproc1') yield handler1.register(myproc1, u'com.myapp.myproc1') d0 = handler0.call(u'com.myapp.myproc1') d1 = handler1.call(u'com.myapp.myproc1') res = yield DeferredList([d0, d1]) self.assertEqual(res, [(True, 23), (True, 23)])
def test_call_with_complex_result(self): handler = ApplicationSession() MockTransport(handler) res = yield handler.call(u'com.myapp.procedure2') self.assertIsInstance(res, types.CallResult) self.assertEqual(res.results, (1, 2, 3)) self.assertEqual(res.kwresults, {}) res = yield handler.call(u'com.myapp.procedure3') self.assertIsInstance(res, types.CallResult) self.assertEqual(res.results, (1, 2, 3)) self.assertEqual(res.kwresults, {'foo': 'bar', 'baz': 23})
def test_call_with_complex_result(self): handler = ApplicationSession() MockTransport(handler) res = yield handler.call(u'com.myapp.procedure2') self.assertIsInstance(res, types.CallResult) self.assertEqual(res.results, (1, 2, 3)) self.assertEqual(res.kwresults, {}) res = yield handler.call(u'com.myapp.procedure3') self.assertIsInstance(res, types.CallResult) self.assertEqual(res.results, (1, 2, 3)) self.assertEqual(res.kwresults, {'foo': 'bar', 'baz': 23})
def test_invoke_progressive_result_just_kwargs(self): handler = ApplicationSession() MockTransport(handler) @inlineCallbacks def bing(details=None): self.assertTrue(details is not None) self.assertTrue(details.progress is not None) details.progress(key='word') yield succeed(True) returnValue(42) got_progress = Deferred() def progress(key=None): got_progress.callback(key) # see MockTransport, must start with "com.myapp.myproc" yield handler.register( bing, u'com.myapp.myproc2', types.RegisterOptions(details_arg='details'), ) res = yield handler.call( u'com.myapp.myproc2', options=types.CallOptions(on_progress=progress), ) self.assertEqual(42, res) self.assertTrue(got_progress.called) self.assertEqual('word', got_progress.result)
def test_invoke_user_raises(self): handler = ApplicationSession() handler.traceback_app = True MockTransport(handler) errors = [] def log_error(e, msg): errors.append((e.value, msg)) handler.onUserError = log_error name_error = NameError('foo') def bing(): raise name_error # see MockTransport, must start with "com.myapp.myproc" yield handler.register(bing, u'com.myapp.myproc99') try: yield handler.call(u'com.myapp.myproc99') self.fail("Expected an error") except Exception as e: # XXX should/could we export all the builtin types? # right now, we always get ApplicationError # self.assertTrue(isinstance(e, NameError)) self.assertTrue(isinstance(e, RuntimeError)) # also, we should have logged the real NameError to # Twisted. self.assertEqual(1, len(errors)) self.assertEqual(name_error, errors[0][0])
def test_invoke_progressive_result(self): handler = ApplicationSession() MockTransport(handler) @inlineCallbacks def bing(details=None): self.assertTrue(details is not None) self.assertTrue(details.progress is not None) for i in range(10): details.progress(i) yield succeed(i) returnValue(42) progressive = list(map(lambda _: Deferred(), range(10))) def progress(arg): progressive[arg].callback(arg) # see MockTransport, must start with "com.myapp.myproc" yield handler.register( bing, u'com.myapp.myproc2', types.RegisterOptions(details_arg='details'), ) res = yield handler.call( u'com.myapp.myproc2', options=types.CallOptions(on_progress=progress), ) self.assertEqual(42, res) # make sure we got *all* our progressive results for i in range(10): self.assertTrue(progressive[i].called) self.assertEqual(i, progressive[i].result)
def test_invoke_user_raises(self): handler = ApplicationSession() handler.traceback_app = True MockTransport(handler) name_error = NameError('foo') def bing(): raise name_error # see MockTransport, must start with "com.myapp.myproc" yield handler.register(bing, u'com.myapp.myproc99') try: yield handler.call(u'com.myapp.myproc99') self.fail("Expected an error") except Exception as e: # XXX should/could we export all the builtin types? # right now, we always get ApplicationError # self.assertTrue(isinstance(e, NameError)) self.assertTrue(isinstance(e, RuntimeError)) # also, we should have logged the real NameError to # Twisted. errs = self.flushLoggedErrors() self.assertEqual(1, len(errs)) self.assertEqual(name_error, errs[0].value)
def test_invoke_progressive_result_just_kwargs(self): handler = ApplicationSession() MockTransport(handler) @inlineCallbacks def bing(details=None): self.assertTrue(details is not None) self.assertTrue(details.progress is not None) details.progress(key='word') yield succeed(True) returnValue(42) got_progress = Deferred() def progress(key=None): got_progress.callback(key) # see MockTransport, must start with "com.myapp.myproc" yield handler.register( bing, u'com.myapp.myproc2', types.RegisterOptions(details_arg='details'), ) res = yield handler.call( u'com.myapp.myproc2', options=types.CallOptions(on_progress=progress), ) self.assertEqual(42, res) self.assertTrue(got_progress.called) self.assertEqual('word', got_progress.result)
def test_invoke_progressive_result(self): handler = ApplicationSession() MockTransport(handler) @inlineCallbacks def bing(details=None): self.assertTrue(details is not None) self.assertTrue(details.progress is not None) for i in range(10): details.progress(i) yield succeed(i) returnValue(42) progressive = list(map(lambda _: Deferred(), range(10))) def progress(arg): progressive[arg].callback(arg) # see MockTransport, must start with "com.myapp.myproc" yield handler.register( bing, u'com.myapp.myproc2', types.RegisterOptions(details_arg='details'), ) res = yield handler.call( u'com.myapp.myproc2', options=types.CallOptions(on_progress=progress), ) self.assertEqual(42, res) # make sure we got *all* our progressive results for i in range(10): self.assertTrue(progressive[i].called) self.assertEqual(i, progressive[i].result)
def test_call(self): handler = ApplicationSession() MockTransport(handler) res = yield handler.call(u'com.myapp.procedure1') self.assertEqual(res, 100) res = yield handler.call(u'com.myapp.procedure1', 1, 2, 3) self.assertEqual(res, 100) res = yield handler.call(u'com.myapp.procedure1', 1, 2, 3, foo=23, bar='hello') self.assertEqual(res, 100) res = yield handler.call(u'com.myapp.procedure1', options=types.CallOptions(timeout=10000)) self.assertEqual(res, 100) res = yield handler.call(u'com.myapp.procedure1', 1, 2, 3, foo=23, bar='hello', options=types.CallOptions(timeout=10000)) self.assertEqual(res, 100)
def test_invoke(self): handler = ApplicationSession() MockTransport(handler) def myproc1(): return 23 yield handler.register(myproc1, u'com.myapp.myproc1') res = yield handler.call(u'com.myapp.myproc1') self.assertEqual(res, 23)
def test_invoke(self): handler = ApplicationSession() MockTransport(handler) def myproc1(): return 23 yield handler.register(myproc1, u'com.myapp.myproc1') res = yield handler.call(u'com.myapp.myproc1') self.assertEqual(res, 23)
def test_invoke_twice(self): handler = ApplicationSession() MockTransport(handler) def myproc1(): return 23 yield handler.register(myproc1, u'com.myapp.myproc1') d0 = handler.call(u'com.myapp.myproc1') d1 = handler.call(u'com.myapp.myproc1') res = yield DeferredList([d0, d1]) self.assertEqual(res, [(True, 23), (True, 23)])
def test_invoke_twice(self): handler = ApplicationSession() MockTransport(handler) def myproc1(): return 23 yield handler.register(myproc1, u'com.myapp.myproc1') d0 = handler.call(u'com.myapp.myproc1') d1 = handler.call(u'com.myapp.myproc1') res = yield DeferredList([d0, d1]) self.assertEqual(res, [(True, 23), (True, 23)])
def test_invoke_progressive_result_error(self): handler = ApplicationSession() MockTransport(handler) @inlineCallbacks def bing(arg, details=None, key=None): self.assertTrue(details is not None) self.assertTrue(details.progress is not None) self.assertEqual(key, 'word') self.assertEqual('arg', arg) details.progress('life', something='nothing') yield succeed('meaning of') returnValue(42) got_progress = Deferred() progress_error = NameError('foo') logged_errors = [] def got_error(e, msg): logged_errors.append((e.value, msg)) handler.onUserError = got_error def progress(arg, something=None): self.assertEqual('nothing', something) got_progress.callback(arg) raise progress_error # see MockTransport, must start with "com.myapp.myproc" yield handler.register( bing, u'com.myapp.myproc2', types.RegisterOptions(details_arg='details'), ) res = yield handler.call( u'com.myapp.myproc2', 'arg', options=types.CallOptions(on_progress=progress), key='word', ) self.assertEqual(42, res) # our progress handler raised an error, but not before # recording success. self.assertTrue(got_progress.called) self.assertEqual('life', got_progress.result) # make sure our progress-handler error was logged self.assertEqual(1, len(logged_errors)) self.assertEqual(progress_error, logged_errors[0][0])
def test_invoke_progressive_result_error(self): handler = ApplicationSession() MockTransport(handler) @inlineCallbacks def bing(arg, details=None, key=None): self.assertTrue(details is not None) self.assertTrue(details.progress is not None) self.assertEqual(key, 'word') self.assertEqual('arg', arg) details.progress('life', something='nothing') yield succeed('meaning of') returnValue(42) got_progress = Deferred() progress_error = NameError('foo') logged_errors = [] def got_error(e, msg): logged_errors.append((e.value, msg)) handler.onUserError = got_error def progress(arg, something=None): self.assertEqual('nothing', something) got_progress.callback(arg) raise progress_error # see MockTransport, must start with "com.myapp.myproc" yield handler.register( bing, u'com.myapp.myproc2', types.RegisterOptions(details_arg='details'), ) res = yield handler.call( u'com.myapp.myproc2', 'arg', options=types.CallOptions(on_progress=progress), key='word', ) self.assertEqual(42, res) # our progress handler raised an error, but not before # recording success. self.assertTrue(got_progress.called) self.assertEqual('life', got_progress.result) # make sure our progress-handler error was logged self.assertEqual(1, len(logged_errors)) self.assertEqual(progress_error, logged_errors[0][0])
def test_call_exception_bare(self): handler = ApplicationSession() MockTransport(handler) exception = Exception() def raiser(): raise exception registration0 = yield handler.register(raiser, "com.myapp.myproc_error") try: yield handler.call('com.myapp.myproc_error') self.fail() except Exception as e: self.assertIsInstance(e, ApplicationError) finally: yield registration0.unregister()
def test_call_exception_bare(self): handler = ApplicationSession() MockTransport(handler) exception = Exception() def raiser(): raise exception registration0 = yield handler.register(raiser, u"com.myapp.myproc_error") try: yield handler.call(u'com.myapp.myproc_error') self.fail() except Exception as e: self.assertIsInstance(e, ApplicationError) finally: yield registration0.unregister()
def test_call_exception_runtimeerror(self): handler = ApplicationSession() MockTransport(handler) exception = RuntimeError("a simple error") def raiser(): raise exception registration0 = yield handler.register(raiser, u"com.myapp.myproc_error") try: yield handler.call(u'com.myapp.myproc_error') self.fail() except Exception as e: self.assertIsInstance(e, ApplicationError) self.assertEqual(e.error_message(), "wamp.error.runtime_error: a simple error") finally: yield registration0.unregister()
def test_call_exception_runtimeerror(self): handler = ApplicationSession() MockTransport(handler) exception = RuntimeError("a simple error") def raiser(): raise exception registration0 = yield handler.register(raiser, "com.myapp.myproc_error") try: yield handler.call('com.myapp.myproc_error') self.fail() except Exception as e: self.assertIsInstance(e, ApplicationError) self.assertEqual(e.error_message(), "wamp.error.runtime_error: a simple error") finally: yield registration0.unregister()
def test_invoke_progressive_result_no_args(self): handler = ApplicationSession() MockTransport(handler) @inlineCallbacks def bing(details=None): self.assertTrue(details is not None) self.assertTrue(details.progress is not None) details.progress() yield succeed(True) returnValue(42) got_progress = Deferred() def progress(): got_progress.callback("intentionally left blank") # see MockTransport, must start with "com.myapp.myproc" yield handler.register(bing, u"com.myapp.myproc2", types.RegisterOptions(details_arg="details")) res = yield handler.call(u"com.myapp.myproc2", options=types.CallOptions(on_progress=progress)) self.assertEqual(42, res) self.assertTrue(got_progress.called)
def stockCall(self, pdid, procedure, *args, **kwargs): out.info('cxbr: (%s) calling (%s)' % (self.pdid, procedure,)) return ApplicationSession.call(self, procedure, *args, **kwargs)
def stockCall(self, procedure, *args, **kwargs): return ApplicationSession.call(self, u''+procedure, *args, **kwargs)
def render_GET(self, request): """ Initiate the rendering of a HTTP/GET request by calling a WAMP procedure, the resulting ``dict`` is rendered together with the specified Jinja2 template for this URL. :param request: The HTTP request. :returns: server.NOT_DONE_YET (special) """ cookie = request.received_cookies.get(b'session_cookie') self.log.debug('Session Cookie is ({})'.format(cookie)) if cookie: session = self._session_cache.get(cookie) if not session: # FIXME: lookup role for current session self.log.debug('Creating a new session for cookie ({})'.format(cookie)) authrole = 'anonymous' session = ApplicationSession(ComponentConfig(realm=self._realm_name, extra=None)) self._worker._router_session_factory.add(session, authrole=authrole) self._session_cache[cookie] = session else: self.log.debug('Using a cached session for ({})'.format(cookie)) else: self.log.debug('No session cookie, falling back on default session') session = self._default_session if not session: self.log.error('could not call procedure - no session') return self._render_error('could not call procedure - no session', request) full_path = request.uri.decode('utf-8') try: # werkzeug.routing.MapAdapter # http://werkzeug.pocoo.org/docs/dev/routing/#werkzeug.routing.MapAdapter.match (procedure, request.template), kwargs = self._map_adapter.match(full_path) self.log.debug( 'WapResource HTTP/GET "{full_path}" mapped to procedure "{procedure}"', full_path=full_path, procedure=procedure) # FIXME: how do we allow calling WAMP procedures with positional args? if kwargs: d = session.call(procedure, **kwargs) else: d = session.call(procedure) # d.addCallback(self._after_call_success, request) # d.addErrback(self._after_call_error, request) d.addCallbacks( self._after_call_success, self._after_call_error, callbackArgs=[request], errbackArgs=[request]) return server.NOT_DONE_YET except NotFound: request.setResponseCode(404) return self._render_error('path not found [werkzeug.routing.MapAdapter.match]', request) except MethodNotAllowed: request.setResponseCode(511) return self._render_error('method not allowed [werkzeug.routing.MapAdapter.match]', request) except Exception: request.setResponseCode(500) request.write(self._render_error('unknown error [werkzeug.routing.MapAdapter.match]', request)) raise
def call(self, procedure, *args, **kwargs): # kwargs['options'] = CallOptions(disclose_me=True) args = (self.pdid, ) + args procedure = _prepend(self.pdid, procedure) # out.info('cxbr: (%s) calling (%s)' % (self.pdid, procedure,)) return ApplicationSession.call(self, procedure, *args, **kwargs)
def stockCall(self, procedure, *args, **kwargs): return ApplicationSession.call(self, u'' + procedure, *args, **kwargs)
def call(self, pdid, procedure, *args, **kwargs): # kwargs['options'] = CallOptions(disclose_me=True) args = (self.pdid,) + args procedure = _prepend(pdid, procedure) #out.info('riff: (%s) calling (%s)' % (self.pdid, procedure,)) return ApplicationSession.call(self, procedure, *args, **kwargs)
def absCall(self, procedure, *args, **kwargs): #out.info('riff: (%s) calling (%s)' % (self.pdid, procedure,)) return ApplicationSession.call(self, u'' + procedure, *args, **kwargs)
def render(self, request): """ Initiate the rendering of a HTTP/GET request by calling a WAMP procedure, the resulting ``dict`` is rendered together with the specified Jinja2 template for this URL. :param request: The HTTP request. :returns: server.NOT_DONE_YET (special) """ # https://twistedmatrix.com/documents/current/api/twisted.web.resource.Resource.html#render # The encoded path of the request URI (_not_ (!) including query arguments), full_path = request.path.decode('utf-8') # HTTP request method (GET, POST, ..) http_method = request.method.decode() if http_method not in ['GET', 'POST']: request.setResponseCode(511) return self._render_error( 'Method not allowed on path "{full_path}" [werkzeug.routing.MapAdapter.match]' .format(full_path=full_path), request) # in case of HTTP/POST, read request body as one binary string if http_method == 'POST' and request.content: content_type = request.getAllHeaders().get( b'content-type', b'application/octet-stream').decode() # https://stackoverflow.com/a/11549600/884770 # http://marianoiglesias.com.ar/python/file-uploading-with-multi-part-encoding-using-twisted/ body_data = request.content.read() self.log.info('POST data len = {newdata_len}', newdata_len=len(body_data)) else: content_type = None body_data = None # parse and decode any query parameters query_args = {} if request.args: for key, values in request.args.items(): key = key.decode() # we only process the first header value per key (!) value = values[0].decode() query_args[key] = value self.log.info('Parsed query parameters: {query_args}', query_args=query_args) # parse client announced accept header client_accept = request.getAllHeaders().get(b'accept', None) if client_accept: client_accept = client_accept.decode() # flag indicating the client wants to get plain JSON results (not rendered HTML) client_return_json = client_accept == 'application/json' # client cookie processing cookie = request.received_cookies.get(b'session_cookie') self.log.debug('Session Cookie is ({})'.format(cookie)) if cookie: session = self._session_cache.get(cookie) if not session: # FIXME: lookup role for current session self.log.debug( 'Creating a new session for cookie ({})'.format(cookie)) authrole = 'anonymous' session = ApplicationSession( ComponentConfig(realm=self._realm_name, extra=None)) self._worker._router_session_factory.add(session, authrole=authrole) self._session_cache[cookie] = session else: self.log.debug( 'Using a cached session for ({})'.format(cookie)) else: self.log.debug( 'No session cookie, falling back on default session') session = self._default_session try: # werkzeug.routing.MapAdapter # http://werkzeug.pocoo.org/docs/dev/routing/#werkzeug.routing.MapAdapter.match (procedure, request.template), kwargs = self._map_adapter.match( full_path, method=http_method, query_args=query_args) if kwargs and query_args: kwargs.update(query_args) else: kwargs = query_args self.log.info( 'WapResource on path "{full_path}" mapped to procedure "{procedure}"', full_path=full_path, procedure=procedure) # FIXME: how do we allow calling WAMP procedures with positional args? if procedure: self.log.info( 'calling procedure "{procedure}" with kwargs={kwargs} and body_data_len={body_data_len}', procedure=procedure, kwargs=kwargs, body_data_len=len(body_data) if body_data else 0) # we need a session to call if not session: self.log.error('could not call procedure - no session') return self._render_error( 'could not call procedure - no session', request) if body_data: if kwargs: d = session.call(procedure, **kwargs, data=body_data, data_type=content_type) else: d = session.call(procedure, data=body_data, data_type=content_type) else: if kwargs: d = session.call(procedure, **kwargs) else: d = session.call(procedure) else: d = succeed({}) d.addCallbacks(self._after_call_success, self._after_call_error, callbackArgs=[request, client_return_json], errbackArgs=[request, client_return_json]) return server.NOT_DONE_YET except NotFound: self.log.info('URL "{url}" not found (method={method})', url=full_path, method=http_method) request.setResponseCode(404) return self._render_error( 'Path "{full_path}" not found [werkzeug.routing.MapAdapter.match]' .format(full_path=full_path), request) except MethodNotAllowed: self.log.info('method={method} not allowed on URL "{url}"', url=full_path, method=http_method) request.setResponseCode(511) return self._render_error( 'Method not allowed on path "{full_path}" [werkzeug.routing.MapAdapter.match]' .format(full_path=full_path), request) except Exception as e: self.log.info( 'error while processing method={method} on URL "{url}": {e}', url=full_path, method=http_method, e=e) request.setResponseCode(500) request.write( self._render_error( 'Unknown error with path "{full_path}" [werkzeug.routing.MapAdapter.match]' .format(full_path=full_path), request)) raise