Repository: urllib3/urllib3

Issue:
Retry retries on fruitless ssl ImportError
### Subject

Describe the issue here.

### Environment

Describe your environment.
At least, paste here the output of:

```python
import platform
import urllib3

print("OS", platform.platform())
print("Python", platform.python_version())
print("urllib3", urllib3.__version__)
```

### Steps to Reproduce

```
Python 3.10.4 (main, Mar 24 2022, 16:12:56) [GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.modules["ssl"] = None
>>> import requests
>>> requests.get("https://google.com")
Traceback (most recent call last):
  File "/home/graingert/.virtualenvs/testing310/lib/python3.10/site-packages/urllib3/connectionpool.py", line 692, in urlopen
    conn = self._get_conn(timeout=pool_timeout)
  File "/home/graingert/.virtualenvs/testing310/lib/python3.10/site-packages/urllib3/connectionpool.py", line 281, in _get_conn
    return conn or self._new_conn()
  File "/home/graingert/.virtualenvs/testing310/lib/python3.10/site-packages/urllib3/connectionpool.py", line 1009, in _new_conn
    raise SSLError(
urllib3.exceptions.SSLError: Can't connect to HTTPS URL because the SSL module is not available.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/graingert/.virtualenvs/testing310/lib/python3.10/site-packages/requests/adapters.py", line 440, in send
    resp = conn.urlopen(
  File "/home/graingert/.virtualenvs/testing310/lib/python3.10/site-packages/urllib3/connectionpool.py", line 785, in urlopen
    retries = retries.increment(
  File "/home/graingert/.virtualenvs/testing310/lib/python3.10/site-packages/urllib3/util/retry.py", line 592, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='google.com', port=443): Max retries exceeded with url: / (Caused by SSLError("Can't connect to HTTPS URL because the SSL module is not available."))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/graingert/.virtualenvs/testing310/lib/python3.10/site-packages/requests/api.py", line 75, in get
    return request('get', url, params=params, **kwargs)
  File "/home/graingert/.virtualenvs/testing310/lib/python3.10/site-packages/requests/api.py", line 61, in request
    return session.request(method=method, url=url, **kwargs)
  File "/home/graingert/.virtualenvs/testing310/lib/python3.10/site-packages/requests/sessions.py", line 529, in request
    resp = self.send(prep, **send_kwargs)
  File "/home/graingert/.virtualenvs/testing310/lib/python3.10/site-packages/requests/sessions.py", line 645, in send
    r = adapter.send(request, **kwargs)
  File "/home/graingert/.virtualenvs/testing310/lib/python3.10/site-packages/requests/adapters.py", line 517, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='google.com', port=443): Max retries exceeded with url: / (Caused by SSLError("Can't connect to HTTPS URL because the SSL module is not available."))
```

### Expected Behavior

only one attempt

### Actual Behavior

^

Retry retries on fruitless ssl ImportError
### Subject

Describe the issue here.

### Environment

Describe your environment.
At least, paste here the output of:

```python
import platform
import urllib3

print("OS", platform.platform())
print("Python", platform.python_version())
print("urllib3", urllib3.__version__)
```

### Steps to Reproduce

```
Python 3.10.4 (main, Mar 24 2022, 16:12:56) [GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.modules["ssl"] = None
>>> import requests
>>> requests.get("https://google.com")
Traceback (most recent call last):
  File "/home/graingert/.virtualenvs/testing310/lib/python3.10/site-packages/urllib3/connectionpool.py", line 692, in urlopen
    conn = self._get_conn(timeout=pool_timeout)
  File "/home/graingert/.virtualenvs/testing310/lib/python3.10/site-packages/urllib3/connectionpool.py", line 281, in _get_conn
    return conn or self._new_conn()
  File "/home/graingert/.virtualenvs/testing310/lib/python3.10/site-packages/urllib3/connectionpool.py", line 1009, in _new_conn
    raise SSLError(
urllib3.exceptions.SSLError: Can't connect to HTTPS URL because the SSL module is not available.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/graingert/.virtualenvs/testing310/lib/python3.10/site-packages/requests/adapters.py", line 440, in send
    resp = conn.urlopen(
  File "/home/graingert/.virtualenvs/testing310/lib/python3.10/site-packages/urllib3/connectionpool.py", line 785, in urlopen
    retries = retries.increment(
  File "/home/graingert/.virtualenvs/testing310/lib/python3.10/site-packages/urllib3/util/retry.py", line 592, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='google.com', port=443): Max retries exceeded with url: / (Caused by SSLError("Can't connect to HTTPS URL because the SSL module is not available."))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/graingert/.virtualenvs/testing310/lib/python3.10/site-packages/requests/api.py", line 75, in get
    return request('get', url, params=params, **kwargs)
  File "/home/graingert/.virtualenvs/testing310/lib/python3.10/site-packages/requests/api.py", line 61, in request
    return session.request(method=method, url=url, **kwargs)
  File "/home/graingert/.virtualenvs/testing310/lib/python3.10/site-packages/requests/sessions.py", line 529, in request
    resp = self.send(prep, **send_kwargs)
  File "/home/graingert/.virtualenvs/testing310/lib/python3.10/site-packages/requests/sessions.py", line 645, in send
    r = adapter.send(request, **kwargs)
  File "/home/graingert/.virtualenvs/testing310/lib/python3.10/site-packages/requests/adapters.py", line 517, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='google.com', port=443): Max retries exceeded with url: / (Caused by SSLError("Can't connect to HTTPS URL because the SSL module is not available."))
```

### Expected Behavior

only one attempt

### Actual Behavior

^



Discussion:
Yep, agreed we should be avoiding this if possible. I think the retry behavior comes in to play because we're [raising an SSLError](https://github.com/urllib3/urllib3/blob/main/src/urllib3/connectionpool.py#L1022-L1025) which is generally retryable.

We might consider changing this to a more specific error that inherits from SSLError for backwards compatibility. That would allow us to control [the flow](https://github.com/urllib3/urllib3/blob/main/src/urllib3/connectionpool.py#L764-L800), and bypass retries like we do for the [`EmptyPoolError` case](https://github.com/urllib3/urllib3/blob/main/src/urllib3/connectionpool.py#L758-L762). 
In addition `urllib3.exceptions.SSLError: Can't connect to HTTPS URL because the SSL module is not available.` hides the original error and resulted in a `pip` user not recieving sufficient feedback to resolve their python installation issue
Yep, agreed we should be avoiding this if possible. I think the retry behavior comes in to play because we're [raising an SSLError](https://github.com/urllib3/urllib3/blob/main/src/urllib3/connectionpool.py#L1022-L1025) which is generally retryable.

We might consider changing this to a more specific error that inherits from SSLError for backwards compatibility. That would allow us to control [the flow](https://github.com/urllib3/urllib3/blob/main/src/urllib3/connectionpool.py#L764-L800), and bypass retries like we do for the [`EmptyPoolError` case](https://github.com/urllib3/urllib3/blob/main/src/urllib3/connectionpool.py#L758-L762). 
In addition `urllib3.exceptions.SSLError: Can't connect to HTTPS URL because the SSL module is not available.` hides the original error and resulted in a `pip` user not recieving sufficient feedback to resolve their python installation issue

Resolving patch:
diff --git a/src/urllib3/connectionpool.py b/src/urllib3/connectionpool.py
--- a/src/urllib3/connectionpool.py
+++ b/src/urllib3/connectionpool.py
@@ -1020,7 +1020,7 @@ def _new_conn(self) -> HTTPConnection:
         )
 
         if not self.ConnectionCls or self.ConnectionCls is DummyConnection:  # type: ignore[comparison-overlap]
-            raise SSLError(
+            raise ImportError(
                 "Can't connect to HTTPS URL because the SSL module is not available."
             )
 


Test patch:
diff --git a/test/with_dummyserver/test_https.py b/test/with_dummyserver/test_https.py
--- a/test/with_dummyserver/test_https.py
+++ b/test/with_dummyserver/test_https.py
@@ -376,11 +376,10 @@ def test_verified_without_ca_certs(self) -> None:
     def test_no_ssl(self) -> None:
         with HTTPSConnectionPool(self.host, self.port) as pool:
             pool.ConnectionCls = None  # type: ignore[assignment]
-            with pytest.raises(SSLError):
+            with pytest.raises(ImportError):
                 pool._new_conn()
-            with pytest.raises(MaxRetryError) as cm:
+            with pytest.raises(ImportError):
                 pool.request("GET", "/", retries=0)
-            assert isinstance(cm.value.reason, SSLError)
 
     def test_unverified_ssl(self) -> None:
         """Test that bare HTTPSConnection can connect, make requests"""