`connect` timeout is *not* a TCP connect timeout, but a TCP connect + send the whole HTTP request timeout

`connect` timeout is *not* a TCP connect timeout, but a TCP connect + send the whole HTTP request timeout.

## Expected Result

I expect that the documentation is correct about `connect` timeout:
The [advanced documentation](https://requests.readthedocs.io/en/master/user/advanced/#timeouts) says:
> The connect timeout is the number of seconds Requests will wait for your client to establish a connection to a remote machine (corresponding to the connect()) call on the socket. It’s a good practice to set connect timeouts to slightly larger than a multiple of 3, which is the default TCP packet retransmission window.

## Actual Result

The `connect` timeout seems to be the TCP connect timeout + sending the whole HTTP request.


## Reproduction Steps
```bash
dd if=/dev/zero of=zero.100MB bs=100M count=1
```
```python
import requests

files = {'file': open('zero.100MB', 'rb')}
requests.post('somehost', timeout=(1, 1000), files=files)
```

(It needs a host accepting the large POST, I couldn't find any public right now...)

Result:
```
Traceback (most recent call last):
  File "/usr/local/lib/python3.5/dist-packages/urllib3/connectionpool.py", line 672, in urlopen
    chunked=chunked,
  File "/usr/local/lib/python3.5/dist-packages/urllib3/connectionpool.py", line 387, in _make_request
    conn.request(method, url, **httplib_request_kw)
  File "/usr/lib/python3.5/http/client.py", line 1106, in request
    self._send_request(method, url, body, headers)
  File "/usr/lib/python3.5/http/client.py", line 1151, in _send_request
    self.endheaders(body)
  File "/usr/lib/python3.5/http/client.py", line 1102, in endheaders
    self._send_output(message_body)
  File "/usr/lib/python3.5/http/client.py", line 936, in _send_output
    self.send(message_body)
  File "/usr/lib/python3.5/http/client.py", line 908, in send
    self.sock.sendall(data)
  File "/usr/lib/python3.5/ssl.py", line 891, in sendall
    v = self.send(data[count:])
  File "/usr/lib/python3.5/ssl.py", line 861, in send
    return self._sslobj.write(data)
  File "/usr/lib/python3.5/ssl.py", line 586, in write
    return self._sslobj.write(data)
socket.timeout: The write operation timed out
```

## System Information

    $ python -m requests.help

```
{
  "chardet": {
    "version": "3.0.4"
  },
  "cryptography": {
    "version": ""
  },
  "idna": {
    "version": "2.8"
  },
  "implementation": {
    "name": "CPython",
    "version": "3.6.8"
  },
  "platform": {
    "release": "4.15.0-64-generic",
    "system": "Linux"
  },
  "pyOpenSSL": {
    "openssl_version": "",
    "version": null
  },
  "requests": {
    "version": "2.22.0"
  },
  "system_ssl": {
    "version": "1010100f"
  },
  "urllib3": {
    "version": "1.25.7"
  },
  "using_pyopenssl": false
}
```

## Analysis

Digging into urllib3:
https://github.com/urllib3/urllib3/blob/37ba61a8b8120cbd866d057eaa3936f4b140dee0/src/urllib3/connectionpool.py#L370-L390

It sets the connect timeout on the connection, then validate https, then sends the full request, and only then sets the read timeout on the connection socket.

That being said I could *not* find any urllib3 doc explicitly defining the `connect` timeout as requests does...

Finally:
https://docs.python.org/3.8/library/socket.html#socket.socket.sendall
> Changed in version 3.5: The socket timeout is no more reset each time data is sent successfully. The socket timeout is now the maximum total duration to send all data.

So maybe it did work as a TCP connect timeout + write idle timeout before python 3.5 (which is still not just TCP connect timeout, but closer to it), but now it is clearly not.


Have you raised this on urllib3 side? That sounds like an odd behavioural change 

I think this is a fair criticism of the documentation @nanonyme given the changes in the standard library in 3.5

FWIW https://github.com/python/cpython/commit/8912d1418e6b336c21bbe21c0dd123332bbe5ab5 is the commit where functionality changed

The bug reference in commit is completely wrong, original issue is here https://bugs.python.org/issue23853

https://mail.python.org/pipermail/python-dev/2015-April/139001.html

It seems all in all urllib3 must be refactored not to use sendall if the original behaviour is preferred. Current situation is really bad that urllib3 timeout handling differs depending on Python version. Actually it must be refactored in either case since that is the *only* way to get same behaviour in all target Python implementations.

urllib3 2.0 is what is active in master and has dropped support for < 3.5 iirc so this isn't an issue as far as current development is concerned. A fix to the 1.26 branch is probably worthwhile, but wouldn't guarantee fixing this for everyone using requests who may be using a different urllib3 version

@nanonyme I have not traced this issue down in `requests` dependencies. I stopped at the first issue: `requests` documentation inconsistent with current behavior, as we initially wrote code based on that part of the documentation, and it didn't work as expected.

It's good to know it's due to a breaking change in python 3.5.

Python 3.5 reached end-of-life in September 2020, python 3.4 before that: I think the "different behavior depending on python version" is not really an issue anymore.

We worked around this behavior change by stopping everywhere to use a tuple for timeout, effectively having only a total send request timeout, but the previous/documented behavior seemed better to us: more fine-grained control: we want to support low bandwidth cases where the request sending can be slow but legitimate, and still promptly fail if the TCP is really slow to connect (cannot rely on kernel timeouts which are usually pretty large). For now we gave up for the later, with large global timeouts.

If you "fix" this issue by going back to the previous/documented behavior, it would give us more control back.

I'm not really promising to fix anything. I'm just saying that if urllib3 used socket.send instead of socket.sendall (and implemented sendall-like functionality themselves), then they would not be subject to this weird interpretation of timeouts that is in modern socket module implementation. 

Remember that Requests are a library for getting HTTP/HTTPS Requests, so the connect time out...is a timeout for your method call...then the read timeout occours...if you are having timeouts on tcp beccause the server response is too slow you ned to keep the VC up so....have you tried to enable  TCP Keep-Alive on your host? Is is disabled by default and the  trigger timmer is set to high...so it is unreachable... I did some thing like this in order to enable it from the  GUI, and  the results were great. it have to be done before creating the session...and this is for Linux.... if you have another OS you will need another socker options.

    import socket
    from urllib3.connection import HTTPConnection
    HTTPConnection.default_socket_options = HTTPConnection.default_socket_options + [
        (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
        ,(socket.SOL_TCP, socket.TCP_KEEPIDLE, 45)
        ,(socket.SOL_TCP, socket.TCP_KEEPINTVL, 10)
        ,(socket.SOL_TCP, socket.TCP_KEEPCNT, 6)
         ]**

    from requests import session
    self.session = session()


Cheers!

If you go that route, I'd advise the adapter in the requests-toolbelt made to make setting those options easier

> requests-toolbelt

Yup,but...since it is not updated, will not appy to Windows Clients...caus Windows depreceated the winsck32 library and in order to set the KEEP_ALIVE timers ioctl usage is required.... unless you have a fork for it or an updated lib....

also...there is sys.platform response issue when ussing python 2.x ...


Or you could send a PR to the library to fix those issue and help the maintainers get those patches out to windows users

ok. I will... I am just a TS,  but I can do that,,,