Merge pull request #835 from ctiller/credit
C++ Credentials Rework
diff --git a/src/core/iomgr/pollset_posix.c b/src/core/iomgr/pollset_posix.c
index 0151550..0bb722e 100644
--- a/src/core/iomgr/pollset_posix.c
+++ b/src/core/iomgr/pollset_posix.c
@@ -66,7 +66,7 @@
gpr_timespec next_poll = gpr_time_add(last_poll, delta);
grpc_pollset_work(&g_backup_pollset, gpr_time_add(gpr_now(), gpr_time_from_seconds(1)));
gpr_mu_unlock(&g_backup_pollset.mu);
- /*gpr_sleep_until(next_poll);*/
+ gpr_sleep_until(next_poll);
gpr_mu_lock(&g_backup_pollset.mu);
last_poll = next_poll;
}
diff --git a/src/python/interop/interop/_insecure_interop_test.py b/src/python/interop/interop/_insecure_interop_test.py
new file mode 100644
index 0000000..1fa6b8b
--- /dev/null
+++ b/src/python/interop/interop/_insecure_interop_test.py
@@ -0,0 +1,56 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Insecure client-server interoperability as a unit test."""
+
+import unittest
+
+from grpc.early_adopter import implementations
+
+from interop import _interop_test_case
+from interop import methods
+
+
+class InsecureInteropTest(
+ _interop_test_case.InteropTestCase,
+ unittest.TestCase):
+
+ def setUp(self):
+ self.server = implementations.insecure_server(methods.SERVER_METHODS, 0)
+ self.server.start()
+ port = self.server.port()
+ self.stub = implementations.insecure_stub(
+ methods.CLIENT_METHODS, 'localhost', port)
+
+ def tearDown(self):
+ self.server.stop()
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/python/interop/interop/_interop_test_case.py b/src/python/interop/interop/_interop_test_case.py
new file mode 100644
index 0000000..fec8f19
--- /dev/null
+++ b/src/python/interop/interop/_interop_test_case.py
@@ -0,0 +1,55 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Common code for unit tests of the interoperability test code."""
+
+from interop import methods
+
+
+class InteropTestCase(object):
+ """Unit test methods.
+
+ This class must be mixed in with unittest.TestCase and a class that defines
+ setUp and tearDown methods that manage a stub attribute.
+ """
+
+ def testEmptyUnary(self):
+ methods.TestCase.EMPTY_UNARY.test_interoperability(self.stub)
+
+ def testLargeUnary(self):
+ methods.TestCase.LARGE_UNARY.test_interoperability(self.stub)
+
+ def testServerStreaming(self):
+ methods.TestCase.SERVER_STREAMING.test_interoperability(self.stub)
+
+ def testClientStreaming(self):
+ methods.TestCase.CLIENT_STREAMING.test_interoperability(self.stub)
+
+ def testPingPong(self):
+ methods.TestCase.PING_PONG.test_interoperability(self.stub)
diff --git a/src/python/interop/interop/_secure_interop_test.py b/src/python/interop/interop/_secure_interop_test.py
new file mode 100644
index 0000000..cc9e938
--- /dev/null
+++ b/src/python/interop/interop/_secure_interop_test.py
@@ -0,0 +1,63 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Secure client-server interoperability as a unit test."""
+
+import unittest
+
+from grpc.early_adopter import implementations
+
+from interop import _interop_test_case
+from interop import methods
+from interop import resources
+
+_SERVER_HOST_OVERRIDE = 'foo.test.google.fr'
+
+
+class SecureInteropTest(
+ _interop_test_case.InteropTestCase,
+ unittest.TestCase):
+
+ def setUp(self):
+ self.server = implementations.secure_server(
+ methods.SERVER_METHODS, 0, resources.private_key(),
+ resources.certificate_chain())
+ self.server.start()
+ port = self.server.port()
+ self.stub = implementations.secure_stub(
+ methods.CLIENT_METHODS, 'localhost', port,
+ resources.test_root_certificates(), None, None,
+ server_host_override=_SERVER_HOST_OVERRIDE)
+
+ def tearDown(self):
+ self.server.stop()
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/python/interop/interop/client.py b/src/python/interop/interop/client.py
index f4a449e..b674a64 100644
--- a/src/python/interop/interop/client.py
+++ b/src/python/interop/interop/client.py
@@ -65,21 +65,30 @@
root_certificates = resources.test_root_certificates()
else:
root_certificates = resources.prod_root_certificates()
- # TODO(nathaniel): server host override.
stub = implementations.secure_stub(
methods.CLIENT_METHODS, args.server_host, args.server_port,
- root_certificates, None, None)
+ root_certificates, None, None,
+ server_host_override=args.server_host_override)
else:
stub = implementations.insecure_stub(
methods.CLIENT_METHODS, args.server_host, args.server_port)
return stub
+def _test_case_from_arg(test_case_arg):
+ for test_case in methods.TestCase:
+ if test_case_arg == test_case.value:
+ return test_case
+ else:
+ raise ValueError('No test case "%s"!' % test_case_arg)
+
+
def _test_interoperability():
args = _args()
stub = _stub(args)
- methods.test_interoperability(args.test_case, stub)
+ test_case = _test_case_from_arg(args.test_case)
+ test_case.test_interoperability(stub)
if __name__ == '__main__':
diff --git a/src/python/interop/interop/methods.py b/src/python/interop/interop/methods.py
index 4da28ee..2e15fac 100644
--- a/src/python/interop/interop/methods.py
+++ b/src/python/interop/interop/methods.py
@@ -29,6 +29,7 @@
"""Implementations of interoperability test methods."""
+import enum
import threading
from grpc.early_adopter import utilities
@@ -265,16 +266,24 @@
pipe.close()
-def test_interoperability(test_case, stub):
- if test_case == 'empty_unary':
- _empty_unary(stub)
- elif test_case == 'large_unary':
- _large_unary(stub)
- elif test_case == 'server_streaming':
- _server_streaming(stub)
- elif test_case == 'client_streaming':
- _client_streaming(stub)
- elif test_case == 'ping_pong':
- _ping_pong(stub)
- else:
- raise NotImplementedError('Test case "%s" not implemented!')
+@enum.unique
+class TestCase(enum.Enum):
+ EMPTY_UNARY = 'empty_unary'
+ LARGE_UNARY = 'large_unary'
+ SERVER_STREAMING = 'server_streaming'
+ CLIENT_STREAMING = 'client_streaming'
+ PING_PONG = 'ping_pong'
+
+ def test_interoperability(self, stub):
+ if self is TestCase.EMPTY_UNARY:
+ _empty_unary(stub)
+ elif self is TestCase.LARGE_UNARY:
+ _large_unary(stub)
+ elif self is TestCase.SERVER_STREAMING:
+ _server_streaming(stub)
+ elif self is TestCase.CLIENT_STREAMING:
+ _client_streaming(stub)
+ elif self is TestCase.PING_PONG:
+ _ping_pong(stub)
+ else:
+ raise NotImplementedError('Test case "%s" not implemented!' % self.name)
diff --git a/src/python/src/grpc/_adapter/_c_test.py b/src/python/src/grpc/_adapter/_c_test.py
index 7492df1..437a673 100644
--- a/src/python/src/grpc/_adapter/_c_test.py
+++ b/src/python/src/grpc/_adapter/_c_test.py
@@ -70,7 +70,8 @@
def testChannel(self):
_c.init()
- channel = _c.Channel('test host:12345', None)
+ channel = _c.Channel(
+ 'test host:12345', None, server_host_override='ignored')
del channel
_c.shut_down()
diff --git a/src/python/src/grpc/_adapter/_channel.c b/src/python/src/grpc/_adapter/_channel.c
index 9cf580b..6be8f1c 100644
--- a/src/python/src/grpc/_adapter/_channel.c
+++ b/src/python/src/grpc/_adapter/_channel.c
@@ -42,19 +42,35 @@
static int pygrpc_channel_init(Channel *self, PyObject *args, PyObject *kwds) {
const char *hostport;
PyObject *client_credentials;
- static char *kwlist[] = {"hostport", "client_credentials", NULL};
+ char *server_host_override = NULL;
+ static char *kwlist[] = {"hostport", "client_credentials",
+ "server_host_override", NULL};
+ grpc_arg server_host_override_arg;
+ grpc_channel_args channel_args;
- if (!(PyArg_ParseTupleAndKeywords(args, kwds, "sO:Channel", kwlist,
- &hostport, &client_credentials))) {
+ if (!(PyArg_ParseTupleAndKeywords(args, kwds, "sO|z:Channel", kwlist,
+ &hostport, &client_credentials,
+ &server_host_override))) {
return -1;
}
if (client_credentials == Py_None) {
self->c_channel = grpc_channel_create(hostport, NULL);
return 0;
} else {
- self->c_channel = grpc_secure_channel_create(
- ((ClientCredentials *)client_credentials)->c_client_credentials,
- hostport, NULL);
+ if (server_host_override == NULL) {
+ self->c_channel = grpc_secure_channel_create(
+ ((ClientCredentials *)client_credentials)->c_client_credentials,
+ hostport, NULL);
+ } else {
+ server_host_override_arg.type = GRPC_ARG_STRING;
+ server_host_override_arg.key = GRPC_SSL_TARGET_NAME_OVERRIDE_ARG;
+ server_host_override_arg.value.string = server_host_override;
+ channel_args.num_args = 1;
+ channel_args.args = &server_host_override_arg;
+ self->c_channel = grpc_secure_channel_create(
+ ((ClientCredentials *)client_credentials)->c_client_credentials,
+ hostport, &channel_args);
+ }
return 0;
}
}
diff --git a/src/python/src/grpc/_adapter/rear.py b/src/python/src/grpc/_adapter/rear.py
index bfde5f5..fc71bf0 100644
--- a/src/python/src/grpc/_adapter/rear.py
+++ b/src/python/src/grpc/_adapter/rear.py
@@ -93,7 +93,8 @@
def __init__(
self, host, port, pool, request_serializers, response_deserializers,
- secure, root_certificates, private_key, certificate_chain):
+ secure, root_certificates, private_key, certificate_chain,
+ server_host_override=None):
"""Constructor.
Args:
@@ -111,6 +112,8 @@
key should be used.
certificate_chain: The PEM-encoded certificate chain to use or None if
no certificate chain should be used.
+ server_host_override: (For testing only) the target name used for SSL
+ host name checking.
"""
self._condition = threading.Condition()
self._host = host
@@ -132,6 +135,7 @@
self._root_certificates = root_certificates
self._private_key = private_key
self._certificate_chain = certificate_chain
+ self._server_host_override = server_host_override
def _on_write_event(self, operation_id, event, rpc_state):
if event.write_accepted:
@@ -327,7 +331,8 @@
with self._condition:
self._completion_queue = _low.CompletionQueue()
self._channel = _low.Channel(
- '%s:%d' % (self._host, self._port), self._client_credentials)
+ '%s:%d' % (self._host, self._port), self._client_credentials,
+ server_host_override=self._server_host_override)
return self
def _stop(self):
@@ -388,7 +393,8 @@
def __init__(
self, host, port, request_serializers, response_deserializers, secure,
- root_certificates, private_key, certificate_chain):
+ root_certificates, private_key, certificate_chain,
+ server_host_override=None):
self._host = host
self._port = port
self._request_serializers = request_serializers
@@ -397,6 +403,7 @@
self._root_certificates = root_certificates
self._private_key = private_key
self._certificate_chain = certificate_chain
+ self._server_host_override = server_host_override
self._lock = threading.Lock()
self._pool = None
@@ -415,7 +422,8 @@
self._rear_link = RearLink(
self._host, self._port, self._pool, self._request_serializers,
self._response_deserializers, self._secure, self._root_certificates,
- self._private_key, self._certificate_chain)
+ self._private_key, self._certificate_chain,
+ server_host_override=self._server_host_override)
self._rear_link.join_fore_link(self._fore_link)
self._rear_link.start()
return self
@@ -477,7 +485,7 @@
def secure_activated_rear_link(
host, port, request_serializers, response_deserializers, root_certificates,
- private_key, certificate_chain):
+ private_key, certificate_chain, server_host_override=None):
"""Creates a RearLink that is also an activated.Activated.
The returned object is only valid for use between calls to its start and stop
@@ -496,7 +504,10 @@
should be used.
certificate_chain: The PEM-encoded certificate chain to use or None if no
certificate chain should be used.
+ server_host_override: (For testing only) the target name used for SSL
+ host name checking.
"""
return _ActivatedRearLink(
host, port, request_serializers, response_deserializers, True,
- root_certificates, private_key, certificate_chain)
+ root_certificates, private_key, certificate_chain,
+ server_host_override=server_host_override)
diff --git a/src/python/src/grpc/early_adopter/implementations.py b/src/python/src/grpc/early_adopter/implementations.py
index 6195958..87ea18d 100644
--- a/src/python/src/grpc/early_adopter/implementations.py
+++ b/src/python/src/grpc/early_adopter/implementations.py
@@ -125,7 +125,8 @@
def secure_stub(
- methods, host, port, root_certificates, private_key, certificate_chain):
+ methods, host, port, root_certificates, private_key, certificate_chain,
+ server_host_override=None):
"""Constructs an insecure interfaces.Stub.
Args:
@@ -140,6 +141,8 @@
should be used.
certificate_chain: The PEM-encoded certificate chain to use or None if no
certificate chain should be used.
+ server_host_override: (For testing only) the target name used for SSL
+ host name checking.
Returns:
An interfaces.Stub affording RPC invocation.
@@ -148,7 +151,7 @@
activated_rear_link = _rear.secure_activated_rear_link(
host, port, breakdown.request_serializers,
breakdown.response_deserializers, root_certificates, private_key,
- certificate_chain)
+ certificate_chain, server_host_override=server_host_override)
return _build_stub(breakdown, activated_rear_link)
diff --git a/tools/run_tests/build_python.sh b/tools/run_tests/build_python.sh
index de63308..0eba1c6 100755
--- a/tools/run_tests/build_python.sh
+++ b/tools/run_tests/build_python.sh
@@ -39,3 +39,4 @@
source python2.7_virtual_environment/bin/activate
pip install enum34==1.0.4 futures==2.2.0 protobuf==3.0.0-alpha-1
CFLAGS=-I$root/include LDFLAGS=-L$root/libs/opt pip install src/python/src
+pip install src/python/interop
diff --git a/tools/run_tests/python_tests.json b/tools/run_tests/python_tests.json
index 4b43ee8..69022af 100755
--- a/tools/run_tests/python_tests.json
+++ b/tools/run_tests/python_tests.json
@@ -46,5 +46,11 @@
},
{
"module": "grpc.framework.foundation._logging_pool_test"
+ },
+ {
+ "module": "interop._insecure_interop_test"
+ },
+ {
+ "module": "interop._secure_interop_test"
}
]