THRIFT-2408 Named Pipe Transport Option for C#

Patch: Carl Yeksigian & Jens Geyer
diff --git a/lib/csharp/Makefile.am b/lib/csharp/Makefile.am
index 71cc83b..e847237 100644
--- a/lib/csharp/Makefile.am
+++ b/lib/csharp/Makefile.am
@@ -58,6 +58,8 @@
             src/Transport/THttpClient.cs \
             src/Transport/THttpHandler.cs \
             src/Transport/TMemoryBuffer.cs \
+            src/Transport/TNamedPipeClientTransport.cs \
+            src/Transport/TNamedPipeServerTransport.cs \
             src/TProcessor.cs \
             src/TException.cs \
             src/TApplicationException.cs
diff --git a/lib/csharp/src/Thrift.csproj b/lib/csharp/src/Thrift.csproj
index 58d3793..d475ed6 100644
--- a/lib/csharp/src/Thrift.csproj
+++ b/lib/csharp/src/Thrift.csproj
@@ -112,6 +112,8 @@
     <Compile Include="Transport\TFramedTransport.cs" />
     <Compile Include="Transport\THttpClient.cs" />
     <Compile Include="Transport\THttpHandler.cs" />
+    <Compile Include="Transport\TNamedPipeClientTransport.cs" />
+    <Compile Include="Transport\TNamedPipeServerTransport.cs" />
     <Compile Include="Transport\TServerSocket.cs" />
     <Compile Include="Transport\TServerTransport.cs" />
     <Compile Include="Transport\TSocket.cs" />
diff --git a/lib/csharp/src/Transport/TNamedPipeClientTransport.cs b/lib/csharp/src/Transport/TNamedPipeClientTransport.cs
new file mode 100644
index 0000000..4c320e6
--- /dev/null
+++ b/lib/csharp/src/Transport/TNamedPipeClientTransport.cs
@@ -0,0 +1,72 @@
+using System.IO.Pipes;
+
+namespace Thrift.Transport
+{
+	public class TNamedPipeClientTransport : TTransport
+	{
+		private NamedPipeClientStream client;
+		private string ServerName;
+		private string PipeName;
+
+		public TNamedPipeClientTransport(string pipe)
+		{
+			ServerName = ".";
+			PipeName = pipe;
+		}
+
+		public TNamedPipeClientTransport(string server, string pipe)
+		{
+			ServerName = (server != "") ? server : ".";
+			PipeName = pipe;
+		}
+
+		public override bool IsOpen
+		{
+			get { return client != null && client.IsConnected; }
+		}
+
+		public override void Open()
+		{
+			if (IsOpen)
+			{
+				throw new TTransportException(TTransportException.ExceptionType.AlreadyOpen);
+			}
+			client = new NamedPipeClientStream(ServerName, PipeName, PipeDirection.InOut, PipeOptions.None);
+			client.Connect();
+		}
+
+		public override void Close()
+		{
+			if (client != null)
+			{
+				client.Close();
+				client = null;
+			}
+		}
+
+		public override int Read(byte[] buf, int off, int len)
+		{
+			if (client == null)
+			{
+				throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+			}
+
+			return client.Read(buf, off, len);
+		}
+
+		public override void Write(byte[] buf, int off, int len)
+		{
+			if (client == null)
+			{
+				throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+			}
+
+			client.Write(buf, off, len);
+		}
+
+		protected override void Dispose(bool disposing)
+		{
+			client.Dispose();
+		}
+	}
+}
\ No newline at end of file
diff --git a/lib/csharp/src/Transport/TNamedPipeServerTransport.cs b/lib/csharp/src/Transport/TNamedPipeServerTransport.cs
new file mode 100644
index 0000000..b87898e
--- /dev/null
+++ b/lib/csharp/src/Transport/TNamedPipeServerTransport.cs
@@ -0,0 +1,113 @@
+using System;
+using System.Collections.Generic;
+using System.IO.Pipes;
+
+namespace Thrift.Transport
+{
+	public class TNamedPipeServerTransport : TServerTransport
+	{
+		/// <summary>
+		/// This is the address of the Pipe on the localhost.
+		/// </summary>
+		private readonly string pipeAddress;
+		NamedPipeServerStream stream = null;
+
+		public TNamedPipeServerTransport(string pipeAddress)
+		{
+			this.pipeAddress = pipeAddress;
+		}
+
+		public override void Listen()
+		{
+			// nothing to do here
+		}
+
+		public override void Close()
+		{
+			if (stream != null)
+			{
+				try
+				{
+					stream.Close();
+					stream.Dispose();
+				}
+				finally
+				{
+					stream = null;
+				}
+			}
+		}
+
+		private void EnsurePipeInstance()
+		{
+			if( stream == null)
+				stream = new NamedPipeServerStream(
+					pipeAddress, PipeDirection.InOut, 254,
+					PipeTransmissionMode.Byte,
+					PipeOptions.None, 4096, 4096 /*TODO: security*/);
+		}
+
+		protected override TTransport AcceptImpl()
+		{
+			try
+			{
+				EnsurePipeInstance();
+				stream.WaitForConnection();
+				var trans = new ServerTransport(stream);
+				stream = null;  // pass ownership to ServerTransport
+				return trans;
+			}
+			catch (Exception e)
+			{
+				Close();
+				throw new TTransportException(TTransportException.ExceptionType.NotOpen, e.Message);
+			}
+		}
+
+		private class ServerTransport : TTransport
+		{
+			private NamedPipeServerStream server;
+			public ServerTransport(NamedPipeServerStream server)
+			{
+				this.server = server;
+			}
+
+			public override bool IsOpen
+			{
+				get { return server != null && server.IsConnected; }
+			}
+
+			public override void Open()
+			{
+			}
+
+			public override void Close()
+			{
+				if (server != null) server.Close();
+			}
+
+			public override int Read(byte[] buf, int off, int len)
+			{
+				if (server == null)
+				{
+					throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+				}
+				return server.Read(buf, off, len);
+			}
+
+			public override void Write(byte[] buf, int off, int len)
+			{
+				if (server == null)
+				{
+					throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+				}
+				server.Write(buf, off, len);
+			}
+
+			protected override void Dispose(bool disposing)
+			{
+				server.Dispose();
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/lib/csharp/test/ThriftTest/TestClient.cs b/lib/csharp/test/ThriftTest/TestClient.cs
index c7b81b4..ba2d4d0 100644
--- a/lib/csharp/test/ThriftTest/TestClient.cs
+++ b/lib/csharp/test/ThriftTest/TestClient.cs
@@ -37,7 +37,7 @@
 			{
 				string host = "localhost";
 				int port = 9090;
-				string url = null;
+				string url = null, pipe = null;
 				int numThreads = 1;
 				bool buffered = false, framed = false;
 
@@ -72,6 +72,11 @@
 							framed = true;
 							Console.WriteLine("Using framed transport");
 						}
+						else if (args[i] == "-pipe")  // -pipe <name>
+						{
+							pipe = args[++i];
+							Console.WriteLine("Using named pipes transport");
+						}
 						else if (args[i] == "-t")
 						{
 							numThreads = Convert.ToInt32(args[++i]);
@@ -94,7 +99,14 @@
 					threads[test] = t;
 					if (url == null)
 					{
-						TTransport trans = new TSocket(host, port);
+						// endpoint transport
+						TTransport trans = null;
+						if( pipe != null)
+							trans = new TNamedPipeClientTransport(pipe);
+						else
+							trans = new TSocket(host, port);
+						
+						// layered transport
 						if (buffered)
 							trans = new TBufferedTransport(trans as TStreamTransport);
 						if (framed)
diff --git a/lib/csharp/test/ThriftTest/TestServer.cs b/lib/csharp/test/ThriftTest/TestServer.cs
index 8a4e605..965a7de 100644
--- a/lib/csharp/test/ThriftTest/TestServer.cs
+++ b/lib/csharp/test/ThriftTest/TestServer.cs
@@ -117,25 +117,25 @@
 				return thing;
 			}
 
-            public Dictionary<string, string> testStringMap(Dictionary<string, string> thing)
-            {
-                Console.WriteLine("testStringMap({");
-                bool first = true;
-                foreach (string key in thing.Keys)
-                {
-                    if (first)
-                    {
-                        first = false;
-                    }
-                    else
-                    {
-                        Console.WriteLine(", ");
-                    }
-                    Console.WriteLine(key + " => " + thing[key]);
-                }
-                Console.WriteLine("})");
-                return thing;
-            }
+			public Dictionary<string, string> testStringMap(Dictionary<string, string> thing)
+			{
+				Console.WriteLine("testStringMap({");
+				bool first = true;
+				foreach (string key in thing.Keys)
+				{
+					if (first)
+					{
+						first = false;
+					}
+					else
+					{
+						Console.WriteLine(", ");
+					}
+					Console.WriteLine(key + " => " + thing[key]);
+				}
+				Console.WriteLine("})");
+				return thing;
+			}
 
 			public THashSet<int> testSet(THashSet<int> thing)
 			{
@@ -322,29 +322,39 @@
 			try
 			{
 				bool useBufferedSockets = false, useFramed = false;
-				int port = 9090;
+				int port = 9090, i = 0;
+				string pipe = null;
 				if (args.Length > 0)
 				{
-					port = int.Parse(args[0]);
-
-					if (args.Length > 1)
+					i = 0;
+					if (args[i] == "-pipe")  // -pipe name
 					{
-						if ( args[1] == "raw" )
+						pipe = args[++i];
+					}
+					else  // default to port number (compatibility)
+					{
+						port = int.Parse(args[i]);
+					}
+
+					++i;
+					if (args.Length > i)
+					{
+						if ( args[i] == "raw" )
 						{
 							// as default
 						}
-						else if ( args[1] == "buffered" )
+						else if ( args[i] == "buffered" )
 						{
 							useBufferedSockets = true;
 						}
-						else if ( args[1] == "framed" )
+						else if (args[i] == "framed")
 						{
 							useFramed = true;
 						}
 						else
 						{
 							// Fall back to the older boolean syntax
-							bool.TryParse(args[1], out useBufferedSockets);
+							bool.TryParse(args[i], out useBufferedSockets);
 						}
 					}
 				}
@@ -354,14 +364,22 @@
 				ThriftTest.Processor testProcessor = new ThriftTest.Processor(testHandler);
 
 				// Transport
-				TServerSocket tServerSocket = new TServerSocket(port, 0, useBufferedSockets);
+				TServerTransport trans;
+				if( pipe != null)
+				{
+					trans = new TNamedPipeServerTransport(pipe);
+				}
+				else
+				{
+					trans = new TServerSocket(port, 0, useBufferedSockets);
+				}
 
 				// Simple Server
 				TServer serverEngine;
 				if ( useFramed )
-					serverEngine = new TSimpleServer(testProcessor, tServerSocket, new TFramedTransport.Factory());
+					serverEngine = new TSimpleServer(testProcessor, trans, new TFramedTransport.Factory());
 				else
-					serverEngine = new TSimpleServer(testProcessor, tServerSocket);
+					serverEngine = new TSimpleServer(testProcessor, trans);
 
 				// ThreadPool Server
 				// serverEngine = new TThreadPoolServer(testProcessor, tServerSocket);
@@ -372,7 +390,8 @@
 				testHandler.server = serverEngine;
 
 				// Run it
-				Console.WriteLine("Starting the server on port " + port + 
+				string where = ( pipe != null ? "on pipe "+pipe : "on port " + port);
+				Console.WriteLine("Starting the server " +where+
 					(useBufferedSockets ? " with buffered socket" : "") + 
 					(useFramed ? " with framed transport" : "") + 
 					"...");