On requests 2.8.0+ chunked requests can cause an unhandled urllib3.exceptions.NewConnectionError

When we perform a chunked request, we cause a call to `HTTPConnection._new_conn` through `HTTPConnection.connect` which can raise an instance of `urllib3.exceptions.NewConnectionError`.

Code to reproduce

``` py
import requests

requests.post('https://is.invalid', data=(str(i) for i in range(2)))
```

cc @stevelle


Yup, this looks wrong. Lemme get a patch up and running.


Hmm. Interestingly, I can't reproduce this on the current master.


Yup, so this appears to be fixed in master but I can't work out why. I'll close until someone else can verify that I'm losing my mind.


Huh, so this raises the proper exception now. @stevelle and I specifically tested this on 2.8.1 and were seeing the exception from urllib3 bubble up. On master, I see the (expected) `requests.exceptions.ConnectionError` (which wraps the `NewConnectionError` from urllib3).

This is especially baffling because the exception was coming from the line where `endheaders` is called on `low_conn` and the difference between 2.8.1 and HEAD is:

``` diff
diff --git a/requests/adapters.py b/requests/adapters.py
index 7682db0..cecf6c2 100644
--- a/requests/adapters.py
+++ b/requests/adapters.py
@@ -8,6 +8,7 @@ This module contains the transport adapters that Requests uses to define
 and maintain connections.
 """

+import os.path
 import socket

 from .models import Response
@@ -185,10 +186,15 @@ class HTTPAdapter(BaseAdapter):
                 raise Exception("Could not find a suitable SSL CA certificate bundle.")

             conn.cert_reqs = 'CERT_REQUIRED'
-            conn.ca_certs = cert_loc
+
+            if not os.path.isdir(cert_loc):
+                conn.ca_certs = cert_loc
+            else:
+                conn.ca_cert_dir = cert_loc
         else:
             conn.cert_reqs = 'CERT_NONE'
             conn.ca_certs = None
+            conn.ca_cert_dir = None

         if cert:
             if not isinstance(cert, basestring):
@@ -394,7 +400,15 @@ class HTTPAdapter(BaseAdapter):
                         low_conn.send(b'\r\n')
                     low_conn.send(b'0\r\n\r\n')

-                    r = low_conn.getresponse()
+                    # Receive the response from the server
+                    try:
+                        # For Python 2.7+ versions, use buffering of HTTP
+                        # responses
+                        r = low_conn.getresponse(buffering=True)
+                    except TypeError:
+                        # For compatibility with Python 2.6 versions and back
+                        r = low_conn.getresponse()
+
                     resp = HTTPResponse.from_httplib(
                         r,
                         pool=conn,
```

In other words, the exception should be raised before we get to this try/except logic.

I can't imagine this would be system specific, but I'll toy around with the server that @stevelle and I were testing on yesterday to see if I can reproduce this on master there. (I'm using OSX right now, we were using Ubuntu 14.04 inside a virtualenv yesterday.)


This issue seems to be happening on requests 2.9.1. You can check the [requests_issue_2887.py](https://gist.github.com/suoto/2c3e9fe51b38208dfddd4144c0ae80cb) gist, it has the relevant code that seems to reproduce this issue every time I run.

At least it seems the same issue to me, I'm sorry if it's a different issue.

For the record, the shell output is as follows

```
Bottle v0.12.9 server starting up (using WaitressServer())...
Listening on http://localhost:50000/
Hit Ctrl-C to quit.

serving on http://127.0.0.1:50000
Traceback (most recent call last):
  File "/home/souto/dummy.py", line 39, in <module>
    main()
  File "/home/souto/dummy.py", line 33, in main
    requests.post('http://localhost:50000/foobar')
  File "/home/souto/.local/lib/python2.7/site-packages/requests/api.py", line 107, in post
    return request('post', url, data=data, json=json, **kwargs)
  File "/home/souto/.local/lib/python2.7/site-packages/requests/api.py", line 53, in request
    return session.request(method=method, url=url, **kwargs)
  File "/home/souto/.local/lib/python2.7/site-packages/requests/sessions.py", line 468, in request
    resp = self.send(prep, **send_kwargs)
  File "/home/souto/.local/lib/python2.7/site-packages/requests/sessions.py", line 576, in send
    r = adapter.send(request, **kwargs)
  File "/home/souto/.local/lib/python2.7/site-packages/requests/adapters.py", line 437, in send
    raise ConnectionError(e, request=request)
requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=50000): Max retries exceeded with url: /foobar (Caused by NewConnectionError('<requests.packages.urllib3.connection.HTTPConnection object at 0x1a991d0>: Failed to establish a new connection: [Errno 111] Connection refused',))
```


@suoto It's not the same bug. =)

Your bug is a simple timing issue: the requests.post call executes before the server has started running, which means it isn't yet listening on the socket. If you add a `sleep(1)` call before the call to `requests.post()` you'll find it works fine. =)


Yup, that's true. What I found it's weird is the fact I was seeing `requests.packages.urllib3.exceptions.NewConnectionError` exception instead of `requests.exceptions.ConnectionError` on the gist and on a project I'm working on. I'm so sure of this that I actually added the exception from `urllib3` to this project.

Anyway, sorry about that. I'll let you know if I have a code that reproduces this. :confounded: :confused: 


=) The most likely bet is just that the requests in question there was older.