mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-01-19 19:26:25 +00:00
Handle interrupted connections
This commit is contained in:
parent
9146d2a0b9
commit
c31300fd0d
@ -13,13 +13,22 @@ from testtools.matchers import (
|
||||
Equals,
|
||||
Contains,
|
||||
MatchesPredicate,
|
||||
AfterPreprocessing,
|
||||
)
|
||||
from testtools.twistedsupport import (
|
||||
failed,
|
||||
succeeded,
|
||||
has_no_result,
|
||||
)
|
||||
|
||||
from twisted.python.failure import (
|
||||
Failure,
|
||||
)
|
||||
from twisted.internet.error import (
|
||||
ConnectionDone,
|
||||
)
|
||||
from twisted.internet.defer import (
|
||||
Deferred,
|
||||
fail,
|
||||
)
|
||||
from twisted.web.server import (
|
||||
@ -52,9 +61,11 @@ class StaticResource(Resource, object):
|
||||
def __init__(self, response):
|
||||
Resource.__init__(self)
|
||||
self._response = response
|
||||
self._request = None
|
||||
|
||||
@render_exception
|
||||
def render(self, request):
|
||||
self._request = request
|
||||
return self._response
|
||||
|
||||
|
||||
@ -214,3 +225,25 @@ class RenderExceptionTests(SyncTestCase):
|
||||
Equals(b"Internal Server Error"),
|
||||
),
|
||||
)
|
||||
|
||||
def test_disconnected(self):
|
||||
"""
|
||||
If the transport is disconnected before the response is available, nothing
|
||||
is written to the request.
|
||||
"""
|
||||
result = Deferred()
|
||||
resource = StaticResource(result)
|
||||
d = render(resource, {})
|
||||
|
||||
resource._request.connectionLost(Failure(ConnectionDone()))
|
||||
result.callback(b"Some result")
|
||||
|
||||
self.assertThat(
|
||||
d,
|
||||
failed(
|
||||
AfterPreprocessing(
|
||||
lambda reason: reason.type,
|
||||
Equals(ConnectionDone),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
@ -41,6 +41,7 @@ from twisted.python.failure import (
|
||||
Failure,
|
||||
)
|
||||
from twisted.internet.defer import (
|
||||
CancelledError,
|
||||
maybeDeferred,
|
||||
)
|
||||
from twisted.web.resource import (
|
||||
@ -472,7 +473,17 @@ def render_exception(render):
|
||||
# Apply `_finish` all of our result handling logic to whatever it
|
||||
# returned.
|
||||
result.addBoth(_finish, bound_render, request)
|
||||
result.addActionFinish()
|
||||
d = result.addActionFinish()
|
||||
|
||||
# If the connection is lost then there's no point running our _finish
|
||||
# logic because it has nowhere to send anything. There may also be no
|
||||
# point in finishing whatever operation was being performed because
|
||||
# the client cannot be informed of its result. Also, Twisted Web
|
||||
# raises exceptions from some Request methods if they're used after
|
||||
# the connection is lost.
|
||||
request.notifyFinish().addErrback(
|
||||
lambda ignored: d.cancel(),
|
||||
)
|
||||
return NOT_DONE_YET
|
||||
|
||||
return g
|
||||
@ -497,6 +508,8 @@ def _finish(result, render, request):
|
||||
:return: ``None``
|
||||
"""
|
||||
if isinstance(result, Failure):
|
||||
if result.check(CancelledError):
|
||||
return
|
||||
Message.log(
|
||||
message_type=u"allmydata:web:common-render:failure",
|
||||
message=result.getErrorMessage(),
|
||||
|
@ -480,7 +480,10 @@ class FileDownloader(Resource, object):
|
||||
d = self.filenode.read(req, first, size)
|
||||
|
||||
def _error(f):
|
||||
req._tahoe_request_had_error = f # for HTTP-style logging
|
||||
if f.check(defer.CancelledError):
|
||||
# The HTTP connection was lost and we no longer have anywhere
|
||||
# to send our result. Let this pass through.
|
||||
return f
|
||||
if req.startedWriting:
|
||||
# The content-type is already set, and the response code has
|
||||
# already been sent, so we can't provide a clean error
|
||||
|
Loading…
Reference in New Issue
Block a user