summary refs log tree commit diff
path: root/synapse/http
diff options
context:
space:
mode:
authorSean Quah <8349537+squahtx@users.noreply.github.com>2022-05-10 14:06:08 +0100
committerGitHub <noreply@github.com>2022-05-10 14:06:08 +0100
commit5cfb0045955f8b5e9e8a1e0505fa2b5ed4f7bde2 (patch)
tree32d89a41bca41d3a1f689be4597440b01f42d53e /synapse/http
parentAdd `@cancellable` decorator, for use on request handlers (#12586) (diff)
downloadsynapse-5cfb0045955f8b5e9e8a1e0505fa2b5ed4f7bde2.tar.xz
Add ability to cancel disconnected requests to `SynapseRequest` (#12588)
Signed-off-by: Sean Quah <seanq@element.io>
Diffstat (limited to 'synapse/http')
-rw-r--r--synapse/http/site.py24
1 files changed, 23 insertions, 1 deletions
diff --git a/synapse/http/site.py b/synapse/http/site.py
index 0b85a57d77..f7f1c57042 100644
--- a/synapse/http/site.py
+++ b/synapse/http/site.py
@@ -19,6 +19,7 @@ from typing import TYPE_CHECKING, Any, Generator, Optional, Tuple, Union
 import attr
 from zope.interface import implementer
 
+from twisted.internet.defer import Deferred
 from twisted.internet.interfaces import IAddress, IReactorTime
 from twisted.python.failure import Failure
 from twisted.web.http import HTTPChannel
@@ -91,6 +92,13 @@ class SynapseRequest(Request):
         # we can't yet create the logcontext, as we don't know the method.
         self.logcontext: Optional[LoggingContext] = None
 
+        # The `Deferred` to cancel if the client disconnects early. Expected to be set
+        # by `Resource.render`.
+        self.render_deferred: Optional["Deferred[None]"] = None
+        # A boolean indicating whether `_render_deferred` should be cancelled if the
+        # client disconnects early. Expected to be set during `Resource.render`.
+        self.is_render_cancellable = False
+
         global _next_request_seq
         self.request_seq = _next_request_seq
         _next_request_seq += 1
@@ -357,7 +365,21 @@ class SynapseRequest(Request):
                     {"event": "client connection lost", "reason": str(reason.value)}
                 )
 
-            if not self._is_processing:
+            if self._is_processing:
+                if self.is_render_cancellable:
+                    if self.render_deferred is not None:
+                        # Throw a cancellation into the request processing, in the hope
+                        # that it will finish up sooner than it normally would.
+                        # The `self.processing()` context manager will call
+                        # `_finished_processing()` when done.
+                        with PreserveLoggingContext():
+                            self.render_deferred.cancel()
+                    else:
+                        logger.error(
+                            "Connection from client lost, but have no Deferred to "
+                            "cancel even though the request is marked as cancellable."
+                        )
+            else:
                 self._finished_processing()
 
     def _started_processing(self, servlet_name: str) -> None: