|
19 | 19 | import attr
|
20 | 20 | from zope.interface import implementer
|
21 | 21 |
|
| 22 | +from twisted.internet.defer import Deferred |
22 | 23 | from twisted.internet.interfaces import IAddress, IReactorTime
|
23 | 24 | from twisted.python.failure import Failure
|
24 | 25 | from twisted.web.http import HTTPChannel
|
@@ -91,6 +92,13 @@ def __init__(
|
91 | 92 | # we can't yet create the logcontext, as we don't know the method.
|
92 | 93 | self.logcontext: Optional[LoggingContext] = None
|
93 | 94 |
|
| 95 | + # The `Deferred` to cancel if the client disconnects early. Expected to be set |
| 96 | + # by `Resource.render`. |
| 97 | + self.render_deferred: Optional["Deferred[None]"] = None |
| 98 | + # A boolean indicating whether `_render_deferred` should be cancelled if the |
| 99 | + # client disconnects early. Expected to be set during `Resource.render`. |
| 100 | + self.is_render_cancellable = False |
| 101 | + |
94 | 102 | global _next_request_seq
|
95 | 103 | self.request_seq = _next_request_seq
|
96 | 104 | _next_request_seq += 1
|
@@ -357,7 +365,21 @@ def connectionLost(self, reason: Union[Failure, Exception]) -> None:
|
357 | 365 | {"event": "client connection lost", "reason": str(reason.value)}
|
358 | 366 | )
|
359 | 367 |
|
360 |
| - if not self._is_processing: |
| 368 | + if self._is_processing: |
| 369 | + if self.is_render_cancellable: |
| 370 | + if self.render_deferred is not None: |
| 371 | + # Throw a cancellation into the request processing, in the hope |
| 372 | + # that it will finish up sooner than it normally would. |
| 373 | + # The `self.processing()` context manager will call |
| 374 | + # `_finished_processing()` when done. |
| 375 | + with PreserveLoggingContext(): |
| 376 | + self.render_deferred.cancel() |
| 377 | + else: |
| 378 | + logger.error( |
| 379 | + "Connection from client lost, but have no Deferred to " |
| 380 | + "cancel even though the request is marked as cancellable." |
| 381 | + ) |
| 382 | + else: |
361 | 383 | self._finished_processing()
|
362 | 384 |
|
363 | 385 | def _started_processing(self, servlet_name: str) -> None:
|
|
0 commit comments