diff --git a/.sampo/changesets/doughty-princess-loviatar.md b/.sampo/changesets/doughty-princess-loviatar.md new file mode 100644 index 00000000..c3c4a218 --- /dev/null +++ b/.sampo/changesets/doughty-princess-loviatar.md @@ -0,0 +1,5 @@ +--- +pypi/posthog: patch +--- + +Fall back to uncompressed uploads when gzip compression fails diff --git a/posthog/request.py b/posthog/request.py index de16f33a..18111894 100644 --- a/posthog/request.py +++ b/posthog/request.py @@ -240,13 +240,16 @@ def post( log.debug("making request: %s to url: %s", data, url) headers = {"Content-Type": "application/json", "User-Agent": USER_AGENT} if gzip: - headers["Content-Encoding"] = "gzip" - buf = BytesIO() - with GzipFile(fileobj=buf, mode="w") as gz: - # 'data' was produced by json.dumps(), - # whose default encoding is utf-8. - gz.write(cast(str, data).encode("utf-8")) - data = buf.getvalue() + try: + buf = BytesIO() + with GzipFile(fileobj=buf, mode="w") as gz: + # 'data' was produced by json.dumps(), + # whose default encoding is utf-8. + gz.write(cast(str, data).encode("utf-8")) + data = buf.getvalue() + headers["Content-Encoding"] = "gzip" + except Exception as exc: + log.warning("failed to gzip request body, sending uncompressed: %s", exc) res = (session or _get_session()).post( url, data=data, headers=headers, timeout=timeout diff --git a/posthog/test/test_request.py b/posthog/test/test_request.py index 4cae68f9..f1ab7032 100644 --- a/posthog/test/test_request.py +++ b/posthog/test/test_request.py @@ -1,5 +1,6 @@ import json import unittest +import zlib from datetime import date, datetime from unittest import mock @@ -179,6 +180,31 @@ def test_post_sends_bytes_payload_with_gzip(self): self.assertIsInstance(data, bytes) self.assertEqual(headers["Content-Encoding"], "gzip") + def test_post_falls_back_to_uncompressed_payload_when_gzip_fails(self): + for compression_error in [OSError("boom"), zlib.error("boom")]: + with self.subTest(compression_error=type(compression_error)): + mock_response = requests.Response() + mock_response.status_code = 200 + mock_session = mock.MagicMock() + mock_session.post.return_value = mock_response + + with mock.patch.object( + request_module, "GzipFile", side_effect=compression_error + ): + request_module.post( + TEST_API_KEY, + host="https://test.posthog.com", + path="/batch/", + gzip=True, + session=mock_session, + batch=[], + ) + + data = mock_session.post.call_args.kwargs["data"] + headers = mock_session.post.call_args.kwargs["headers"] + self.assertIsInstance(data, str) + self.assertNotIn("Content-Encoding", headers) + def test_datetime_serialization(self): data = {"created": datetime(2012, 3, 4, 5, 6, 7, 891011)} result = json.dumps(data, cls=DatetimeSerializer)