Repository: urllib3/urllib3

Issue:
HEAD response with chunked Transfer-Encoding does not release the connection
Amazon S3 sets "Transfer-Encoding: chunked" in the response even for a HEAD request. For example:
```
DEBUG:botocore.vendored.requests.packages.urllib3.connectionpool:Starting new HTTPS connection (1): s3.amazonaws.com
send: 'HEAD /timur-test/garbage HTTP/1.1\r\nHost: s3.amazonaws.com\r\nAccept-Encoding: identity\r\n\r\n'
reply: 'HTTP/1.1 301 Moved Permanently\r\n'
header: x-amz-bucket-region: us-west-2
header: x-amz-request-id: 4E83FBED76CBB9BB
header: x-amz-id-2: dxdu+gwK6fdzdK2CaA/0yAcBxO7wdSmzRDwo9JRYA7QeMEk8xMkuE5VJ2AiabWWSqpi+IHweXqA=
header: Content-Type: application/xml
header: Transfer-Encoding: chunked
header: Date: Fri, 21 Jul 2017 06:41:02 GMT
header: Server: AmazonS3
```
This causes the connection to not be returned to the pool after calling `stream()` or `read_chunked()` (which `stream()` is a thin wrapper for in this case). This is due to the following conditional in the `read_chunked()` method: https://github.com/shazow/urllib3/blob/master/urllib3/response.py#L592.

I believe moving this conditional inside the `error_catcher` context manager is the right resolution to this (or calling `release_conn()` explicitly). Will submit a PR for this.


Discussion:

Resolving patch:
diff --git a/urllib3/response.py b/urllib3/response.py
--- a/urllib3/response.py
+++ b/urllib3/response.py
@@ -588,12 +588,12 @@ def read_chunked(self, amt=None, decode_content=None):
                 "Body should be httplib.HTTPResponse like. "
                 "It should have have an fp attribute which returns raw chunks.")
 
-        # Don't bother reading the body of a HEAD request.
-        if self._original_response and is_response_to_head(self._original_response):
-            self._original_response.close()
-            return
-
         with self._error_catcher():
+            # Don't bother reading the body of a HEAD request.
+            if self._original_response and is_response_to_head(self._original_response):
+                self._original_response.close()
+                return
+
             while True:
                 self._update_chunk_length()
                 if self.chunk_left == 0:


Test patch:
diff --git a/test/test_response.py b/test/test_response.py
--- a/test/test_response.py
+++ b/test/test_response.py
@@ -3,6 +3,7 @@
 from io import BytesIO, BufferedReader
 
 import pytest
+import mock
 
 from urllib3.response import HTTPResponse
 from urllib3.exceptions import (
@@ -606,6 +607,22 @@ def test_chunked_response_with_extensions(self):
         resp = HTTPResponse(r, preload_content=False, headers={'transfer-encoding': 'chunked'})
         assert stream == list(resp.stream())
 
+    def test_chunked_head_response(self):
+        r = httplib.HTTPResponse(MockSock, method='HEAD')
+        r.chunked = True
+        r.chunk_left = None
+        resp = HTTPResponse('',
+                            preload_content=False,
+                            headers={'transfer-encoding': 'chunked'},
+                            original_response=r)
+        assert resp.chunked is True
+
+        resp.supports_chunked_reads = lambda: True
+        resp.release_conn = mock.Mock()
+        for _ in resp.stream():
+            continue
+        resp.release_conn.assert_called_once_with()
+
     def test_get_case_insensitive_headers(self):
         headers = {'host': 'example.com'}
         r = HTTPResponse(headers=headers)