From 7b9b8cf9996a204b851ea65f1dfaf1ba9f35f6c1 Mon Sep 17 00:00:00 2001 From: Patrick Talbert Date: Wed, 10 Jul 2024 15:10:17 +0200 Subject: [PATCH 1/2] recorder: use a case-insensitive HTTP headers check _recorder._remove_default_headers() does a case-sensitive check for HTTP header field names are case insensitive. The result is that for example, a lower-case 'content-type' field will not be removed. Later, when _add_from_file tries to load the data using RequestsMock.add(), a RuntimeError exception will be raised when it finds the 'content_type' kwarg was passed and 'content-type' is in the 'headers' kwargs. Fix this by simply having _remove_default_headers() do a case-insensitive check. Signed-off-by: Patrick Talbert --- responses/_recorder.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/responses/_recorder.py b/responses/_recorder.py index 64533e29..beb03afc 100644 --- a/responses/_recorder.py +++ b/responses/_recorder.py @@ -42,17 +42,18 @@ def _remove_default_headers(data: "Any") -> "Any": record functionality. """ if isinstance(data, dict): + # Items in this keys_to_remove list must be lower case! keys_to_remove = [ - "Content-Length", - "Content-Type", - "Date", - "Server", - "Connection", - "Content-Encoding", + "content-length", + "content-type", + "date", + "server", + "connection", + "content-encoding", ] for i, response in enumerate(data["responses"]): - for key in keys_to_remove: - if key in response["response"]["headers"]: + for key in [key for key in response["response"]["headers"]]: + if key.lower() in keys_to_remove: del data["responses"][i]["response"]["headers"][key] if not response["response"]["headers"]: del data["responses"][i]["response"]["headers"] From 8d2bfe78d482313d202783c17216fa796a15cdc4 Mon Sep 17 00:00:00 2001 From: Patrick Talbert Date: Wed, 10 Jul 2024 18:55:50 +0200 Subject: [PATCH 2/2] recorder: set content_type for Response in Recorder._on_request Recorder._on_request does not set the content_type parameter when creating a new Response instance. When content_type is not set the Response.__init__ will always set the value to "text/plain". This means the recording file is always written with that content type and when responses are replayed they may not have their original content-type header value. For example, the python-gitlab package expects the response header content type to be 'application/json' but this information is in the recording and playing it back will fail. Fix this by having _on_request() pass in the content_type of the requests_response.header. Signed-off-by: Patrick Talbert --- responses/_recorder.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/responses/_recorder.py b/responses/_recorder.py index beb03afc..00723508 100644 --- a/responses/_recorder.py +++ b/responses/_recorder.py @@ -144,13 +144,20 @@ def _on_request( headers_values = { key: value for key, value in requests_response.headers.items() } - responses_response = Response( - method=str(request.method), - url=str(requests_response.request.url), - status=requests_response.status_code, - body=requests_response.text, - headers=headers_values, - ) + + response_params = { + "method": str(request.method), + "url": str(requests_response.request.url), + "status": requests_response.status_code, + "body": requests_response.text, + "headers": headers_values, + } + + # If the header has a content type then pass it in. + if content_type := requests_response.headers.get("content-type"): + response_params["content_type"] = content_type + + responses_response = Response(**response_params) self._registry.add(responses_response) return requests_response