Repository: pydap/pydap

Issue:
InvalidSchema: No connection adapters were found for `dap2://...`
One approach for users to specify the dap4 protocol when requesting responses from OPenDAP servers, is to 

```
https://<data_url> ----> dap4://<data_url>
```
Internally pydap identifies this and correctly requests the dmr and then dap response. Another way to specify the `protocol` as a string. In any scenario, when nothing is specified, dap2 is assumed, and a `UserWarning` is returned, along with the expected dap2 dataset.

However, when the scheme of the url is replaced with `dap2` leads to an `InvalidSchema`, even though `pydap` correctly identifies `dap2` as the protocol

The issue is that the scheme of the URL is never replaced by `http` when it is originally `dap2` (it is in the case its originally `dap4`).

## minimal example
```python
url1 = "http://test.opendap.org/opendap/data/nc/coads_climatology.nc"
url2 = "dap2://test.opendap.org/opendap/data/nc/coads_climatology.nc"
from pydap.client import open_url
```
### Default works but there is a warning:
```python
ds = open_url(url1)
>>> UserWarning: PyDAP was unable to determine the DAP protocol defaulting to DAP2 which is consider legacy and may result in slower responses. For more, see go to https://www.opendap.org/faq-page.
```
### This should work, but it does not
```python
ds = open_url(url2) # dap2://test.opendap.org/...
>>>
.
.
.
InvalidSchema: No connection adapters were found for 'dap2://test.opendap.org/opendap/data/nc/coads_climatology.nc.dds'
```

It is pretty clear that pydap identified DAP2 as the protocol since it requested the `.dds`, but never replaced the schema to http.





Discussion:

Patch:
diff --git a/src/pydap/handlers/dap.py b/src/pydap/handlers/dap.py
index e38f3b3..29986d8 100644
--- a/src/pydap/handlers/dap.py
+++ b/src/pydap/handlers/dap.py
@@ -88,7 +88,12 @@ class DAPHandler(BaseHandler):
         self.query = query
         self.fragment = fragment
 
-        self.protocol = self.determine_protocol(protocol)
+        if protocol:
+            if protocol not in ["dap2", "dap4"]:
+                raise TypeError("protocol must be one of `dap2` or `dap4")
+            self.protocol = protocol
+        else:
+            self.protocol = self.determine_protocol()
         self.get_kwargs = get_kwargs or {}
 
         self.projection, self.selection = parse_ce(self.query, self.protocol)
@@ -104,32 +109,31 @@ class DAPHandler(BaseHandler):
         self.make_dataset()
         self.add_proxies()
 
-    def determine_protocol(self, protocol):
-        if protocol == "dap4":
-            self.scheme = "http"
-            return protocol
-        elif protocol == "dap2":
-            return protocol
-        elif self.scheme == "dap4":
-            self.scheme = "http"
-            return "dap4"
-        elif self.query[:4] == "dap4":
-            self.sheme = "http"
-            return "dap4"
-        else:
-            extension = self.path.split(".")[-1]
-            if extension in ["dmr", "dap"]:
-                return "dap4"
-            elif extension in ["dds", "dods"]:
-                return "dap2"
+    def determine_protocol(self):
+        # determines the opendap protocol from url scheme, query, or extension
+        if self.application:
+            # server - only dap2 for now.
+            return "dap2"
+        if self.scheme not in ["http", "https"]:
+            if self.scheme in ["dap4", "dap2"]:
+                protocol = self.scheme
+                self.scheme = "http"  # revert to http
+                return protocol
             else:
-                _warnings.warn(
-                    "PyDAP was unable to determine the DAP protocol "
-                    "defaulting to DAP2 which is consider legacy and "
-                    "may result in slower responses. For more, see "
-                    "go to https://www.opendap.org/faq-page."
+                raise TypeError(
+                    "Invalid URL scheme - acceptable options are"
+                    "`dap2`, `dap4`. `https` and `http`",
                 )
-                return "dap2"
+        if self.query[:4] == "dap4":
+            return "dap4"
+        else:
+            _warnings.warn(
+                "PyDAP was unable to determine the DAP protocol defaulting "
+                "to DAP2. DAP2 is consider legacy and may result in slower "
+                "responses. \n For more information about, see go to  "
+                "https://www.opendap.org/faq-page."
+            )
+            return "dap2"
 
     def make_dataset(
         self,


Test patch:
diff --git a/src/pydap/tests/test_handlers_dap.py b/src/pydap/tests/test_handlers_dap.py
index 528586f..3523c0b 100644
--- a/src/pydap/tests/test_handlers_dap.py
+++ b/src/pydap/tests/test_handlers_dap.py
@@ -4,6 +4,7 @@ import os
 import unittest
 
 import numpy as np
+import pytest
 from webob.response import Response
 
 import pydap.model
@@ -326,6 +327,41 @@ class TestBaseProxy(unittest.TestCase):
         np.testing.assert_array_equal(self.data < 2, np.arange(5) < 2)
 
 
+test_url = "http://test.opendap.org/opendap/data/nc/coads_climatology.nc"
+
+
+@pytest.mark.parametrize(
+    "url, app, expect",
+    [
+        ("dap2://test.opendap.org/opendap/data/nc/coads_climatology.nc", None, "dap2"),
+        ("dap4://test.opendap.org/opendap/data/nc/coads_climatology.nc", None, "dap4"),
+        (test_url, None, "warn"),
+        (
+            test_url + "?dap4.ce=/TIME",
+            None,
+            "dap4",
+        ),
+        ("a://test.opendap.org/opendap/data/nc/coads_climatology.nc", None, "error"),
+        (" ", BaseHandler(SimpleGrid), "dap2"),
+    ],
+)
+def test_protocols(url, app, expect):
+    if app:
+        assert DAPHandler(url=url, application=app).protocol == expect
+    else:
+        if expect == "error":
+            with pytest.raises(TypeError):
+                DAPHandler(url=url)
+        elif expect == "warn":
+            with pytest.warns(UserWarning):
+                DAPHandler(url=url)
+        else:
+            assert DAPHandler(url=url).protocol == expect
+    with pytest.raises(TypeError):
+        # only protocol='dap2' or `dap4` is supported
+        DAPHandler(url=url, application=app, protocol="errdap")
+
+
 class TestBaseProxyShort(unittest.TestCase):
     """Test `BaseProxy` objects with short dtype."""