Repository: boto/botocore (python)

Issue:
Dynamodb local uses HTTPS instead of HTTP
In our endpoints.json we have:

```
            "local": {
              "hostname": "localhost:8000",
              "protocols": [
                "http"
              ],
              "credentialScope": {
                "region": "us-east-1"
              }
            }

```

However, creating a client with this region will use HTTPS:

```
>>> import botocore.session
>>> botocore.session.get_session().create_client('dynamodb', 'local').list_tables()
...
botocore.vendored.requests.exceptions.SSLError: [SSL: UNKNOWN_PROTOCOL] unknown protocol (_ssl.c:581)
```

My best guess is that we're not honoring the `protocol` key from endpoints.json when creating clients.

Pull request:
Use 'protocols' key to influence scheme
This fixes a bug where if you try to use
DynamoDB Local via `--region local`,
we'll try to connect over https:// despite
the fact that the `protocols` key for that
region indicates that only http is supported.

Also verified I could connect to dynamodb local
after these changes.

Fixes #819.

cc @kyleknap @JordonPhillips 

Patch:
diff --git a/botocore/client.py b/botocore/client.py
index c31f9c725..a201503c4 100644
--- a/botocore/client.py
+++ b/botocore/client.py
@@ -291,7 +291,8 @@ class ClientEndpointBridge(object):
         if endpoint_url is None:
             # Use the sslCommonName over the hostname for Python 2.6 compat.
             hostname = resolved.get('sslCommonName', resolved.get('hostname'))
-            endpoint_url = self._make_url(hostname, is_secure)
+            endpoint_url = self._make_url(hostname, is_secure,
+                                          resolved.get('protocols', []))
         signature_version = self._resolve_signature_version(
             service_name, resolved)
         signing_name = self._resolve_signing_name(service_name, resolved)
@@ -307,7 +308,8 @@ class ClientEndpointBridge(object):
             # Expand the default hostname URI template.
             hostname = self.default_endpoint.format(
                 service=service_name, region=region_name)
-            endpoint_url = self._make_url(hostname, is_secure)
+            endpoint_url = self._make_url(hostname, is_secure,
+                                          ['http', 'https'])
         logger.debug('Assuming an endpoint for %s, %s: %s',
                      service_name, region_name, endpoint_url)
         # We still want to allow the user to provide an explicit version.
@@ -332,9 +334,12 @@ class ClientEndpointBridge(object):
             'metadata': metadata
         }
 
-    def _make_url(self, hostname, is_secure):
-        endpoint_url = 'https://' if is_secure else 'http://'
-        return endpoint_url + hostname
+    def _make_url(self, hostname, is_secure, supported_protocols):
+        if is_secure and 'https' in supported_protocols:
+            scheme ='https'
+        else:
+            scheme = 'http'
+        return '%s://%s' % (scheme, hostname)
 
     def _resolve_signing_name(self, service_name, resolved):
         # CredentialScope overrides everything else.


Test patch:
diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py
index d17f1f5a0..c99b7e379 100644
--- a/tests/unit/test_client.py
+++ b/tests/unit/test_client.py
@@ -1240,7 +1240,8 @@ class TestClientEndpointBridge(unittest.TestCase):
         resolver = mock.Mock()
         resolver.construct_endpoint.return_value = {
             'partition': 'aws', 'hostname': 's3.amazonaws.com',
-            'endpointName': 'us-east-1', 'signatureVersions': ['s3', 's3v4']}
+            'endpointName': 'us-east-1', 'signatureVersions': ['s3', 's3v4'],
+            'protocols': ['https']}
         bridge = ClientEndpointBridge(resolver)
         resolved = bridge.resolve('s3')
         self.assertEqual('us-east-1', resolved['region_name'])
@@ -1288,7 +1289,7 @@ class TestClientEndpointBridge(unittest.TestCase):
         resolver.construct_endpoint.return_value = {
             'partition': 'aws', 'hostname': 'do-not-use-this',
             'signatureVersions': ['v4'], 'sslCommonName': 'common-name.com',
-            'endpointName': 'us-west-2'}
+            'endpointName': 'us-west-2', 'protocols': ['https']}
         bridge = ClientEndpointBridge(resolver)
         resolved = bridge.resolve('myservice', 'us-west-2')
         self.assertEqual('us-west-2', resolved['region_name'])
@@ -1348,14 +1349,33 @@ class TestClientEndpointBridge(unittest.TestCase):
     def test_resolved_region_overrides_region_when_no_endpoint_url(self):
         resolver = mock.Mock()
         resolver.construct_endpoint.return_value = {
-            'partition': 'aws', 'hostname': 'host.com',
-            'signatureVersions': ['v4'], 'endpointName': 'override'}
+            'partition': 'aws',
+            'hostname': 'host.com',
+            'signatureVersions': ['v4'],
+            'endpointName': 'override',
+            'protocols': ['https'],
+        }
         bridge = ClientEndpointBridge(resolver)
         resolved = bridge.resolve('myservice', 'will-not-be-there')
         self.assertEqual('override', resolved['region_name'])
         self.assertEqual('override', resolved['signing_region'])
         self.assertEqual('https://host.com', resolved['endpoint_url'])
 
+    def test_does_not_use_https_if_not_available(self):
+        resolver = mock.Mock()
+        resolver.construct_endpoint.return_value = {
+            'partition': 'aws',
+            'hostname': 'host.com',
+            'signatureVersions': ['v4'],
+            'endpointName': 'foo',
+            # Note: http, not https
+            'protocols': ['http'],
+        }
+        bridge = ClientEndpointBridge(resolver)
+        resolved = bridge.resolve('myservice')
+        # We should resolve to http://, not https://
+        self.assertEqual('http://host.com', resolved['endpoint_url'])
+
     def test_uses_signature_version_from_client_config(self):
         resolver = mock.Mock()
         resolver.construct_endpoint.return_value = {