ionrock/cachecontrol — pull request #25 by dstufft (2014-04-23 12:16:47+00:00)
Title: Lazily cache the response once it has been naturally consumed

This makes it so ``stream=True`` works with CacheControl again. It does this by wrapping the underlying file object that an urllib3 ``HTTPResponse`` class uses with a proxy object which will tee the read data into a buffer, and then once the underlying file object is closed it will execute the registered callback. In this case the callback is the method that will actually store the response in the cache.

The effect of this is that caching happens when the response is consumed, instead of CacheControl consuming the response to be able to cache it. This could be minorly backwards incompatible in that unless the caller of the code actually consumes the entire response (with ``stream=True``) the response won't actually be cached whereas before it was because CacheControl consumed it for them. There is no difference for calls without ``stream=True``.

It's hard to show an example of this working, however if you run:

```python
from cachecontrol import CacheControl
import requests
import logging; logging.basicConfig(level=logging.DEBUG)
s = CacheControl(requests.session())
r = s.get("https://pypi.python.org/packages/source/m/matplotlib/matplotlib-1.3.1.tar.gz", stream=True)
for i, c in enumerate(r.iter_content(4096)):
print("Chunk #{}".format(i))
break
```

Using ``time`` you can see the difference.

**Without this PR:**

```console
$ time python check.py
INFO:requests.packages.urllib3.connectionpool:Starting new HTTPS connection (1): pypi.python.org
DEBUG:requests.packages.urllib3.connectionpool:"GET /packages/source/m/matplotlib/matplotlib-1.3.1.tar.gz HTTP/1.1" 200 42702580
Chunk #0
python t.py 0.53s user 0.45s system 12% cpu 7.821 total
```

**With this PR:**

```console
$ time python check.py
INFO:requests.packages.urllib3.connectionpool:Starting new HTTPS connection (1): pypi.python.org
DEBUG:requests.packages.urllib3.connectionpool:"GET /packages/source/m/matplotlib/matplotlib-1.3.1.tar.gz HTTP/1.1" 200 42702580
Chunk #0
python t.py 0.14s user 0.03s system 20% cpu 0.803 total
```

The shorter time being because it only actually downloads the first chunk vs downloading the entire thing.