iter_content with chunk_size=None hangs when used with requests.Session on Python 3

Under some specific circumstances, a loop using `iter_content` ends up hanging after all the content has been iterated over. Specifically, all the following conditions must be met for this to occur:
- You must be using Python 3 (I've tested using 2.7.11 (under which this works) and 3.3.6, 3.4.11 and 3.5.1 (under which this bug occurs).
- Use `requests.Session()`, not `requests`.
- Set `chunk_size=None` in `iter_content`.

Here, have an example:

``` python
# requests_bug.py
import requests

def iterate_through_streamed_content(requests_module_or_session, chunk_size):
    r = requests_module_or_session.get('http://example.org', stream=True)
    r.raise_for_status()
    for chunk in r.iter_content(chunk_size=chunk_size):
        print(len(chunk))


iterate_through_streamed_content(requests, 1024)
print('requests, 1024 works')

iterate_through_streamed_content(requests, None)
print('requests, None works')

iterate_through_streamed_content(requests.Session(), 1024)
print('requests.Session(), 1024 works')

iterate_through_streamed_content(requests.Session(), None)
print('requests.Session(), None works')

print("That's all.")
```

Output 1 (bug does not occur in Python2.7):

```
$ python --version
Python 2.7.11
$ pip install -U requests
Requirement already up-to-date: requests in /home/...
$ python requests_bug.py 
1270
requests, 1024 works
1270
requests, None works
1270
requests.Session(), 1024 works
1270
requests.Session(), None works
That's all.
```

Output 2 (bug occurs in Python3.5):

```
$ python --version
Python 3.5.1
$ pip install -U requests
Requirement already up-to-date: requests in /home/...
$ python requests_bug.py 
1270
requests, 1024 works
1270
requests, None works
1270
requests.Session(), 1024 works
1270
```

...after which the script hangs until you do Ctrl+C.

This is all done on Ubuntu 16.04, using pyenv and its virtualenv feature for testing using different Python versions. (Thank you all for requests, btw! Keep on rocking :+1: )


So I can reproduce this on OSX, and it appears to be an issue in urllib3. [Here](https://github.com/shazow/urllib3/blob/master/urllib3/response.py#L361) to be more specific. The file object isn't marking itself as closed for some reason and the while loop never exits. This happens on both https://httpbin.org and http://example.org but not https://google.com, so Google is doing something to force the connection closed. I'll do a bit more digging and see if I can figure out why the stream doesn't have an EOF.


Just speculation on my part, but may it be related to Keep-Alive? Considering it only occurs when using requests.Session().


Ok, I can reproduce this test under test conditions in urllib3. This suggests that it's orthogonal to the use of `requests.Session`.


Note as well that using `requests` directly is really no different at all: `requests.request` creates a one-off `Session` through which to run all its calls. The _real_ question is why this happens consistently on the _second_ request, which I'm stunningly unsure about at this time.


While we're here, note also that this bug doesn't appear to actually be affected by timeouts, because none of the _socket_ calls take long: we're just busy-looping.


So, this seems relevant:

``` python
Python 2.7.11 (default, Dec 14 2015, 09:18:27) 
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import httplib
>>> hasattr(httplib.HTTPResponse, 'closed')
False
```

``` python
Python 3.5.1 (default, Dec 14 2015, 09:21:15) 
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import http.client
>>> hasattr(http.client.HTTPResponse, 'closed')
True
```


Note that in Python 3.5 `_close_conn` does not call `super().close()`, which means that the closed property never gets set.


That's about where I am at in debugging too @Lukasa. It looks like `isclosed` is being set and will evaluate to `True` but the `closed` attribute is permanently `False`. I'm trying to figure out if this is a bug in `httplib` or `urllib3`'s use of it. Our current check in `is_fp_closed` is checking the `closed` attribute.


The answer is that it doesn't really matter. =)

This behaviour is present in basically all the shipped Python 3 solutions, which means even if it's a bug we need to be bug-compatible with it. I think the best solution is simply to rely on `isclosed` if it's present, in preference to `closed`, which is then in preference to reaching in to the `fp` thing.

Are you interested in writing the PR that fixes this, @nateprewitt?


And, if you are: do you want my test?


Yeah, I'm happy to throw something together if that'll help. Any test code would be greatly appreciated :)


Ok, for now let's close this. @nateprewitt, can you open an equivalent issue on the urllib3 repository? You can use this test code as the example (it should live in `test_socketlevel.py` in the urllib3 repo):

``` python
class TestStream(SocketDummyServerTestCase):
    def test_stream_none_unchunked_response_does_not_hang(self):
        done_event = Event()

        def socket_handler(listener):
            sock = listener.accept()[0]

            buf = b''
            while not buf.endswith(b'\r\n\r\n'):
                buf += sock.recv(65536)

            sock.send(
                b'HTTP/1.1 200 OK\r\n'
                b'Content-Length: 12\r\n'
                b'Content-type: text/plain\r\n'
                b'\r\n'
                b'hello, world'
            )
            done_event.wait(5)
            sock.close()

        self._start_server(socket_handler)
        pool = HTTPConnectionPool(self.host, self.port, retries=False)
        r = pool.request('GET', '/', timeout=1, preload_content=False)

        # Stream should read to the end.
        self.assertEqual([b'hello, world'], list(r.stream(None)))

        done_event.set()
```


Sounds good, will do.