Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🔧 relax strictness around Response.json(...) that checked Content-Type prior to decoding #210

Merged
merged 3 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
version: 2

build:
os: ubuntu-22.04
os: ubuntu-24.04
tools:
python: "3.10"
python: "3.12"

sphinx:
configuration: docs/conf.py
Expand Down
8 changes: 8 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
Release History
===============

3.12.3 (2025-01-28)
-------------------

**Changed**
- Relaxed strict compliance on JSON parsing. We brought strict compliance into Niquests, a response
must explicitly set `Content-Type: application/json` or alike prior to attempt parsing the JSON string.
We decided to relax that constraint as old and bad-behaving server may send JSON with missing or broken Content-Type.

3.12.2 (2025-01-22)
-------------------

Expand Down
2 changes: 1 addition & 1 deletion docs/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Used by RTD to generate docs.
Sphinx==7.2.6
sphinx-copybutton==0.5.2
urllib3.future>=2.1.900
urllib3.future>=2.12.900
wassima>=1,<2
kiss_headers>=2,<4
furo>=2023.9.10
4 changes: 2 additions & 2 deletions src/niquests/__version__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
__url__: str = "https://niquests.readthedocs.io"

__version__: str
__version__ = "3.12.2"
__version__ = "3.12.3"

__build__: int = 0x031202
__build__: int = 0x031203
__author__: str = "Kenneth Reitz"
__author_email__: str = "me@kennethreitz.org"
__license__: str = "Apache-2.0"
Expand Down
16 changes: 11 additions & 5 deletions src/niquests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1444,8 +1444,8 @@ def json(self, **kwargs: typing.Any) -> typing.Any:
contain valid json or if content-type is not about json.
"""

if not self.content or "json" not in self.headers.get("content-type", "").lower():
raise RequestsJSONDecodeError("response content is not JSON", self.text or "", 0)
if not self.content:
raise RequestsJSONDecodeError("response content is not JSON", "", 0)

if not self.encoding:
# No encoding set. JSON RFC 4627 section 3 states we should expect
Expand All @@ -1467,10 +1467,13 @@ def json(self, **kwargs: typing.Any) -> typing.Any:
).best()

if encoding_guess is not None:
self.encoding = encoding_guess.encoding
try:
return _json.loads(str(encoding_guess), **kwargs)
except _json.JSONDecodeError as e:
raise RequestsJSONDecodeError(e.msg, e.doc, e.pos)
else:
self.encoding = "utf-8" # try Unicode anyway[...]

plain_content = self.text

Expand Down Expand Up @@ -1813,8 +1816,8 @@ async def text(self) -> str | None: # type: ignore[override]
async def json(self, **kwargs: typing.Any) -> typing.Any: # type: ignore[override]
content = await self.content

if not content or "json" not in self.headers.get("content-type", "").lower():
raise RequestsJSONDecodeError("response content is not JSON", await self.text or "", 0)
if not content:
raise RequestsJSONDecodeError("response content is not JSON", "", 0)

if not self.encoding:
# No encoding set. JSON RFC 4627 section 3 states we should expect
Expand All @@ -1836,15 +1839,18 @@ async def json(self, **kwargs: typing.Any) -> typing.Any: # type: ignore[overri
).best()

if encoding_guess is not None:
self.encoding = encoding_guess.encoding
try:
return _json.loads(str(encoding_guess), **kwargs)
except _json.JSONDecodeError as e:
raise RequestsJSONDecodeError(e.msg, e.doc, e.pos)
else:
self.encoding = "utf-8"

plain_content = await self.text

if plain_content is None:
raise RequestsJSONDecodeError("response cannot lead to decodable JSON", "", 0)
raise RequestsJSONDecodeError("response cannot lead to unserializable JSON", "", 0)

try:
return _json.loads(plain_content, **kwargs)
Expand Down
Loading