Repository: getsentry/responses

Issue:
responses 0.10.8 introduces an implicit dependency on urllib3>=1.23
responses 0.10.8 introduces these blocks:
https://github.com/getsentry/responses/blob/2d5d9f5ec33f168f097bb92f99243f18f264184c/responses.py#L382-L386

The `msg` param was not introduced until urllib3 1.23. 

`requests` itself permits any `urllib3>=1.21.1,<1.26,!=1.25.0,!=1.25.1`. If `responses` is run in an env with `urllib3<1.23`, this error gets thrown: 
```
    def get_response(self, request):
        if self.body and isinstance(self.body, Exception):
            raise self.body
    
        headers = self.get_headers()
        status = self.status
        body = _handle_body(self.body)
    
        # The requests library's cookie handling depends on the response object
        # having an original response object with the headers as the `msg`, so
        # we give it what it needs.
        orig_response = HTTPResponse(
            body=body,  # required to avoid "ValueError: Unable to determine whether fp is closed."
            msg=headers,
>           preload_content=False,
        )
E       TypeError: __init__() got an unexpected keyword argument 'msg'
```

Perhaps these new blocks can be modified to avoid `msg`? ~~Or a hard requirement for urllib3>=1.23 could be added to the responses setup.py?~~

Discussion:

Patch:
diff --git a/responses.py b/responses.py
index 6148f1d..47c81cf 100644
--- a/responses.py
+++ b/responses.py
@@ -375,21 +375,12 @@ class Response(BaseResponse):
         headers = self.get_headers()
         status = self.status
         body = _handle_body(self.body)
-
-        # The requests library's cookie handling depends on the response object
-        # having an original response object with the headers as the `msg`, so
-        # we give it what it needs.
-        orig_response = HTTPResponse(
-            body=body,  # required to avoid "ValueError: Unable to determine whether fp is closed."
-            msg=headers,
-            preload_content=False,
-        )
         return HTTPResponse(
             status=status,
             reason=six.moves.http_client.responses.get(status),
             body=body,
             headers=headers,
-            original_response=orig_response,
+            original_response=OriginalResponseShim(headers),
             preload_content=False,
         )
 
@@ -414,27 +405,45 @@ class CallbackResponse(BaseResponse):
         if isinstance(body, Exception):
             raise body
 
+        # If the callback set a content-type remove the one
+        # set in add_callback() so that we don't have multiple
+        # content type values.
+        if "Content-Type" in r_headers:
+            headers.pop("Content-Type", None)
+
         body = _handle_body(body)
         headers.extend(r_headers)
 
-        # The requests library's cookie handling depends on the response object
-        # having an original response object with the headers as the `msg`, so
-        # we give it what it needs.
-        orig_response = HTTPResponse(
-            body=body,  # required to avoid "ValueError: Unable to determine whether fp is closed."
-            msg=headers,
-            preload_content=False,
-        )
         return HTTPResponse(
             status=status,
             reason=six.moves.http_client.responses.get(status),
             body=body,
             headers=headers,
-            original_response=orig_response,
+            original_response=OriginalResponseShim(headers),
             preload_content=False,
         )
 
 
+class OriginalResponseShim(object):
+    """
+    Shim for compatibility with older versions of urllib3
+
+    requests cookie handling depends on responses having a property chain of
+    `response._original_response.msg` which contains the response headers [1]
+
+    Using HTTPResponse() for this purpose causes compatibility errors with
+    urllib3<1.23.0. To avoid adding more dependencies we can use this shim.
+
+    [1]: https://github.com/psf/requests/blob/75bdc998e2d/requests/cookies.py#L125
+    """
+
+    def __init__(self, headers):
+        self.msg = headers
+
+    def isclosed(self):
+        return True
+
+
 class RequestsMock(object):
     DELETE = "DELETE"
     GET = "GET"


Test patch:
diff --git a/test_responses.py b/test_responses.py
index 65904de..223c0a7 100644
--- a/test_responses.py
+++ b/test_responses.py
@@ -410,7 +410,11 @@ def test_callback():
     body = b"test callback"
     status = 400
     reason = "Bad Request"
-    headers = {"foo": "bar"}
+    headers = {
+        "foo": "bar",
+        "Content-Type": "application/json",
+        "Content-Length": "13",
+    }
     url = "http://example.com/"
 
     def request_callback(request):
@@ -423,8 +427,9 @@ def test_callback():
         assert resp.text == "test callback"
         assert resp.status_code == status
         assert resp.reason == reason
-        assert "foo" in resp.headers
-        assert resp.headers["foo"] == "bar"
+        assert "bar" == resp.headers.get("foo")
+        assert "application/json" == resp.headers.get("Content-Type")
+        assert "13" == resp.headers.get("Content-Length")
 
     run()
     assert_reset()