Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit 682151a

Browse files
authored
Do not fail completely if oEmbed autodiscovery fails. (#15092)
Previously if an autodiscovered oEmbed request failed (e.g. the oEmbed endpoint is down or does not exist) then the entire URL preview would fail. Instead we now return everything we can, even if this additional request fails.
1 parent f8a584e commit 682151a

File tree

3 files changed

+65
-13
lines changed

3 files changed

+65
-13
lines changed

changelog.d/15092.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a long-standing bug where a URL preview would break if the discovered oEmbed failed to download.

synapse/rest/media/v1/preview_url_resource.py

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,10 @@ class PreviewUrlResource(DirectServeJsonResource):
163163
7. Stores the result in the database cache.
164164
4. Returns the result.
165165
166+
If any additional requests (e.g. from oEmbed autodiscovery, step 5.3 or
167+
image thumbnailing, step 5.4 or 6.4) fails then the URL preview as a whole
168+
does not fail. As much information as possible is returned.
169+
166170
The in-memory cache expires after 1 hour.
167171
168172
Expired entries in the database cache (and their associated media files) are
@@ -364,16 +368,25 @@ async def _do_preview(self, url: str, user: UserID, ts: int) -> bytes:
364368
oembed_url = self._oembed.autodiscover_from_html(tree)
365369
og_from_oembed: JsonDict = {}
366370
if oembed_url:
367-
oembed_info = await self._handle_url(
368-
oembed_url, user, allow_data_urls=True
369-
)
370-
(
371-
og_from_oembed,
372-
author_name,
373-
expiration_ms,
374-
) = await self._handle_oembed_response(
375-
url, oembed_info, expiration_ms
376-
)
371+
try:
372+
oembed_info = await self._handle_url(
373+
oembed_url, user, allow_data_urls=True
374+
)
375+
except Exception as e:
376+
# Fetching the oEmbed info failed, don't block the entire URL preview.
377+
logger.warning(
378+
"oEmbed fetch failed during URL preview: %s errored with %s",
379+
oembed_url,
380+
e,
381+
)
382+
else:
383+
(
384+
og_from_oembed,
385+
author_name,
386+
expiration_ms,
387+
) = await self._handle_oembed_response(
388+
url, oembed_info, expiration_ms
389+
)
377390

378391
# Parse Open Graph information from the HTML in case the oEmbed
379392
# response failed or is incomplete.

tests/rest/media/v1/test_url_preview.py

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,7 @@ def test_nonexistent_image(self) -> None:
657657
"""If the preview image doesn't exist, ensure some data is returned."""
658658
self.lookups["matrix.org"] = [(IPv4Address, "10.1.2.3")]
659659

660-
end_content = (
660+
result = (
661661
b"""<html><body><img src="http://cdn.matrix.org/foo.jpg"></body></html>"""
662662
)
663663

@@ -678,8 +678,8 @@ def test_nonexistent_image(self) -> None:
678678
b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
679679
b'Content-Type: text/html; charset="utf8"\r\n\r\n'
680680
)
681-
% (len(end_content),)
682-
+ end_content
681+
% (len(result),)
682+
+ result
683683
)
684684

685685
self.pump()
@@ -688,6 +688,44 @@ def test_nonexistent_image(self) -> None:
688688
# The image should not be in the result.
689689
self.assertNotIn("og:image", channel.json_body)
690690

691+
def test_oembed_failure(self) -> None:
692+
"""If the autodiscovered oEmbed URL fails, ensure some data is returned."""
693+
self.lookups["matrix.org"] = [(IPv4Address, "10.1.2.3")]
694+
695+
result = b"""
696+
<title>oEmbed Autodiscovery Fail</title>
697+
<link rel="alternate" type="application/json+oembed"
698+
href="http://example.com/oembed?url=http%3A%2F%2Fmatrix.org&format=json"
699+
title="matrixdotorg" />
700+
"""
701+
702+
channel = self.make_request(
703+
"GET",
704+
"preview_url?url=http://matrix.org",
705+
shorthand=False,
706+
await_result=False,
707+
)
708+
self.pump()
709+
710+
client = self.reactor.tcpClients[0][2].buildProtocol(None)
711+
server = AccumulatingProtocol()
712+
server.makeConnection(FakeTransport(client, self.reactor))
713+
client.makeConnection(FakeTransport(server, self.reactor))
714+
client.dataReceived(
715+
(
716+
b"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
717+
b'Content-Type: text/html; charset="utf8"\r\n\r\n'
718+
)
719+
% (len(result),)
720+
+ result
721+
)
722+
723+
self.pump()
724+
self.assertEqual(channel.code, 200)
725+
726+
# The image should not be in the result.
727+
self.assertEqual(channel.json_body["og:title"], "oEmbed Autodiscovery Fail")
728+
691729
def test_data_url(self) -> None:
692730
"""
693731
Requesting to preview a data URL is not supported.

0 commit comments

Comments
 (0)