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"
   }
 ]