Skip to content
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: 4 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
`content_type` is now used.
* Added strict_match to urlencoded_params_matcher, enabling partial request parameter
matching.
* Default headers (such as ``Content-Type``, ``Date`` and ``Server``) are now stripped
from recorded files regardless of header name case. Previously, responses recorded from
servers that send lowercase header names (for example over HTTP/2) kept these redundant
headers in the generated file.

0.26.0
------
Expand Down
12 changes: 8 additions & 4 deletions responses/_recorder.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,15 @@ def _remove_default_headers(data: "Any") -> "Any":
"Connection",
"Content-Encoding",
]
# HTTP header names are case-insensitive, and HTTP/2 servers send them
# lowercase, so match without regard to case.
keys_to_remove_lower = {key.lower() for key in keys_to_remove}
for i, response in enumerate(data["responses"]):
for key in keys_to_remove:
if key in response["response"]["headers"]:
del data["responses"][i]["response"]["headers"][key]
if not response["response"]["headers"]:
headers = data["responses"][i]["response"]["headers"]
for key in list(headers):
if key.lower() in keys_to_remove_lower:
del headers[key]
if not headers:
del data["responses"][i]["response"]["headers"]
return data

Expand Down
39 changes: 39 additions & 0 deletions responses/tests/test_recorder.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import responses
from responses import _recorder
from responses._recorder import _dump
from responses._recorder import _remove_default_headers

try:
import tomli as _toml
Expand Down Expand Up @@ -67,6 +68,44 @@ def get_data(host, port):
return data


def test_remove_default_headers_is_case_insensitive():
"""Default headers should be stripped regardless of their case.

HTTP/2 servers send header names in lowercase, so the recorded file may
contain e.g. ``content-type`` / ``date`` rather than ``Content-Type`` /
``Date``. These still need to be removed, otherwise the recorded file
keeps verbose default headers (and, for content-type, a value that is
already stored in the ``content_type`` field).
"""
data = {
"responses": [
{
"response": {
# mixed lower/title/upper case to pin the case-insensitive contract
"headers": {
"content-type": "application/json",
"Date": "Mon, 01 Jan 2024 00:00:00 GMT",
"SERVER": "nginx",
"x-custom": "keep-me",
}
}
},
{
# a response whose headers are all default ones is left without a
# "headers" key at all
"response": {
"headers": {"content-length": "12", "connection": "keep-alive"}
}
},
]
}

result = _remove_default_headers(data)

assert result["responses"][0]["response"]["headers"] == {"x-custom": "keep-me"}
assert "headers" not in result["responses"][1]["response"]


class TestRecord:
def setup_method(self):
self.out_file = Path("response_record")
Expand Down
Loading