Feature Request: Response MaxFilesize and MaxConnectionTimeout

Total Request Timeout is a request in #1928, but I think it could be implemented at the same time as a Filesize Max.

As a user of requests, there are 2 issues I often run into:
- I don't necessarily know how large the remote file is. It is 100k or 2GB?  Large files can create issues based on my machine's memory and filesystem.  I can't trust the data in response headers, as it's not always provided or correct.
- I can often encounter slow connections or data streams, where information is continually provided.  The current read/connection timeouts handle the amount of time between data being sent -- but not the overall amount of time.

Both of these issues can be handled by developers using the current requests API, but variants of these issues often appear on StackOverflow and this issue tracker -- and  I think they could be more elegantly handled within requests and (mostly) a single chunk of code.

My suggestion is the following:
- new (default=None) arguments for `max_filesize` and `max_connectiontime`
- if either are set, the response is read in chunks (r.raw.read)
- if the full response is read, the data is transferred to the content stash
- if only a portion of the response is read, an Exception is raised.  details about the connection and the read response data are available on the exception instance.

I think this logic would mostly go in `requests.apapters.HTTPAdapter.send`, but I could be wrong


There's been some discussion about max_connectiontime in the past. It is pretty difficult to do inline; the best way to implement this would probably be to put the request in a thread and put a `threading.Timer` on the thread so it either completes or fails in a specified amount of time.

Maybe this could go in requests-toolbelt? Or an example of how to implement this, for people that want it?


This is an interesting proposal, that I think suffers from being for an extremely specific use-case that is relatively uncommon. Let's take it in two parts.

First, `max_connectiontime`. `time` in this context is underspecified. What do we mean? CPU time? On a heavily loaded CPU you may not get scheduled and so this can turn into an extremely long wall-clock duration. Wall-clock time? This is more likely, but it still allows for us to be resource starved out, and to achieve it correctly requires that we register for signals from the OS, which is an extremely unpleasant thing for a library to do. For us to pick which of these the user meant is extremely presumptuous, especially when, as @kevinburke says, it's not that hard for the user to do themselves.

The max file-size is an interesting idea, but fundamentally starts adding some weirdness to the API. Firstly, what does `requests.send(url, stream=False, max_filesize=80)` mean? It should never be possible to write a mutually contradictory line of code in this manner. I'd be _more_ open to having this be a property on a `Response` object, realistically I think I'm pretty happy with the situation as is. I'd be interested to hear from the other maintainers though.


I should also mention another key aspect of API design: the more you put in, the harder it is to work with. Good design comes from restriction and constraint, and if we added an option for every way you might possibly want to work with HTTP our API documentation will be a million pages long and incomprehensible. We should make popular things easy, and provide tools for building the less popular parts, but at some stage we need to stop adding features. This means that, at this stage, I'm a default -1 on new features unless they can be extremely well justified.


I'm also a default -1 on every new feature proposed and -2 on these proposed features. (In fact I'm confused as to why https://github.com/kennethreitz/requests/issues/1928 is even still open.)

That said, why does there need to be a maximum filesize enforced by requests. Why aren't you using streaming and detecting this yourself? I feel like this is another potential use-case for something like [tee](https://github.com/sigmavirus24/requests-toolbelt/issues/41). That said, you've given us no real justification or real world examples with which to experiment. You've described a "content stash" that has no basis in anything this library does and did not define it. The feature request in general is poorly specified and founded in what appear to be extreme use cases.


"Why does there need to be a maximum filesize enforced by requests. Why aren't you using streaming and detecting this yourself?"

I recently implemented streaming and chunking.  It wasn't difficult for me to implement.

However:
- This appears to be a question/need that often occurs on StackOverflow and in this issue tracker
- Because there are no limits to filesize in the requests library, it is easy for a user of the library to request a large file or endless stream of data and not realize this is happening. This can lead to issues with memory and disk usage. 
- Dealing with a chunked read is a bit of an advanced topic as it deals with manipulating the `raw` file-like object.  While it is something that is old and familiar for people with a few years of Python experience, it's not something that is easy for junior developers.  Some sort `max_filesize` argument or a `safe_read` function could encapsulate this logic and make it accessible to all.

"You've described a "content stash" that has no basis in anything this library does and did not define it."
- The response object has a `.content` property/method (https://github.com/kennethreitz/requests/blob/master/requests/models.py#L714-L736)
- Said property iterates through the `.iter_content()` filelike object (https://github.com/kennethreitz/requests/blob/master/requests/models.py#L639-L683) 
- The read content is then stored in an internal stash named `._content` (https://github.com/kennethreitz/requests/blob/master/requests/models.py#L539-L540)

I think I was wrong about placement, and it would better function as a method in Response as @Lukasa noted.  What I'm essentially suggesting, is extending the library to have a safeguard around the call to `.iter_content`.  The data would be read in chunks, and stored in the object's `._content` stash.  If the entirety of the data is read, everything functions as normal.  If the filesize is exceeded, an exception is raised.  It would make a defensive programming concept relatively accessible.

This is in line with stdlib functions like `file.read` and `httlib.read` (and consquently urllib) that all support a form of ranged reads without having to loop over chunks.  It is possible to do a ranged read on the `raw` property of the response object, but it's a looped chunk read and it appears to possibly have the potential for unintentionally changing some internal flags on the response object and create issues with future calls to `.content`.

I think features/recipes like this could work in requests-toolbelt if there were an ability/hook to register consumed content into the response object so that the API doesn't change for end users.  For example, if I were to read everything by chunks into a variable, I could them call "Response._register_content(content)" which would set  `Response._content`, and `Response._content_consumed`.