From c9d587f63fff47b372bac40307bd4adb873d4399 Mon Sep 17 00:00:00 2001 From: Paul Selkirk Date: Sat, 2 Dec 2023 21:55:09 -0500 Subject: [PATCH 1/5] fix: Wrap weasyprint to catch exceptions (#6324) --- ietf/doc/tests.py | 6 ++++++ ietf/doc/views_doc.py | 12 +++++++++++- ietf/templates/doc/weasyprint_failed.html | 17 +++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 ietf/templates/doc/weasyprint_failed.html diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index ace55a0d79..b740e3398f 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -2843,6 +2843,12 @@ def test_pdfized(self): self.should_succeed(dict(name=rfc.name,rev=f'{r:02d}',ext=ext)) self.should_404(dict(name=rfc.name,rev='02')) + import socket + socket.socket = lambda *args, **kwargs: None + url = urlreverse(self.view, kwargs=dict(name=rfc.name)) + r = self.client.get(url) + self.assertEqual(r.status_code, 504) + class NotifyValidationTests(TestCase): def test_notify_validation(self): valid_values = [ diff --git a/ietf/doc/views_doc.py b/ietf/doc/views_doc.py index a3548fa921..eea945c068 100644 --- a/ietf/doc/views_doc.py +++ b/ietf/doc/views_doc.py @@ -51,6 +51,7 @@ from django import forms from django.contrib.staticfiles import finders +from weasyprint.urls import URLFetchingError import debug # pyflakes:ignore @@ -974,7 +975,16 @@ def document_pdfized(request, name, rev=None, ext=None): if not os.path.exists(doc.get_file_name()): raise Http404("File not found: %s" % doc.get_file_name()) - pdf = doc.pdfized() + try: + pdf = doc.pdfized() + except URLFetchingError: + # retry once, then give up + try: + pdf = doc.pdfized() + except URLFetchingError as exception: + return render(request, "doc/weasyprint_failed.html", + dict(error=f'{type(exception).__name__}: {exception}'), + status=504) if pdf: return HttpResponse(pdf,content_type='application/pdf') else: diff --git a/ietf/templates/doc/weasyprint_failed.html b/ietf/templates/doc/weasyprint_failed.html new file mode 100644 index 0000000000..b4e87d26b3 --- /dev/null +++ b/ietf/templates/doc/weasyprint_failed.html @@ -0,0 +1,17 @@ +{# Copyright The IETF Trust 2023, All Rights Reserved #} +{% extends "base.html" %} +{% load origin %} +{% block title %}Network error while rendering PDF{% endblock %} +{% block content %} + {% origin %} +

Network error while rendering PDF

+

+ A network error was encountered while trying to render your document as PDF. +

+

+ We have retried the operation, and it failed again. You may want to wait a bit, and try again. +

+

+ The error was: {{ error }} +

+{% endblock %} \ No newline at end of file From 353faaaeab546c533e7cea5092820ba10e0d68d3 Mon Sep 17 00:00:00 2001 From: Paul Selkirk Date: Mon, 4 Dec 2023 14:04:39 -0500 Subject: [PATCH 2/5] test: Restore socket function after test --- ietf/doc/tests.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index b740e3398f..2cae5893ec 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -2844,10 +2844,12 @@ def test_pdfized(self): self.should_404(dict(name=rfc.name,rev='02')) import socket + realsocket = socket.socket socket.socket = lambda *args, **kwargs: None url = urlreverse(self.view, kwargs=dict(name=rfc.name)) r = self.client.get(url) self.assertEqual(r.status_code, 504) + socket.socket = realsocket class NotifyValidationTests(TestCase): def test_notify_validation(self): From 590216881394f5f964059ddabbaac4892b0817cf Mon Sep 17 00:00:00 2001 From: Paul Selkirk Date: Wed, 6 Dec 2023 16:16:54 -0500 Subject: [PATCH 3/5] test: Use mock instead of monkeying with sockets --- ietf/doc/tests.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index 2cae5893ec..25e416fdd0 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -1,4 +1,4 @@ -# Copyright The IETF Trust 2012-2020, All Rights Reserved +# Copyright The IETF Trust 2012-2023, All Rights Reserved # -*- coding: utf-8 -*- @@ -31,6 +31,8 @@ from tastypie.test import ResourceTestCaseMixin +from weasyprint.urls import URLFetchingError + import debug # pyflakes:ignore from ietf.doc.models import ( Document, DocAlias, DocRelationshipName, RelatedDocument, State, @@ -2843,13 +2845,10 @@ def test_pdfized(self): self.should_succeed(dict(name=rfc.name,rev=f'{r:02d}',ext=ext)) self.should_404(dict(name=rfc.name,rev='02')) - import socket - realsocket = socket.socket - socket.socket = lambda *args, **kwargs: None - url = urlreverse(self.view, kwargs=dict(name=rfc.name)) - r = self.client.get(url) - self.assertEqual(r.status_code, 504) - socket.socket = realsocket + with mock.patch('ietf.doc.models.DocumentInfo.pdfized', side_effect=URLFetchingError): + url = urlreverse(self.view, kwargs=dict(name=rfc.name)) + r = self.client.get(url) + self.assertEqual(r.status_code, 504) class NotifyValidationTests(TestCase): def test_notify_validation(self): From 26cc393eed370e48d0eb2393a344f73384cd59a1 Mon Sep 17 00:00:00 2001 From: Paul Selkirk Date: Wed, 13 Dec 2023 11:03:49 -0500 Subject: [PATCH 4/5] refactor: Log the error --- ietf/doc/models.py | 3 +++ ietf/doc/tests.py | 3 ++- ietf/doc/views_doc.py | 12 ++---------- ietf/templates/doc/weasyprint_failed.html | 14 +++++++------- 4 files changed, 14 insertions(+), 18 deletions(-) diff --git a/ietf/doc/models.py b/ietf/doc/models.py index 30d95fbf50..d51ec70df6 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -648,6 +648,9 @@ def pdfized(self): ) except AssertionError: pdf = None + except Exception as e: + log.log('weasyprint failed:'+str(e)) + raise if pdf: cache.set(cache_key, pdf, settings.PDFIZER_CACHE_TIME) return pdf diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index 25e416fdd0..0035e21705 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -2848,7 +2848,8 @@ def test_pdfized(self): with mock.patch('ietf.doc.models.DocumentInfo.pdfized', side_effect=URLFetchingError): url = urlreverse(self.view, kwargs=dict(name=rfc.name)) r = self.client.get(url) - self.assertEqual(r.status_code, 504) + self.assertEqual(r.status_code, 200) + self.assertContains(r, "Error while rendering PDF") class NotifyValidationTests(TestCase): def test_notify_validation(self): diff --git a/ietf/doc/views_doc.py b/ietf/doc/views_doc.py index eea945c068..e992ef7b2c 100644 --- a/ietf/doc/views_doc.py +++ b/ietf/doc/views_doc.py @@ -51,8 +51,6 @@ from django import forms from django.contrib.staticfiles import finders -from weasyprint.urls import URLFetchingError - import debug # pyflakes:ignore from ietf.doc.models import ( Document, DocAlias, DocHistory, DocEvent, BallotDocEvent, BallotType, @@ -977,14 +975,8 @@ def document_pdfized(request, name, rev=None, ext=None): try: pdf = doc.pdfized() - except URLFetchingError: - # retry once, then give up - try: - pdf = doc.pdfized() - except URLFetchingError as exception: - return render(request, "doc/weasyprint_failed.html", - dict(error=f'{type(exception).__name__}: {exception}'), - status=504) + except: + return render(request, "doc/weasyprint_failed.html") if pdf: return HttpResponse(pdf,content_type='application/pdf') else: diff --git a/ietf/templates/doc/weasyprint_failed.html b/ietf/templates/doc/weasyprint_failed.html index b4e87d26b3..985fe12fe1 100644 --- a/ietf/templates/doc/weasyprint_failed.html +++ b/ietf/templates/doc/weasyprint_failed.html @@ -1,17 +1,17 @@ {# Copyright The IETF Trust 2023, All Rights Reserved #} {% extends "base.html" %} {% load origin %} -{% block title %}Network error while rendering PDF{% endblock %} +{% block title %}Error while rendering PDF{% endblock %} {% block content %} {% origin %} -

Network error while rendering PDF

+

Error while rendering PDF

- A network error was encountered while trying to render your document as PDF. + An error was encountered while trying to render your document as PDF. + In case this was a temporary error, you may want to try again in a + little while.

- We have retried the operation, and it failed again. You may want to wait a bit, and try again. -

-

- The error was: {{ error }} + A failure report with details about what happened has been sent to the + server administrators.

{% endblock %} \ No newline at end of file From 73471ff45298d01a1bfc6e6f7520084f9920ba19 Mon Sep 17 00:00:00 2001 From: Paul Selkirk Date: Mon, 18 Dec 2023 17:46:12 -0500 Subject: [PATCH 5/5] fix: Don't catch non-Exception interruptions --- ietf/doc/views_doc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/doc/views_doc.py b/ietf/doc/views_doc.py index e992ef7b2c..ea6cf0ae6b 100644 --- a/ietf/doc/views_doc.py +++ b/ietf/doc/views_doc.py @@ -975,7 +975,7 @@ def document_pdfized(request, name, rev=None, ext=None): try: pdf = doc.pdfized() - except: + except Exception: return render(request, "doc/weasyprint_failed.html") if pdf: return HttpResponse(pdf,content_type='application/pdf')