THRIFT-2347 C# TLS Transport based on THRIFT-181
authorJens Geyer <jensg@apache.org>
Tue, 22 Apr 2014 20:52:43 +0000 (22:52 +0200)
committerJens Geyer <jensg@apache.org>
Tue, 22 Apr 2014 20:52:43 +0000 (22:52 +0200)
Client: C#
Patch: Beat Käslin

This closes #104

commit 21c33abd59a2333c48722933c6894d8ed145e638
 Author: Beat Kaeslin <beat.kaeslin@siemens.com>
 Date: 2014-04-16T14:07:58Z

Add TLS transport for C#

commit 60a0baa1797b0ef0ea6f8c21e5b81a78cdfcdf16
 Author: Beat Kaeslin <beat.kaeslin@siemens.com>
 Date: 2014-04-17T06:23:57Z

csharp tests moved to the end

lib/csharp/Makefile.am
lib/csharp/src/Thrift.csproj
lib/csharp/src/Transport/TTLSServerSocket.cs [new file with mode: 0644]
lib/csharp/src/Transport/TTLSSocket.cs [new file with mode: 0644]
lib/csharp/test/ThriftTest/TestClient.cs
lib/csharp/test/ThriftTest/TestServer.cs
lib/csharp/test/ThriftTest/maketest.sh
test/test.sh

index e847237..b99674c 100644 (file)
@@ -60,6 +60,8 @@ THRIFTCODE= \
             src/Transport/TMemoryBuffer.cs \
             src/Transport/TNamedPipeClientTransport.cs \
             src/Transport/TNamedPipeServerTransport.cs \
+            src/Transport/TTLSSocket.cs \
+            src/Transport/TTLSServerSocket.cs \
             src/TProcessor.cs \
             src/TException.cs \
             src/TApplicationException.cs
index d475ed6..1f10868 100644 (file)
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+\feff<?xml version="1.0" encoding="utf-8"?>
 <!--
   Licensed to the Apache Software Foundation (ASF) under one
   or more contributor license agreements. See the NOTICE file
     <Compile Include="Transport\TServerTransport.cs" />
     <Compile Include="Transport\TSocket.cs" />
     <Compile Include="Transport\TStreamTransport.cs" />
+    <Compile Include="Transport\TTLSServerSocket.cs" />
+    <Compile Include="Transport\TTLSSocket.cs" />
     <Compile Include="Transport\TTransport.cs" />
     <Compile Include="Transport\TTransportException.cs" />
     <Compile Include="Transport\TTransportFactory.cs" />
diff --git a/lib/csharp/src/Transport/TTLSServerSocket.cs b/lib/csharp/src/Transport/TTLSServerSocket.cs
new file mode 100644 (file)
index 0000000..948b1d7
--- /dev/null
@@ -0,0 +1,186 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System;
+using System.Net.Sockets;
+using System.Security.Cryptography.X509Certificates;
+
+namespace Thrift.Transport
+{
+       /// <summary>
+       /// SSL Server Socket Wrapper Class
+       /// </summary>
+       public class TTLSServerSocket : TServerTransport
+       {
+               /// <summary>
+               /// Underlying tcp server
+               /// </summary>
+               private TcpListener server = null;
+
+               /// <summary>
+               /// The port where the socket listen
+               /// </summary>
+               private int port = 0;
+
+               /// <summary>
+               /// Timeout for the created server socket
+               /// </summary>
+               private int clientTimeout = 0;
+
+               /// <summary>
+               /// Whether or not to wrap new TSocket connections in buffers
+               /// </summary>
+               private bool useBufferedSockets = false;
+
+               /// <summary>
+               /// The servercertificate with the private- and public-key
+               /// </summary>
+               private X509Certificate serverCertificate;
+
+
+               /// <summary>
+               /// Initializes a new instance of the <see cref="TTLSServerSocket" /> class.
+               /// </summary>
+               /// <param name="port">The port where the server runs.</param>
+               /// <param name="certificate">The certificate object.</param>
+               public TTLSServerSocket(int port, X509Certificate2 certificate)
+                       : this(port,  0, certificate)
+               {
+               }
+
+               /// <summary>
+               /// Initializes a new instance of the <see cref="TTLSServerSocket" /> class.
+               /// </summary>
+               /// <param name="port">The port where the server runs.</param>
+               /// <param name="clientTimeout">Send/receive timeout.</param>
+               /// <param name="certificate">The certificate object.</param>
+               public TTLSServerSocket(int port, int clientTimeout, X509Certificate2 certificate)
+                       : this(port, clientTimeout, false, certificate)
+               {
+               }
+
+               /// <summary>
+               /// Initializes a new instance of the <see cref="TTLSServerSocket" /> class.
+               /// </summary>
+               /// <param name="port">The port where the server runs.</param>
+               /// <param name="clientTimeout">Send/receive timeout.</param>
+               /// <param name="useBufferedSockets">If set to <c>true</c> [use buffered sockets].</param>
+               /// <param name="certificate">The certificate object.</param>
+               public TTLSServerSocket(int port, int clientTimeout, bool useBufferedSockets, X509Certificate2 certificate)
+               {
+                       if (!certificate.HasPrivateKey)
+                       {
+                               throw new TTransportException(TTransportException.ExceptionType.Unknown, "Your server-certificate needs to have a private key");
+                       }
+
+                       this.port = port;
+                       this.serverCertificate = certificate;
+                       this.useBufferedSockets = useBufferedSockets;
+                       try
+                       {
+                               // Create server socket
+                               server = new TcpListener(System.Net.IPAddress.Any, this.port);
+                               server.Server.NoDelay = true;
+                       }
+                       catch (Exception)
+                       {
+                               server = null;
+                               throw new TTransportException("Could not create ServerSocket on port " + port + ".");
+                       }
+               }
+
+               /// <summary>
+               /// Starts the server.
+               /// </summary>
+               public override void Listen()
+               {
+                       // Make sure accept is not blocking
+                       if (this.server != null)
+                       {
+                               try
+                               {
+                                       this.server.Start();
+                               }
+                               catch (SocketException sx)
+                               {
+                                       throw new TTransportException("Could not accept on listening socket: " + sx.Message);
+                               }
+                       }
+               }
+
+               /// <summary>
+               /// Callback for Accept Implementation
+               /// </summary>
+               /// <returns>
+               /// TTransport-object.
+               /// </returns>
+               protected override TTransport AcceptImpl()
+               {
+                       if (this.server == null)
+                       {
+                               throw new TTransportException(TTransportException.ExceptionType.NotOpen, "No underlying server socket.");
+                       }
+
+                       try
+                       {
+                               TcpClient client = this.server.AcceptTcpClient();
+                               client.SendTimeout = client.ReceiveTimeout = this.clientTimeout;
+
+                               //wrap the client in an SSL Socket passing in the SSL cert
+                               TTLSSocket socket = new TTLSSocket(client, this.serverCertificate, true);
+
+                               socket.setupTLS();
+
+                               if (useBufferedSockets)
+                               {
+                                       TBufferedTransport trans = new TBufferedTransport(socket);
+                                       return trans;
+                               }
+                               else
+                               {
+                                       return socket;
+                               }
+                               
+                       }
+                       catch (Exception ex)
+                       {
+                               throw new TTransportException(ex.ToString());
+                       }
+               }
+
+               /// <summary>
+               /// Stops the Server
+               /// </summary>
+               public override void Close()
+               {
+                       if (this.server != null)
+                       {
+                               try
+                               {
+                                       this.server.Stop();
+                               }
+                               catch (Exception ex)  
+                               {
+                                       throw new TTransportException("WARNING: Could not close server socket: " + ex);
+                               }
+                               this.server = null;
+                       }
+               }
+       }
+}
diff --git a/lib/csharp/src/Transport/TTLSSocket.cs b/lib/csharp/src/Transport/TTLSSocket.cs
new file mode 100644 (file)
index 0000000..beb5876
--- /dev/null
@@ -0,0 +1,284 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System;
+using System.Net.Security;
+using System.Net.Sockets;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
+
+namespace Thrift.Transport
+{
+       /// <summary>
+       /// SSL Socket Wrapper class
+       /// </summary>
+       public class TTLSSocket : TStreamTransport
+       {
+               /// <summary>
+               /// Internal TCP Client
+               /// </summary>
+               private TcpClient client = null;
+
+               /// <summary>
+               /// The host
+               /// </summary>
+               private string host = null;
+
+               /// <summary>
+               /// The port
+               /// </summary>
+               private int port = 0;
+
+               /// <summary>
+               /// The timeout for the connection
+               /// </summary>
+               private int timeout = 0;
+
+               /// <summary>
+               /// Internal SSL Stream for IO
+               /// </summary>
+               private SslStream secureStream = null;
+
+               /// <summary>
+               /// Defines wheter or not this socket is a server socket<br/>
+               /// This is used for the TLS-authentication
+               /// </summary>
+               private bool isServer = false;
+
+               /// <summary>
+               /// The certificate
+               /// </summary>
+               private X509Certificate certificate = null;
+
+               /// <summary>
+               /// Initializes a new instance of the <see cref="TTLSSocket"/> class.
+               /// </summary>
+               /// <param name="client">An already created TCP-client</param>
+               /// <param name="certificate">The certificate.</param>
+               /// <param name="isServer">if set to <c>true</c> [is server].</param>
+               public TTLSSocket(TcpClient client, X509Certificate certificate, bool isServer = false)
+               {
+                       this.client = client;
+                       this.certificate = certificate;
+                       this.isServer = isServer;
+
+                       if (IsOpen)
+                       {
+                               base.inputStream = client.GetStream();
+                               base.outputStream = client.GetStream();
+                       }
+               }
+
+               /// <summary>
+               /// Initializes a new instance of the <see cref="TTLSSocket"/> class.
+               /// </summary>
+               /// <param name="host">The host, where the socket should connect to.</param>
+               /// <param name="port">The port.</param>
+               /// <param name="certificatePath">The certificate path.</param>
+               public TTLSSocket(string host, int port, string certificatePath)
+                       : this(host, port, 0, X509Certificate.CreateFromCertFile(certificatePath))
+               {
+               }
+
+               /// <summary>
+               /// Initializes a new instance of the <see cref="TTLSSocket"/> class.
+               /// </summary>
+               /// <param name="host">The host, where the socket should connect to.</param>
+               /// <param name="port">The port.</param>
+               /// <param name="certificate">The certificate.</param>
+               public TTLSSocket(string host, int port, X509Certificate certificate)
+                       : this(host, port, 0, certificate)
+               {
+               }
+
+               /// <summary>
+               /// Initializes a new instance of the <see cref="TTLSSocket"/> class.
+               /// </summary>
+               /// <param name="host">The host, where the socket should connect to.</param>
+               /// <param name="port">The port.</param>
+               /// <param name="timeout">The timeout.</param>
+               /// <param name="certificate">The certificate.</param>
+               public TTLSSocket(string host, int port, int timeout, X509Certificate certificate)
+               {
+                       this.host = host;
+                       this.port = port;
+                       this.timeout = timeout;
+                       this.certificate = certificate;
+
+                       InitSocket();
+               }
+
+               /// <summary>
+               /// Creates the TcpClient and sets the timeouts
+               /// </summary>
+               private void InitSocket()
+               {
+                       this.client = new TcpClient();
+                       client.ReceiveTimeout = client.SendTimeout = timeout;
+                       client.Client.NoDelay = true;
+               }
+
+               /// <summary>
+               /// Sets Send / Recv Timeout for IO
+               /// </summary>
+               public int Timeout
+               {
+                       set
+                       {
+                               this.client.ReceiveTimeout = this.client.SendTimeout = this.timeout = value;
+                       }
+               }
+
+               /// <summary>
+               /// Gets the TCP client.
+               /// </summary>
+               public TcpClient TcpClient
+               {
+                       get
+                       {
+                               return client;
+                       }
+               }
+
+               /// <summary>
+               /// Gets the host.
+               /// </summary>
+               public string Host
+               {
+                       get
+                       {
+                               return host;
+                       }
+               }
+
+               /// <summary>
+               /// Gets the port.
+               /// </summary>
+               public int Port
+               {
+                       get
+                       {
+                               return port;
+                       }
+               }
+
+               /// <summary>
+               /// Gets a value indicating whether TCP Client is Cpen 
+               /// </summary>
+               public override bool IsOpen
+               {
+                       get
+                       {
+                               if (this.client == null)
+                               {
+                                       return false;
+                               }
+
+                               return this.client.Connected;
+                       }
+               }
+
+               /// <summary>
+               /// Validates the certificates!<br/>
+               /// </summary>
+               /// <param name="sender">The sender-object.</param>
+               /// <param name="certificate">The used certificate.</param>
+               /// <param name="chain">The certificate chain.</param>
+               /// <param name="sslPolicyErrors">An enum, which lists all the errors from the .NET certificate check.</param>
+               /// <returns></returns>
+               private bool CertificateValidator(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslValidationErrors)
+               {
+                       return (sslValidationErrors == SslPolicyErrors.None);
+               }
+
+               /// <summary>
+               /// Connects to the host and starts the routine, which sets up the TLS
+               /// </summary>
+               public override void Open()
+               {
+                       if (IsOpen)
+                       {
+                               throw new TTransportException(TTransportException.ExceptionType.AlreadyOpen, "Socket already connected");
+                       }
+
+                       if (String.IsNullOrEmpty(host))
+                       {
+                               throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open null host");
+                       }
+
+                       if (port <= 0)
+                       {
+                               throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open without port");
+                       }
+
+                       if (client == null)
+                       {
+                               InitSocket();
+                       }
+
+                       client.Connect(host, port);
+
+                       setupTLS();
+               }
+
+               /// <summary>
+               /// Creates a TLS-stream and lays it over the existing socket
+               /// </summary>
+               public void setupTLS()
+               {
+                       if (isServer)
+                       {
+                               // Server authentication
+                               this.secureStream = new SslStream(this.client.GetStream(), false);
+                               this.secureStream.AuthenticateAsServer(this.certificate, false, SslProtocols.Tls, true);
+                       }
+                       else
+                       {
+                               // Client authentication
+                               X509CertificateCollection validCerts = new X509CertificateCollection();
+                               validCerts.Add(certificate);
+
+                               this.secureStream = new SslStream(this.client.GetStream(), false, new RemoteCertificateValidationCallback(CertificateValidator));
+                               this.secureStream.AuthenticateAsClient(host, validCerts, SslProtocols.Tls, true);
+                       }
+
+                       inputStream = this.secureStream;
+                       outputStream = this.secureStream;
+               }
+
+               /// <summary>
+               /// Closes the SSL Socket
+               /// </summary>
+               public override void Close()
+               {
+                       base.Close();
+                       if (this.client != null)
+                       {
+                               this.client.Close();
+                               this.client = null;
+                       }
+
+                       if (this.secureStream != null)
+                       {
+                               this.secureStream.Close();
+                               this.secureStream = null;
+                       }
+               }
+       }
+}
index ba2d4d0..593169f 100644 (file)
@@ -30,6 +30,7 @@ namespace Test
        public class TestClient
        {
                private static int numIterations = 1;
+               private static string protocol = "";
 
                public static void Execute(string[] args)
                {
@@ -39,7 +40,7 @@ namespace Test
                                int port = 9090;
                                string url = null, pipe = null;
                                int numThreads = 1;
-                               bool buffered = false, framed = false;
+                               bool buffered = false, framed = false, encrypted = false;
 
                                try
                                {
@@ -81,6 +82,21 @@ namespace Test
                                                {
                                                        numThreads = Convert.ToInt32(args[++i]);
                                                }
+                                               else if (args[i] == "-ssl")
+                                               {
+                                                       encrypted = true;
+                                                       Console.WriteLine("Using encrypted transport");
+                                               }
+                                               else if (args[i] == "-compact")
+                                               {
+                                                       protocol = "compact";
+                                                       Console.WriteLine("Using compact protocol");
+                                               }
+                                               else if (args[i] == "-json")
+                                               {
+                                                       protocol = "json";
+                                                       Console.WriteLine("Using JSON protocol");
+                                               }
                                        }
                                }
                                catch (Exception e)
@@ -88,8 +104,6 @@ namespace Test
                                        Console.WriteLine(e.StackTrace);
                                }
 
-
-
                                //issue tests on separate threads simultaneously
                                Thread[] threads = new Thread[numThreads];
                                DateTime start = DateTime.Now;
@@ -101,17 +115,22 @@ namespace Test
                                        {
                                                // endpoint transport
                                                TTransport trans = null;
-                                               ifpipe != null)
+                                               if (pipe != null)
                                                        trans = new TNamedPipeClientTransport(pipe);
                                                else
-                                                       trans = new TSocket(host, port);
-                                               
+                                               {
+                                                       if (encrypted)
+                                                               trans = new TTLSSocket(host, port, "../../../../../keys/client.pem");
+                                                       else
+                                                               trans = new TSocket(host, port);
+                                               }
+
                                                // layered transport
                                                if (buffered)
                                                        trans = new TBufferedTransport(trans as TStreamTransport);
                                                if (framed)
                                                        trans = new TFramedTransport(trans);
-                                               
+
                                                //ensure proper open/close of transport
                                                trans.Open();
                                                trans.Close();
@@ -151,9 +170,15 @@ namespace Test
 
                public static void ClientTest(TTransport transport)
                {
-                       TBinaryProtocol binaryProtocol = new TBinaryProtocol(transport);
-
-                       ThriftTest.Client client = new ThriftTest.Client(binaryProtocol);
+                       TProtocol proto;
+                       if (protocol == "compact")
+                               proto = new TCompactProtocol(transport);
+                       else if (protocol == "json")
+                               proto = new TJSONProtocol(transport);
+                       else
+                               proto = new TBinaryProtocol(transport);
+
+                       ThriftTest.Client client = new ThriftTest.Client(proto);
                        try
                        {
                                if (!transport.IsOpen)
@@ -430,7 +455,6 @@ namespace Test
                        }
                        Console.WriteLine("}");
 
-
                        sbyte arg0 = 1;
                        int arg1 = 2;
                        long arg2 = long.MaxValue;
index 965a7de..f0e9abb 100644 (file)
@@ -23,6 +23,7 @@
 // http://developers.facebook.com/thrift/
 using System;
 using System.Collections.Generic;
+using System.Security.Cryptography.X509Certificates;
 using Thrift.Collections;
 using Thrift.Test; //generated code
 using Thrift.Transport;
@@ -321,7 +322,7 @@ namespace Test
                {
                        try
                        {
-                               bool useBufferedSockets = false, useFramed = false;
+                               bool useBufferedSockets = false, useFramed = false, useEncryption = false, compact = false, json = false;
                                int port = 9090, i = 0;
                                string pipe = null;
                                if (args.Length > 0)
@@ -343,7 +344,7 @@ namespace Test
                                                {
                                                        // as default
                                                }
-                                               else if ( args[i] == "buffered" )
+                                               else if (args[i] == "buffered")
                                                {
                                                        useBufferedSockets = true;
                                                }
@@ -351,6 +352,18 @@ namespace Test
                                                {
                                                        useFramed = true;
                                                }
+                                               else if (args[i] == "ssl")
+                                               {
+                                                       useEncryption = true;
+                                               }
+                                               else if (args[i] == "compact" )
+                                               {
+                                                       compact = true;
+                                               }
+                                               else if (args[i] == "json" )
+                                               {
+                                                       json = true;
+                                               }
                                                else
                                                {
                                                        // Fall back to the older boolean syntax
@@ -371,15 +384,30 @@ namespace Test
                                }
                                else
                                {
-                                       trans = new TServerSocket(port, 0, useBufferedSockets);
+                                       if (useEncryption)
+                                       {
+                                               trans = new TTLSServerSocket(port, 0, useBufferedSockets, new X509Certificate2("../../../../../keys/server.pem"));
+                                       }
+                                       else
+                                       {
+                                               trans = new TServerSocket(port, 0, useBufferedSockets);
+                                       }
                                }
 
+                               TProtocolFactory proto;
+                               if ( compact )
+                                       proto = new TCompactProtocol.Factory();
+                               else if ( json )
+                                       proto = new TJSONProtocol.Factory();
+                               else
+                                       proto = new TBinaryProtocol.Factory();
+                                       
                                // Simple Server
                                TServer serverEngine;
                                if ( useFramed )
-                                       serverEngine = new TSimpleServer(testProcessor, trans, new TFramedTransport.Factory());
+                                       serverEngine = new TSimpleServer(testProcessor, trans, new TFramedTransport.Factory(), proto);
                                else
-                                       serverEngine = new TSimpleServer(testProcessor, trans);
+                                       serverEngine = new TSimpleServer(testProcessor, trans, new TTransportFactory(), proto);
 
                                // ThreadPool Server
                                // serverEngine = new TThreadPoolServer(testProcessor, tServerSocket);
@@ -391,9 +419,11 @@ namespace Test
 
                                // Run it
                                string where = ( pipe != null ? "on pipe "+pipe : "on port " + port);
-                               Console.WriteLine("Starting the server " +where+
+                               Console.WriteLine("Starting the server " + where +
                                        (useBufferedSockets ? " with buffered socket" : "") + 
                                        (useFramed ? " with framed transport" : "") + 
+                                       (useEncryption ? " with encryption" : "") + 
+                                       (compact ? " with compact protocol" : "") + 
                                        "...");
                                serverEngine.Serve();
 
index 86c1a11..210a523 100644 (file)
@@ -26,4 +26,5 @@ gmcs  /out:TestClientServer.exe /reference:../../Thrift.dll /reference:ThriftImp
 export MONO_PATH=../../
 
 timeout 120 ./TestClientServer.exe server &
+sleep 1
 ./TestClientServer.exe client
index b73635f..91ac175 100755 (executable)
@@ -102,6 +102,10 @@ nodejs_protocols="binary json"
 nodejs_transports="buffered framed"
 nodejs_sockets="ip ip-ssl"
 
+csharp_protocols="binary compact json"
+csharp_transports="buffered framed"
+csharp_sockets="ip ip-ssl"
+
 ant -f ../lib/java/build.xml compile-test 1>/dev/null
 
 ######### java client - java server #############
@@ -209,6 +213,24 @@ done
 # delete Unix Domain Socket used by cpp tests
 rm -f /tmp/ThriftTest.thrift
 
+######### csharp client - csharp server #############
+export MONO_PATH=../lib/csharp
+for proto in $csharp_protocols; do
+  for trans in $csharp_transports; do
+    for sock in $csharp_sockets; do
+      case "$sock" in
+        "ip" ) extraparam="";;
+        "ip-ssl" ) extraparam="--ssl";;
+      esac
+      do_test "csharp-csharp"   "${proto}" "${trans}-${sock}" \
+              "../lib/csharp/test/ThriftTest/TestClientServer.exe client --protocol=${proto} --transport=${trans} ${extraparam}" \
+              "../lib/csharp/test/ThriftTest/TestClientServer.exe server --protocol=${proto} --transport=${trans} ${extraparam}" \
+              "10" "10"
+    done
+  done
+done
+
+
 do_test "py-py" "binary" "buffered-ip" \
         "py/TestClient.py --proto=binary --port=9090 --host=localhost --genpydir=py/gen-py" \
         "py/TestServer.py --proto=binary --port=9090 --genpydir=py/gen-py TSimpleServer" \