SSL error when trying to open a webpage

``` pycon
>>> requests.get("https://www.howsmyssl.com/a/check")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/alex_gaynor/.virtualenvs/tempenv-6827228677281/lib/python2.7/site-packages/requests/api.py", line 55, in get
    return request('get', url, **kwargs)
  File "/Users/alex_gaynor/.virtualenvs/tempenv-6827228677281/lib/python2.7/site-packages/requests/api.py", line 44, in request
    return session.request(method=method, url=url, **kwargs)
  File "/Users/alex_gaynor/.virtualenvs/tempenv-6827228677281/lib/python2.7/site-packages/requests/sessions.py", line 382, in request
    resp = self.send(prep, **send_kwargs)
  File "/Users/alex_gaynor/.virtualenvs/tempenv-6827228677281/lib/python2.7/site-packages/requests/sessions.py", line 485, in send
    r = adapter.send(request, **kwargs)
  File "/Users/alex_gaynor/.virtualenvs/tempenv-6827228677281/lib/python2.7/site-packages/requests/adapters.py", line 379, in send
    raise SSLError(e)
requests.exceptions.SSLError: [Errno 1] _ssl.c:504: error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version
```

This page loads fine in my browser or when using `treq` with Twisted.


Yeah, discussed this on Twitter already. It's worth noting that I'm fairly sure that anyone who uses SSL in Python from the stdlib will encounter this problem. I'll confirm by testing with httplib.


Yes, you can reproduce this with `urllib` as well -- I believe this does not affect Python 3.


@kennethreitz has previously stated that he views requests as a product and anything else as an implementation detail, so I assumed he'd want to know about this to address this in requests, if at all possible.


Sorry, you're right, Python 3 is immune. And I didn't mean "we don't care about this bug", just that I was aware of it. =P Opening this is the right call, Kenneth will want to know about it.


You know, I should build an API for people to report failing URLs to us. 


So, @Lukasa what's causing this? 


Based on the discussion I had on Twitter, it's probably because in Python 2.7 we attempt to establish a SSLv2 connection first, and negotiate up. The endpoint server is refusing the SSLv2 connection entirely.

It's possible that fixing this would mean setting a higher minimum SSL/TLS version. I'd be interested to know if we can force everyone to use SSLv3 without breaking anything for anyone.


I'll also grab some tcpdump to confirm that that's the difference between SSLv2 and SSLv3.


As far as I know, there is no good reason to be attempting to use SSLv2 on
the internet know, it's completely broken.

On Tue, Jan 7, 2014 at 12:05 PM, Cory Benfield notifications@github.comwrote:

> I'll also grab some tcpdump to confirm that that's the difference between
> SSLv2 and SSLv3.
> 
> —
> Reply to this email directly or view it on GitHubhttps://github.com/kennethreitz/requests/issues/1847#issuecomment-31773798
> .

## 

"I disapprove of what you say, but I will defend to the death your right to
say it." -- Evelyn Beatrice Hall (summarizing Voltaire)
"The people's good is the highest law." -- Cicero
GPG Key fingerprint: 125F 5C67 DFE9 4084


Ah, this always bites me in the ass. I thought we implemented something for auto-negotitaion ~1 year ago?


@alex In principle I agree, but currently Requests just uses whatever the Python version has as the default in the `ssl` module.

@kennethreitz We _are_ autonegotiating (I'm pretty sure), but the remote server refuses the initial version we try.


Fucking C extensions ruin everything :)


Yup, in Python 2 we initially send a SSLv2 request, in Python 3 we send TLSv1.0.


I'll investigate whether forcing to SSLv3 improves any of this


It depends on the version of openssl in use. For me the reported url works just fine with Python 2.7.6 and OpenSSL 1.0.1f.


Goddamn it I hate SSL so much.

Ok, so forcing SSLv3 improves _this particular website_, so that fix will work. That's probably the safest fix we can make in this situation.


I'm on OS X -- which uses OpenSSL 0.9.8<some letter>

On Tue, Jan 7, 2014 at 12:30 PM, Thomas Weißschuh
notifications@github.comwrote:

> It depends on the version of openssl in use. For me the reported url works
> just fine with Python 2.7.6 and OpenSSL 1.0.1f.
> 
> —
> Reply to this email directly or view it on GitHubhttps://github.com/kennethreitz/requests/issues/1847#issuecomment-31776093
> .

## 

"I disapprove of what you say, but I will defend to the death your right to
say it." -- Evelyn Beatrice Hall (summarizing Voltaire)
"The people's good is the highest law." -- Cicero
GPG Key fingerprint: 125F 5C67 DFE9 4084


Same as me, which is why I can reproduce it. Gotta love Apple's totally bass-ackwards attitude to upgrading critical security software.


Nope, that's not going to fly. Are we really going to have to implement fallback in requests/urllib3?


This is going to suck with the servers that do not any response triggering the 30 second timeout.
If we do the automatic fallback and no exception/feedback people will have see really slow requests if they hit this.


Can we mimic the Python 3 functionality? Shouldn't be that hard. Web browsers do it.


MONKEYPATCH THE SYSTEM


We're really in between a rock and a hard place. Is our current solution of pointing people to [here](https://lukasa.co.uk/2013/01/Choosing_SSL_Version_In_Requests/) really unacceptable?

@kennethreitz I think it's harder than that, I just realised that my Python 3 is using a more recent OpenSSL:

``` python
Python 2.7.5 (default, Aug 29 2013, 18:40:14) 
[GCC 4.2.1 Compatible Apple LLVM 4.2 (clang-425.0.28)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import ssl
>>> ssl.OPENSSL_VERSION
'OpenSSL 0.9.8y 5 Feb 2013'
```

``` python
Python 3.3.3 (default, Nov 20 2013, 19:35:16) 
[GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.2.79)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import ssl
>>> ssl.OPENSSL_VERSION
'OpenSSL 1.0.1e 11 Feb 2013'
```


Yeah, that's almost always what the problem is. Honestly, I'm extremely proud of my SSL implementation and I think it's one of the most seamless SSL experiences a developer can ever have. Many users have absolutely no idea that they're even verifying SSL when they're using Requests until a verification fails, and we're making the world a much better and secure place because of it. 

A URL that fails every now and again because of an odd openssl + python combination is unfortunate, but there's always room for improvement :)


I'm prepared to accept that, in general. We might want to document the `SSLAdapter` fallback. It's being implemented in `requests-toolbelt`, so it'll be easy to get hold of it if needed. Does that sound like an acceptable route?


I think this particular server simply misbehaves and refuses connections from clients which announce _support_ for `SSLv2`.

This does not work:

```
$ openssl s_client -connect www.howsmyssl.com:443  -cipher 'ALL'
```

While this works:

```
$ openssl s_client -connect www.howsmyssl.com:443  -cipher 'ALL:!SSLv2'
```

If try to connect with `ALL` to my own server I end up using TLSv1.2 which looks rather fine.


Sounds like an intentionally bitchy server. The domain name is telling :)


It's definitely intentionally bitchy. Observe the twitter conversation I had earlier [here](https://twitter.com/jmhodges/status/420614636606468096).


Cross referencing the generic monkey-patching solution here: https://github.com/kennethreitz/requests/issues/606#issuecomment-45704671


Hi @Lukasa ,

I am trying to use python requests to send a requests to a gunicorn server that accepts tls_v1 protocol which means that the client will use tls_v1 only. I create an adapter like:
class TLSAdapter(HTTPAdapter):
  def init_poolmanager(self, connections, maxsize, block=False):
    self.poolmanager = PoolManager(num_pools=connections,
                                   maxsize=maxsize,
                                   block=block,
                                   ssl_version=ssl.PROTOCOL_TLSv1)

And use it like:
requests_session = requests.Session()
requests_session.mount('https://', TLSAdapter())

Now when I try to send a request to my server like:
resp = requests_session.put(request.full_uri, data=request.data, headers=request_headers, verify=verify, cert=(cert, key))

Then I get an error like:
requests.exceptions.ConnectionError: ('Connection aborted.', BadStatusLine('',))

Could you please let me know if this issue has been seen before.
I am running server on my mac and sending the request from the same mac.

I am using python 2.6.9