diff options
-rw-r--r-- | src/core/iomgr/pollset_posix.c | 2 | ||||
-rw-r--r-- | src/python/interop/interop/_insecure_interop_test.py | 56 | ||||
-rw-r--r-- | src/python/interop/interop/_interop_test_case.py | 55 | ||||
-rw-r--r-- | src/python/interop/interop/_secure_interop_test.py | 63 | ||||
-rw-r--r-- | src/python/interop/interop/client.py | 15 | ||||
-rw-r--r-- | src/python/interop/interop/methods.py | 35 | ||||
-rw-r--r-- | src/python/src/grpc/_adapter/_c_test.py | 3 | ||||
-rw-r--r-- | src/python/src/grpc/_adapter/_channel.c | 28 | ||||
-rw-r--r-- | src/python/src/grpc/_adapter/rear.py | 23 | ||||
-rw-r--r-- | src/python/src/grpc/early_adopter/implementations.py | 7 | ||||
-rwxr-xr-x | tools/run_tests/build_python.sh | 1 | ||||
-rwxr-xr-x | tools/run_tests/python_tests.json | 6 |
12 files changed, 262 insertions, 32 deletions
diff --git a/src/core/iomgr/pollset_posix.c b/src/core/iomgr/pollset_posix.c index 0151550a81..0bb722e2b1 100644 --- a/src/core/iomgr/pollset_posix.c +++ b/src/core/iomgr/pollset_posix.c @@ -66,7 +66,7 @@ static void backup_poller(void *p) { 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 0000000000..1fa6b8b3f8 --- /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 0000000000..fec8f1915d --- /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 0000000000..cc9e93821a --- /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 f4a449ef9e..b674a64f9d 100644 --- a/src/python/interop/interop/client.py +++ b/src/python/interop/interop/client.py @@ -65,21 +65,30 @@ def _stub(args): 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 4da28ee775..2e15fac915 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 @@ def _ping_pong(stub): 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 7492df1291..437a6730cd 100644 --- a/src/python/src/grpc/_adapter/_c_test.py +++ b/src/python/src/grpc/_adapter/_c_test.py @@ -70,7 +70,8 @@ class _CTest(unittest.TestCase): 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 9cf580bcfb..6be8f1c364 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 bfde5f5c57..fc71bf0a6c 100644 --- a/src/python/src/grpc/_adapter/rear.py +++ b/src/python/src/grpc/_adapter/rear.py @@ -93,7 +93,8 @@ class RearLink(ticket_interfaces.RearLink, activated.Activated): 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 @@ class RearLink(ticket_interfaces.RearLink, activated.Activated): 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 @@ class RearLink(ticket_interfaces.RearLink, activated.Activated): 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 @@ class RearLink(ticket_interfaces.RearLink, activated.Activated): 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 @@ class _ActivatedRearLink(ticket_interfaces.RearLink, activated.Activated): 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 @@ class _ActivatedRearLink(ticket_interfaces.RearLink, activated.Activated): 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 @@ class _ActivatedRearLink(ticket_interfaces.RearLink, activated.Activated): 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 activated_rear_link( 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 @@ def secure_activated_rear_link( 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 6195958624..87ea18d666 100644 --- a/src/python/src/grpc/early_adopter/implementations.py +++ b/src/python/src/grpc/early_adopter/implementations.py @@ -125,7 +125,8 @@ def insecure_stub(methods, host, port): 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 @@ def secure_stub( 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 @@ def secure_stub( 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 de633083c3..0eba1c6377 100755 --- a/tools/run_tests/build_python.sh +++ b/tools/run_tests/build_python.sh @@ -39,3 +39,4 @@ virtualenv -p /usr/bin/python2.7 python2.7_virtual_environment 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 4b43ee8357..69022af12e 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" } ] |