Skip to content

Commit 971da2b

Browse files
fix(chrome): add robust CfT JSON parsing fallback for compressed responses (#701) (#724)
1 parent 0ce0690 commit 971da2b

2 files changed

Lines changed: 83 additions & 2 deletions

File tree

tests/test_chromium_driver.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import os
2+
import gzip
3+
import json
4+
import zlib
25

36
import pytest
47

@@ -386,3 +389,39 @@ def test_chrome_115_plus_uses_chrome_for_testing_macos_download_url_after_milest
386389
CHROME_FOR_TESTING_LATEST_VERSIONS_PER_MILESTONE_URL,
387390
CHROME_FOR_TESTING_KNOWN_GOOD_VERSIONS_URL,
388391
]
392+
393+
394+
def test_chromium_cft_json_parser_handles_gzip_compressed_response():
395+
class Response:
396+
headers = {"Content-Encoding": "gzip"}
397+
text = ""
398+
content = gzip.compress(
399+
json.dumps({"builds": {"120.0.6099": {"version": "120.0.6099.109"}}}).encode("utf-8")
400+
)
401+
402+
@staticmethod
403+
def json():
404+
raise ValueError("broken json parser")
405+
406+
driver, _ = chrome_driver_for(browser_version="120.0.6099.71", responses={})
407+
parsed = driver._parse_json_response(Response())
408+
409+
assert parsed["builds"]["120.0.6099"]["version"] == "120.0.6099.109"
410+
411+
412+
def test_chromium_cft_json_parser_handles_deflate_compressed_response():
413+
class Response:
414+
headers = {"Content-Encoding": "deflate"}
415+
text = ""
416+
content = zlib.compress(
417+
json.dumps({"versions": [{"version": "120.0.6099.109"}]}).encode("utf-8")
418+
)
419+
420+
@staticmethod
421+
def json():
422+
raise ValueError("broken json parser")
423+
424+
driver, _ = chrome_driver_for(browser_version="120.0.6099.71", responses={})
425+
parsed = driver._parse_json_response(Response())
426+
427+
assert parsed["versions"][0]["version"] == "120.0.6099.109"

webdriver_manager/drivers/chrome.py

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
from webdriver_manager.core.logger import log
55
from webdriver_manager.core.os_manager import ChromeType
66

7+
import gzip
78
import json
9+
import zlib
810

911
CHROME_FOR_TESTING_LATEST_PATCH_VERSIONS_PER_BUILD_URL = (
1012
"https://googlechromelabs.github.io/chrome-for-testing/latest-patch-versions-per-build.json"
@@ -142,7 +144,7 @@ def _get_cft_json(self, url):
142144
response = self._http_client.get(url)
143145

144146
try:
145-
return json.loads(response.text)
147+
return self._parse_json_response(response)
146148
except ValueError as error:
147149
raise ValueError(
148150
f"Could not parse Chrome for Testing metadata from {url}."
@@ -163,7 +165,12 @@ def _raise_missing_chromedriver_version(self, browser_version, checked_urls, det
163165
def get_url_for_version_and_platform(self, browser_version, platform):
164166
url = "https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json"
165167
response = self._http_client.get(url)
166-
data = response.json()
168+
try:
169+
data = self._parse_json_response(response)
170+
except ValueError as error:
171+
raise ValueError(
172+
f"Could not parse Chrome for Testing metadata from {url}."
173+
) from error
167174
versions = data["versions"]
168175

169176
if version.parse(browser_version) >= version.parse("115"):
@@ -189,3 +196,38 @@ def get_url_for_version_and_platform(self, browser_version, platform):
189196
return d["url"]
190197

191198
raise Exception(f"No such driver version {browser_version} for {platform}")
199+
200+
def _parse_json_response(self, response):
201+
try:
202+
return response.json()
203+
except Exception:
204+
pass
205+
206+
text = getattr(response, "text", None)
207+
if text:
208+
try:
209+
return json.loads(text)
210+
except ValueError:
211+
pass
212+
213+
raw = getattr(response, "content", None)
214+
if not raw:
215+
raise ValueError("Response body is empty")
216+
217+
headers = getattr(response, "headers", {}) or {}
218+
encoding = (headers.get("Content-Encoding") or "").lower().strip()
219+
220+
if encoding == "gzip":
221+
raw = gzip.decompress(raw)
222+
elif encoding == "br":
223+
try:
224+
import brotli
225+
except ImportError as error:
226+
raise ValueError(
227+
"Response is brotli-compressed but brotli package is not installed"
228+
) from error
229+
raw = brotli.decompress(raw)
230+
elif encoding == "deflate":
231+
raw = zlib.decompress(raw)
232+
233+
return json.loads(raw.decode("utf-8"))

0 commit comments

Comments
 (0)