From 7c59362a15dfce538512ff1fce4e07d33a925cfb Mon Sep 17 00:00:00 2001
From: Berker Peksag <berker.peksag@gmail.com>
Date: Sun, 19 May 2019 18:56:15 +0300
Subject: [PATCH] bpo-29183: Fix double exceptions in
 wsgiref.handlers.BaseHandler (GH-12914)

---
 Lib/test/test_wsgiref.py                      | 25 +++++++++++++++++++
 Lib/wsgiref/handlers.py                       | 11 +++++++-
 .../2019-04-22-22-55-29.bpo-29183.MILvsk.rst  |  3 +++
 3 files changed, 38 insertions(+), 1 deletion(-)
 create mode 100644 Misc/NEWS.d/next/Library/2019-04-22-22-55-29.bpo-29183.MILvsk.rst

diff --git a/Lib/test/test_wsgiref.py b/Lib/test/test_wsgiref.py
index 46f88a9443..42432bfbd2 100644
--- a/Lib/test/test_wsgiref.py
+++ b/Lib/test/test_wsgiref.py
@@ -806,6 +806,31 @@ class HandlerTests(TestCase):
 
                 self.assertFalse(stderr.getvalue())
 
+    def testDontResetInternalStateOnException(self):
+        class CustomException(ValueError):
+            pass
+
+        # We are raising CustomException here to trigger an exception
+        # during the execution of SimpleHandler.finish_response(), so
+        # we can easily test that the internal state of the handler is
+        # preserved in case of an exception.
+        class AbortingWriter:
+            def write(self, b):
+                raise CustomException
+
+        stderr = StringIO()
+        environ = {"SERVER_PROTOCOL": "HTTP/1.0"}
+        h = SimpleHandler(BytesIO(), AbortingWriter(), stderr, environ)
+        h.run(hello_app)
+
+        self.assertIn("CustomException", stderr.getvalue())
+
+        # Test that the internal state of the handler is preserved.
+        self.assertIsNotNone(h.result)
+        self.assertIsNotNone(h.headers)
+        self.assertIsNotNone(h.status)
+        self.assertIsNotNone(h.environ)
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/Lib/wsgiref/handlers.py b/Lib/wsgiref/handlers.py
index 834073d500..31360e5878 100644
--- a/Lib/wsgiref/handlers.py
+++ b/Lib/wsgiref/handlers.py
@@ -183,7 +183,16 @@ class BaseHandler:
                 for data in self.result:
                     self.write(data)
                 self.finish_content()
-        finally:
+        except:
+            # Call close() on the iterable returned by the WSGI application
+            # in case of an exception.
+            if hasattr(self.result, 'close'):
+                self.result.close()
+            raise
+        else:
+            # We only call close() when no exception is raised, because it
+            # will set status, result, headers, and environ fields to None.
+            # See bpo-29183 for more details.
             self.close()
 
 
diff --git a/Misc/NEWS.d/next/Library/2019-04-22-22-55-29.bpo-29183.MILvsk.rst b/Misc/NEWS.d/next/Library/2019-04-22-22-55-29.bpo-29183.MILvsk.rst
new file mode 100644
index 0000000000..1d19f191ee
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-04-22-22-55-29.bpo-29183.MILvsk.rst
@@ -0,0 +1,3 @@
+Fix double exceptions in :class:`wsgiref.handlers.BaseHandler` by calling
+its :meth:`~wsgiref.handlers.BaseHandler.close` method only when no
+exception is raised.
-- 
2.40.0