From bb6bd900cdfc473caed6c1795649e94d9070a7e1 Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Thu, 26 May 2022 16:06:39 -0700 Subject: [PATCH] response: Replace response.asynchronous attribute with new class. Signed-off-by: Anders Kaseorg --- zerver/lib/response.py | 9 +++++++++ zerver/middleware.py | 11 ++++++++--- zerver/tornado/handlers.py | 4 ++-- zerver/tornado/views.py | 8 +++----- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/zerver/lib/response.py b/zerver/lib/response.py index 9bddd228cd..4c5957bba6 100644 --- a/zerver/lib/response.py +++ b/zerver/lib/response.py @@ -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 diff --git a/zerver/middleware.py b/zerver/middleware.py index 803723e441..f8284292c4 100644 --- a/zerver/middleware.py +++ b/zerver/middleware.py @@ -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 diff --git a/zerver/tornado/handlers.py b/zerver/tornado/handlers.py index aaed2ad610..bf7910faae 100644 --- a/zerver/tornado/handlers.py +++ b/zerver/tornado/handlers.py @@ -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 diff --git a/zerver/tornado/views.py b/zerver/tornado/views.py index 0edcfdd098..a886f85e3a 100644 --- a/zerver/tornado/views.py +++ b/zerver/tornado/views.py @@ -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"])