mirror of
https://github.com/zulip/zulip.git
synced 2026-07-12 21:04:41 +08:00
Return a special value to initiate longpolling
Fixes #261. (imported from commit dd0312c3574c9ca924ab76edcb2fb6c03085be32)
This commit is contained in:
parent
30933a9639
commit
b5dccbfa87
@ -8,17 +8,18 @@ from django.conf import settings
|
||||
|
||||
from functools import wraps
|
||||
|
||||
import types
|
||||
class _RespondAsynchronously(object):
|
||||
pass
|
||||
|
||||
class TornadoAsyncException(Exception): pass
|
||||
# Return RespondAsynchronously from an @asynchronous view if the
|
||||
# response will be provided later by calling handler.finish(), or has
|
||||
# already been provided this way. We use this for longpolling mode.
|
||||
RespondAsynchronously = _RespondAsynchronously()
|
||||
|
||||
def asynchronous(method):
|
||||
@wraps(method)
|
||||
def wrapper(request, *args, **kwargs):
|
||||
v = method(request, handler=request._tornado_handler, *args, **kwargs)
|
||||
if v == None or type(v) == types.GeneratorType:
|
||||
raise TornadoAsyncException
|
||||
return v
|
||||
return method(request, handler=request._tornado_handler, *args, **kwargs)
|
||||
if getattr(method, 'csrf_exempt', False):
|
||||
wrapper.csrf_exempt = True
|
||||
return wrapper
|
||||
|
||||
@ -190,13 +190,12 @@ class AsyncDjangoHandler(tornado.web.RequestHandler, base.BaseHandler):
|
||||
break
|
||||
|
||||
if response is None:
|
||||
from ...decorator import TornadoAsyncException
|
||||
from ...decorator import RespondAsynchronously
|
||||
|
||||
try:
|
||||
response = callback(request, *callback_args, **callback_kwargs)
|
||||
except TornadoAsyncException, e:
|
||||
# TODO: Maybe add debugging output here
|
||||
return
|
||||
if response is RespondAsynchronously:
|
||||
return
|
||||
except Exception, e:
|
||||
# If the view raised an exception, run it through exception
|
||||
# middleware, and if the exception middleware returns a
|
||||
|
||||
@ -7,7 +7,7 @@ from django.db.models import Q
|
||||
from zephyr.models import Message, UserProfile, Stream, Recipient, Subscription, \
|
||||
filter_by_subscriptions, Realm, do_send_message, Client
|
||||
from zephyr.views import json_get_updates, api_get_messages
|
||||
from zephyr.decorator import TornadoAsyncException
|
||||
from zephyr.decorator import RespondAsynchronously
|
||||
from zephyr.lib.initial_password import initial_password, initial_api_key
|
||||
|
||||
import simplejson
|
||||
@ -457,10 +457,7 @@ class GetUpdatesTest(AuthedTestCase):
|
||||
post_data = {"last": str(1), "first": str(1)}
|
||||
post_data.update(extra_post_data)
|
||||
request = POSTRequestMock(post_data, user, callback)
|
||||
# json_get_updates returns None, which raises an exception in the
|
||||
# @asynchronous decorator, which raises a TornadoAsyncException. So this
|
||||
# is expected, but should probably change.
|
||||
self.assertRaises(TornadoAsyncException, view_func, request)
|
||||
self.assertEquals(view_func(request), RespondAsynchronously)
|
||||
|
||||
def test_json_get_updates(self):
|
||||
"""
|
||||
@ -497,7 +494,7 @@ class GetUpdatesTest(AuthedTestCase):
|
||||
messages.extend(data)
|
||||
|
||||
request = POSTRequestMock({"last": str(last_received), "first": "1"}, user, callback)
|
||||
self.assertRaises(TornadoAsyncException, json_get_updates, request)
|
||||
self.assertEquals(json_get_updates(request), RespondAsynchronously)
|
||||
self.assertEquals(len(messages), 0)
|
||||
|
||||
def test_missing_last_received(self):
|
||||
@ -514,7 +511,7 @@ class GetUpdatesTest(AuthedTestCase):
|
||||
self.assertTrue(message.id > 1)
|
||||
|
||||
request = POSTRequestMock({}, user, callback)
|
||||
self.assertRaises(TornadoAsyncException, json_get_updates, request)
|
||||
self.assertEquals(json_get_updates(request), RespondAsynchronously)
|
||||
|
||||
class Runner(DjangoTestSuiteRunner):
|
||||
option_list = (
|
||||
|
||||
@ -22,7 +22,7 @@ from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
from zephyr.decorator import asynchronous, require_post, \
|
||||
authenticated_api_view, authenticated_json_view, \
|
||||
internal_notify_view, \
|
||||
internal_notify_view, RespondAsynchronously, \
|
||||
has_request_variables, POST
|
||||
from zephyr.lib.query import last_n
|
||||
from zephyr.lib.avatar import gravatar_hash
|
||||
@ -447,7 +447,12 @@ def get_updates_backend(request, user_profile, handler, client_id,
|
||||
dont_block, **kwargs)
|
||||
if resp is not None:
|
||||
send_with_safety_check(resp, handler, **kwargs)
|
||||
return
|
||||
|
||||
# We have already invoked handler.finish(), so we bypass the usual view
|
||||
# response path. We are "responding asynchronously" except that it
|
||||
# already happened. This is slightly weird, but lets us share
|
||||
# send_with_safety_check with the code below.
|
||||
return RespondAsynchronously
|
||||
|
||||
# Enter long-polling mode.
|
||||
#
|
||||
@ -472,6 +477,9 @@ def get_updates_backend(request, user_profile, handler, client_id,
|
||||
user_profile.add_receive_callback(handler.async_callback(cb))
|
||||
user_profile.add_pointer_update_callback(handler.async_callback(cb))
|
||||
|
||||
# runtornado recognizes this special return value.
|
||||
return RespondAsynchronously
|
||||
|
||||
def generate_client_id():
|
||||
return base64.b16encode(os.urandom(16)).lower()
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user