Fix media repo breaking (#5593)

pull/5605/head
Amber Brown 2019-07-03 04:01:28 +10:00 committed by Richard van der Hoff
parent f8b52eb8c5
commit 0ee9076ffe
6 changed files with 60 additions and 26 deletions

1
changelog.d/5593.bugfix Normal file
View File

@ -0,0 +1 @@
Fix regression in 1.1rc1 where OPTIONS requests to the media repo would fail.

View File

@ -65,8 +65,8 @@ def wrap_json_request_handler(h):
The handler method must have a signature of "handle_foo(self, request)",
where "request" must be a SynapseRequest.
The handler must return a deferred. If the deferred succeeds we assume that
a response has been sent. If the deferred fails with a SynapseError we use
The handler must return a deferred or a coroutine. If the deferred succeeds
we assume that a response has been sent. If the deferred fails with a SynapseError we use
it to send a JSON response with the appropriate HTTP reponse code. If the
deferred fails with any other type of error we send a 500 reponse.
"""
@ -353,16 +353,22 @@ class DirectServeResource(resource.Resource):
"""
Render the request, using an asynchronous render handler if it exists.
"""
render_callback_name = "_async_render_" + request.method.decode("ascii")
async_render_callback_name = "_async_render_" + request.method.decode("ascii")
if hasattr(self, render_callback_name):
# Call the handler
callback = getattr(self, render_callback_name)
defer.ensureDeferred(callback(request))
# Try and get the async renderer
callback = getattr(self, async_render_callback_name, None)
return NOT_DONE_YET
else:
super().render(request)
# No async renderer for this request method.
if not callback:
return super().render(request)
resp = callback(request)
# If it's a coroutine, turn it into a Deferred
if isinstance(resp, types.CoroutineType):
defer.ensureDeferred(resp)
return NOT_DONE_YET
def _options_handler(request):

View File

@ -95,6 +95,7 @@ class PreviewUrlResource(DirectServeResource):
)
def render_OPTIONS(self, request):
request.setHeader(b"Allow", b"OPTIONS, GET")
return respond_with_json(request, 200, {}, send_cors=True)
@wrap_json_request_handler

View File

@ -24,6 +24,7 @@ See doc/log_contexts.rst for details on how this works.
import logging
import threading
import types
from twisted.internet import defer, threads
@ -528,8 +529,9 @@ def run_in_background(f, *args, **kwargs):
return from the function, and that the sentinel context is set once the
deferred returned by the function completes.
Useful for wrapping functions that return a deferred which you don't yield
on (for instance because you want to pass it to deferred.gatherResults()).
Useful for wrapping functions that return a deferred or coroutine, which you don't
yield or await on (for instance because you want to pass it to
deferred.gatherResults()).
Note that if you completely discard the result, you should make sure that
`f` doesn't raise any deferred exceptions, otherwise a scary-looking
@ -544,6 +546,9 @@ def run_in_background(f, *args, **kwargs):
# by synchronous exceptions, so let's turn them into Failures.
return defer.fail()
if isinstance(res, types.CoroutineType):
res = defer.ensureDeferred(res)
if not isinstance(res, defer.Deferred):
return res

View File

@ -460,3 +460,15 @@ class URLPreviewTests(unittest.HomeserverTestCase):
"error": "DNS resolution failure during URL preview generation",
},
)
def test_OPTIONS(self):
"""
OPTIONS returns the OPTIONS.
"""
request, channel = self.make_request(
"OPTIONS", "url_preview?url=http://example.com", shorthand=False
)
request.render(self.preview_url)
self.pump()
self.assertEqual(channel.code, 200)
self.assertEqual(channel.json_body, {})

View File

@ -39,24 +39,17 @@ class LoggingContextTestCase(unittest.TestCase):
callback_completed = [False]
def test():
context_one.request = "one"
d = function()
def cb(res):
self._check_test_key("one")
callback_completed[0] = True
return res
d.addCallback(cb)
return d
with LoggingContext() as context_one:
context_one.request = "one"
# fire off function, but don't wait on it.
logcontext.run_in_background(test)
d2 = logcontext.run_in_background(function)
def cb(res):
callback_completed[0] = True
return res
d2.addCallback(cb)
self._check_test_key("one")
@ -105,6 +98,22 @@ class LoggingContextTestCase(unittest.TestCase):
return self._test_run_in_background(testfunc)
def test_run_in_background_with_coroutine(self):
async def testfunc():
self._check_test_key("one")
d = Clock(reactor).sleep(0)
self.assertIs(LoggingContext.current_context(), LoggingContext.sentinel)
await d
self._check_test_key("one")
return self._test_run_in_background(testfunc)
def test_run_in_background_with_nonblocking_coroutine(self):
async def testfunc():
self._check_test_key("one")
return self._test_run_in_background(testfunc)
@defer.inlineCallbacks
def test_make_deferred_yieldable(self):
# a function which retuns an incomplete deferred, but doesn't follow