Add `json()` method for HTTP Request and Response classes. (#4612)

* Add `json()` method for HTTP Request and Response classes.

* Raise errors when fetching content to decode as json.

* Update http.py

Co-authored-by: Maximilian Hils <github@maximilianhils.com>
This commit is contained in:
Brad Dixon 2021-06-15 04:39:48 -04:00 committed by GitHub
parent 7dbd171887
commit 4ee6bc79a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 43 additions and 0 deletions

View File

@ -68,6 +68,7 @@ If you depend on these features, please raise your voice in
* New `flow.comment` command to add a comment to the flow. Add `~comment <regex>` filter syntax to search flow comments. (@rbdixon) * New `flow.comment` command to add a comment to the flow. Add `~comment <regex>` filter syntax to search flow comments. (@rbdixon)
* Fix multipart forms losing `boundary` values on edit (@roytu) * Fix multipart forms losing `boundary` values on edit (@roytu)
* `Transfer-Encoding: chunked` HTTP message bodies are now retained if they are below the stream_large_bodies limit. * `Transfer-Encoding: chunked` HTTP message bodies are now retained if they are below the stream_large_bodies limit.
* `json()` method for HTTP Request and Response instances will return decoded JSON body. (@rbdixon)
* --- TODO: add new PRs above this line --- * --- TODO: add new PRs above this line ---
* ... and various other fixes, documentation improvements, dependency version bumps, etc. * ... and various other fixes, documentation improvements, dependency version bumps, etc.

View File

@ -3,6 +3,7 @@ import os
import re import re
import time import time
import urllib.parse import urllib.parse
import json
from dataclasses import dataclass from dataclasses import dataclass
from dataclasses import fields from dataclasses import fields
from email.utils import formatdate from email.utils import formatdate
@ -18,6 +19,7 @@ from typing import Optional
from typing import Tuple from typing import Tuple
from typing import Union from typing import Union
from typing import cast from typing import cast
from typing import Any
from mitmproxy import flow from mitmproxy import flow
from mitmproxy.websocket import WebSocketData from mitmproxy.websocket import WebSocketData
@ -499,6 +501,25 @@ class Message(serializable.Serializable):
if "content-encoding" not in self.headers: if "content-encoding" not in self.headers:
raise ValueError("Invalid content encoding {}".format(repr(encoding))) raise ValueError("Invalid content encoding {}".format(repr(encoding)))
def json(self, **kwargs: Any) -> Any:
"""
Returns the JSON encoded content of the response, if any.
`**kwargs` are optional arguments that will be
passed to `json.loads()`.
Will raise if the content can not be decoded and then parsed as JSON.
*Raises:*
- `json.decoder.JSONDecodeError` if content is not valid JSON.
- `TypeError` if the content is not available, for example because the response
has been streamed.
"""
content = self.get_content(strict=False)
if content is None:
raise TypeError('Message content is not available.')
else:
return json.loads(content, **kwargs)
class Request(Message): class Request(Message):
""" """

View File

@ -1,5 +1,6 @@
import email import email
import time import time
import json
from unittest import mock from unittest import mock
import pytest import pytest
@ -1144,3 +1145,23 @@ class TestMessageText:
r.text = '\udcff' r.text = '\udcff'
assert r.headers["content-type"] == "text/html; charset=utf-8" assert r.headers["content-type"] == "text/html; charset=utf-8"
assert r.raw_content == b"\xFF" assert r.raw_content == b"\xFF"
def test_get_json(self):
req = treq(content=None)
with pytest.raises(TypeError):
req.json()
req = treq(content=b'')
with pytest.raises(json.decoder.JSONDecodeError):
req.json()
req = treq(content=b'{}')
assert req.json() == {}
req = treq(content=b'{"a": 1}')
assert req.json() == {"a": 1}
req = treq(content=b'{')
with pytest.raises(json.decoder.JSONDecodeError):
req.json()