response: Replace response.asynchronous attribute with new class.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2022-05-26 16:06:39 -07:00 committed by Tim Abbott
parent 134977b590
commit bb6bd900cd
4 changed files with 22 additions and 10 deletions

View File

@ -80,3 +80,12 @@ def json_response_from_error(exception: JsonableError) -> HttpResponse:
response[header] = value
return response
class AsynchronousResponse(HttpResponse):
"""
This response is just a sentinel to be discarded by Tornado and replaced
with a real response later; see zulip_finish.
"""
status_code = 399

View File

@ -42,7 +42,12 @@ from zerver.lib.html_to_text import get_content_description
from zerver.lib.markdown import get_markdown_requests, get_markdown_time
from zerver.lib.rate_limiter import RateLimitResult
from zerver.lib.request import REQ, RequestNotes, has_request_variables, set_request, unset_request
from zerver.lib.response import json_response, json_response_from_error, json_unauthorized
from zerver.lib.response import (
AsynchronousResponse,
json_response,
json_response_from_error,
json_unauthorized,
)
from zerver.lib.subdomains import get_subdomain
from zerver.lib.types import ViewFuncT
from zerver.lib.user_agent import parse_user_agent
@ -401,8 +406,8 @@ class LogRequests(MiddlewareMixin):
def process_response(
self, request: HttpRequest, response: HttpResponseBase
) -> HttpResponseBase:
if getattr(response, "asynchronous", False):
# This special Tornado "asynchronous" response is
if isinstance(response, AsynchronousResponse):
# This special AsynchronousResponse sentinel is
# discarded after going through this code path as Tornado
# intends to block, so we stop here to avoid unnecessary work.
return response

View File

@ -14,7 +14,7 @@ from django.urls import set_script_prefix
from django.utils.cache import patch_vary_headers
from tornado.wsgi import WSGIContainer
from zerver.lib.response import json_response
from zerver.lib.response import AsynchronousResponse, json_response
from zerver.tornado.descriptors import get_descriptor_by_handler_id
current_handler_id = 0
@ -153,7 +153,7 @@ class AsyncDjangoHandler(tornado.web.RequestHandler, base.BaseHandler):
response = await sync_to_async(lambda: self.get_response(request), thread_sensitive=True)()
try:
if hasattr(response, "asynchronous"):
if isinstance(response, AsynchronousResponse):
# We import async_request_timer_restart during runtime
# to avoid cyclic dependency with zerver.lib.request
from zerver.middleware import async_request_timer_stop

View File

@ -9,7 +9,7 @@ from django.utils.translation import gettext as _
from zerver.decorator import internal_notify_view, process_client
from zerver.lib.exceptions import JsonableError
from zerver.lib.request import REQ, RequestNotes, has_request_variables
from zerver.lib.response import json_success
from zerver.lib.response import AsynchronousResponse, json_success
from zerver.lib.validator import (
check_bool,
check_int,
@ -170,13 +170,11 @@ def get_events_backend(
log_data["extra"] = result["extra_log_data"]
if result["type"] == "async":
# Mark this response with .asynchronous; this will result in
# Return an AsynchronousResponse; this will result in
# Tornado discarding the response and instead long-polling the
# request. See zulip_finish for more design details.
handler._request = request
response = json_success(request)
response.asynchronous = True
return response
return AsynchronousResponse()
if result["type"] == "error":
raise result["exception"]
return json_success(request, data=result["response"])