Revert "THRIFT-4982 Remove deprecated C# bindings from the code base"
Only compiler, test, lib and tutorial code.
diff --git a/lib/csharp/src/Transport/TBufferedTransport.cs b/lib/csharp/src/Transport/TBufferedTransport.cs
new file mode 100644
index 0000000..8870988
--- /dev/null
+++ b/lib/csharp/src/Transport/TBufferedTransport.cs
@@ -0,0 +1,194 @@
+/**
+ * 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.IO;
+
+namespace Thrift.Transport
+{
+ public class TBufferedTransport : TTransport, IDisposable
+ {
+ private readonly int bufSize;
+ private readonly MemoryStream inputBuffer = new MemoryStream(0);
+ private readonly MemoryStream outputBuffer = new MemoryStream(0);
+ private readonly TTransport transport;
+
+ public TBufferedTransport(TTransport transport, int bufSize = 1024)
+ {
+ if (transport == null)
+ throw new ArgumentNullException("transport");
+ if (bufSize <= 0)
+ throw new ArgumentException("bufSize", "Buffer size must be a positive number.");
+ this.transport = transport;
+ this.bufSize = bufSize;
+ }
+
+ public TTransport UnderlyingTransport
+ {
+ get
+ {
+ CheckNotDisposed();
+ return transport;
+ }
+ }
+
+ public override bool IsOpen
+ {
+ get
+ {
+ // We can legitimately throw here but be nice a bit.
+ // CheckNotDisposed();
+ return !_IsDisposed && transport.IsOpen;
+ }
+ }
+
+ public override void Open()
+ {
+ CheckNotDisposed();
+ transport.Open();
+ }
+
+ public override void Close()
+ {
+ CheckNotDisposed();
+ transport.Close();
+ }
+
+ public override int Read(byte[] buf, int off, int len)
+ {
+ CheckNotDisposed();
+ ValidateBufferArgs(buf, off, len);
+ if (!IsOpen)
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+
+ if (inputBuffer.Capacity < bufSize)
+ inputBuffer.Capacity = bufSize;
+
+ while (true)
+ {
+ int got = inputBuffer.Read(buf, off, len);
+ if (got > 0)
+ return got;
+
+ inputBuffer.Seek(0, SeekOrigin.Begin);
+ inputBuffer.SetLength(inputBuffer.Capacity);
+ int filled = transport.Read(inputBuffer.GetBuffer(), 0, (int)inputBuffer.Length);
+ inputBuffer.SetLength(filled);
+ if (filled == 0)
+ return 0;
+ }
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ CheckNotDisposed();
+ ValidateBufferArgs(buf, off, len);
+ if (!IsOpen)
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ // Relative offset from "off" argument
+ int offset = 0;
+ if (outputBuffer.Length > 0)
+ {
+ int capa = (int)(outputBuffer.Capacity - outputBuffer.Length);
+ int writeSize = capa <= len ? capa : len;
+ outputBuffer.Write(buf, off, writeSize);
+ offset += writeSize;
+ if (writeSize == capa)
+ {
+ transport.Write(outputBuffer.GetBuffer(), 0, (int)outputBuffer.Length);
+ outputBuffer.SetLength(0);
+ }
+ }
+ while (len - offset >= bufSize)
+ {
+ transport.Write(buf, off + offset, bufSize);
+ offset += bufSize;
+ }
+ int remain = len - offset;
+ if (remain > 0)
+ {
+ if (outputBuffer.Capacity < bufSize)
+ outputBuffer.Capacity = bufSize;
+ outputBuffer.Write(buf, off + offset, remain);
+ }
+ }
+
+ private void InternalFlush()
+ {
+ if (!IsOpen)
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ if (outputBuffer.Length > 0)
+ {
+ transport.Write(outputBuffer.GetBuffer(), 0, (int)outputBuffer.Length);
+ outputBuffer.SetLength(0);
+ }
+ }
+
+ public override void Flush()
+ {
+ CheckNotDisposed();
+ InternalFlush();
+
+ transport.Flush();
+ }
+
+ public override IAsyncResult BeginFlush(AsyncCallback callback, object state)
+ {
+ CheckNotDisposed();
+ InternalFlush();
+
+ return transport.BeginFlush( callback, state);
+ }
+
+ public override void EndFlush(IAsyncResult asyncResult)
+ {
+ transport.EndFlush( asyncResult);
+ }
+
+
+
+ protected void CheckNotDisposed()
+ {
+ if (_IsDisposed)
+ throw new ObjectDisposedException("TBufferedTransport");
+ }
+
+ #region " IDisposable Support "
+ protected bool _IsDisposed { get; private set; }
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!_IsDisposed)
+ {
+ if (disposing)
+ {
+ if (inputBuffer != null)
+ inputBuffer.Dispose();
+ if (outputBuffer != null)
+ outputBuffer.Dispose();
+ if (transport != null)
+ transport.Dispose();
+ }
+ }
+ _IsDisposed = true;
+ }
+ #endregion
+ }
+}
diff --git a/lib/csharp/src/Transport/TFramedTransport.cs b/lib/csharp/src/Transport/TFramedTransport.cs
new file mode 100644
index 0000000..a746a32
--- /dev/null
+++ b/lib/csharp/src/Transport/TFramedTransport.cs
@@ -0,0 +1,205 @@
+/**
+ * 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.IO;
+
+namespace Thrift.Transport
+{
+ public class TFramedTransport : TTransport, IDisposable
+ {
+ private readonly TTransport transport;
+ private readonly MemoryStream writeBuffer = new MemoryStream(1024);
+ private readonly MemoryStream readBuffer = new MemoryStream(1024);
+
+ private const int HeaderSize = 4;
+ private readonly byte[] headerBuf = new byte[HeaderSize];
+
+ public class Factory : TTransportFactory
+ {
+ public override TTransport GetTransport(TTransport trans)
+ {
+ return new TFramedTransport(trans);
+ }
+ }
+
+ public TFramedTransport(TTransport transport)
+ {
+ if (transport == null)
+ throw new ArgumentNullException("transport");
+ this.transport = transport;
+ InitWriteBuffer();
+ }
+
+ public override void Open()
+ {
+ CheckNotDisposed();
+ transport.Open();
+ }
+
+ public override bool IsOpen
+ {
+ get
+ {
+ // We can legitimately throw here but be nice a bit.
+ // CheckNotDisposed();
+ return !_IsDisposed && transport.IsOpen;
+ }
+ }
+
+ public override void Close()
+ {
+ CheckNotDisposed();
+ transport.Close();
+ }
+
+ public override int Read(byte[] buf, int off, int len)
+ {
+ CheckNotDisposed();
+ ValidateBufferArgs(buf, off, len);
+ if (!IsOpen)
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ int got = readBuffer.Read(buf, off, len);
+ if (got > 0)
+ {
+ return got;
+ }
+
+ // Read another frame of data
+ ReadFrame();
+
+ return readBuffer.Read(buf, off, len);
+ }
+
+ private void ReadFrame()
+ {
+ transport.ReadAll(headerBuf, 0, HeaderSize);
+ int size = DecodeFrameSize(headerBuf);
+
+ readBuffer.SetLength(size);
+ readBuffer.Seek(0, SeekOrigin.Begin);
+ byte[] buff = readBuffer.GetBuffer();
+ transport.ReadAll(buff, 0, size);
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ CheckNotDisposed();
+ ValidateBufferArgs(buf, off, len);
+ if (!IsOpen)
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ if (writeBuffer.Length + (long)len > (long)int.MaxValue)
+ Flush();
+ writeBuffer.Write(buf, off, len);
+ }
+
+ private void InternalFlush()
+ {
+ CheckNotDisposed();
+ if (!IsOpen)
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ byte[] buf = writeBuffer.GetBuffer();
+ int len = (int)writeBuffer.Length;
+ int data_len = len - HeaderSize;
+ if (data_len < 0)
+ throw new System.InvalidOperationException(); // logic error actually
+
+ // Inject message header into the reserved buffer space
+ EncodeFrameSize(data_len, buf);
+
+ // Send the entire message at once
+ transport.Write(buf, 0, len);
+
+ InitWriteBuffer();
+ }
+
+ public override void Flush()
+ {
+ CheckNotDisposed();
+ InternalFlush();
+
+ transport.Flush();
+ }
+
+ public override IAsyncResult BeginFlush(AsyncCallback callback, object state)
+ {
+ CheckNotDisposed();
+ InternalFlush();
+
+ return transport.BeginFlush( callback, state);
+ }
+
+ public override void EndFlush(IAsyncResult asyncResult)
+ {
+ transport.EndFlush( asyncResult);
+ }
+
+ private void InitWriteBuffer()
+ {
+ // Reserve space for message header to be put right before sending it out
+ writeBuffer.SetLength(HeaderSize);
+ writeBuffer.Seek(0, SeekOrigin.End);
+ }
+
+ private static void EncodeFrameSize(int frameSize, byte[] buf)
+ {
+ buf[0] = (byte)(0xff & (frameSize >> 24));
+ buf[1] = (byte)(0xff & (frameSize >> 16));
+ buf[2] = (byte)(0xff & (frameSize >> 8));
+ buf[3] = (byte)(0xff & (frameSize));
+ }
+
+ private static int DecodeFrameSize(byte[] buf)
+ {
+ return
+ ((buf[0] & 0xff) << 24) |
+ ((buf[1] & 0xff) << 16) |
+ ((buf[2] & 0xff) << 8) |
+ ((buf[3] & 0xff));
+ }
+
+
+ private void CheckNotDisposed()
+ {
+ if (_IsDisposed)
+ throw new ObjectDisposedException("TFramedTransport");
+ }
+
+ #region " IDisposable Support "
+ private bool _IsDisposed;
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!_IsDisposed)
+ {
+ if (disposing)
+ {
+ if (readBuffer != null)
+ readBuffer.Dispose();
+ if (writeBuffer != null)
+ writeBuffer.Dispose();
+ if (transport != null)
+ transport.Dispose();
+ }
+ }
+ _IsDisposed = true;
+ }
+ #endregion
+ }
+}
diff --git a/lib/csharp/src/Transport/THttpClient.cs b/lib/csharp/src/Transport/THttpClient.cs
new file mode 100644
index 0000000..986799c
--- /dev/null
+++ b/lib/csharp/src/Transport/THttpClient.cs
@@ -0,0 +1,486 @@
+/**
+ * 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.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Threading;
+using System.Linq;
+using System.Security.Cryptography.X509Certificates;
+using System.IO.Compression;
+
+namespace Thrift.Transport
+{
+ public class THttpClient : TTransport, IDisposable
+ {
+ private readonly Uri uri;
+ private readonly X509Certificate[] certificates;
+ private Stream inputStream;
+ private MemoryStream outputStream = new MemoryStream();
+
+ // Timeouts in milliseconds
+ private int connectTimeout = 30000;
+
+ private int readTimeout = 30000;
+
+ private IDictionary<string, string> customHeaders = new Dictionary<string, string>();
+ private string userAgent = "C#/THttpClient";
+
+#if !SILVERLIGHT
+ private IWebProxy proxy = WebRequest.DefaultWebProxy;
+#endif
+
+ public THttpClient(Uri u)
+ : this(u, Enumerable.Empty<X509Certificate>())
+ {
+ }
+ public THttpClient(Uri u, string userAgent)
+ : this(u, userAgent, Enumerable.Empty<X509Certificate>())
+ {
+ }
+
+ public THttpClient(Uri u, IEnumerable<X509Certificate> certificates)
+ {
+ uri = u;
+ this.certificates = (certificates ?? Enumerable.Empty<X509Certificate>()).ToArray();
+ }
+ public THttpClient(Uri u, string userAgent, IEnumerable<X509Certificate> certificates)
+ {
+ uri = u;
+ this.userAgent = userAgent;
+ this.certificates = (certificates ?? Enumerable.Empty<X509Certificate>()).ToArray();
+ }
+
+ public int ConnectTimeout
+ {
+ set
+ {
+ connectTimeout = value;
+ }
+ }
+
+ public int ReadTimeout
+ {
+ set
+ {
+ readTimeout = value;
+ }
+ }
+
+ public IDictionary<string, string> CustomHeaders
+ {
+ get
+ {
+ return customHeaders;
+ }
+ }
+
+#if !SILVERLIGHT
+ public IWebProxy Proxy
+ {
+ set
+ {
+ proxy = value;
+ }
+ }
+#endif
+
+ public override bool IsOpen
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ public override void Open()
+ {
+ }
+
+ public override void Close()
+ {
+ if (inputStream != null)
+ {
+ inputStream.Close();
+ inputStream = null;
+ }
+ if (outputStream != null)
+ {
+ outputStream.Close();
+ outputStream = null;
+ }
+ }
+
+ public override int Read(byte[] buf, int off, int len)
+ {
+ if (inputStream == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "No request has been sent");
+ }
+
+ try
+ {
+ int ret = inputStream.Read(buf, off, len);
+
+ if (ret == -1)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.EndOfFile, "No more data available");
+ }
+
+ return ret;
+ }
+ catch (IOException iox)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.Unknown, iox.ToString(), iox);
+ }
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ outputStream.Write(buf, off, len);
+ }
+
+#if !SILVERLIGHT
+ public override void Flush()
+ {
+ try
+ {
+ SendRequest();
+ }
+ finally
+ {
+ outputStream = new MemoryStream();
+ }
+ }
+
+ private void SendRequest()
+ {
+ try
+ {
+ HttpWebRequest connection = CreateRequest();
+ connection.Headers.Add("Accept-Encoding", "gzip, deflate");
+
+ byte[] data = outputStream.ToArray();
+ connection.ContentLength = data.Length;
+
+ using (Stream requestStream = connection.GetRequestStream())
+ {
+ requestStream.Write(data, 0, data.Length);
+
+ // Resolve HTTP hang that can happens after successive calls by making sure
+ // that we release the response and response stream. To support this, we copy
+ // the response to a memory stream.
+ using (var response = connection.GetResponse())
+ {
+ using (var responseStream = response.GetResponseStream())
+ {
+ // Copy the response to a memory stream so that we can
+ // cleanly close the response and response stream.
+ inputStream = new MemoryStream();
+ byte[] buffer = new byte[8192]; // multiple of 4096
+ int bytesRead;
+ while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
+ {
+ inputStream.Write(buffer, 0, bytesRead);
+ }
+ inputStream.Seek(0, 0);
+ }
+
+ var encodings = response.Headers.GetValues("Content-Encoding");
+ if (encodings != null)
+ {
+ foreach (var encoding in encodings)
+ {
+ switch (encoding)
+ {
+ case "gzip":
+ DecompressGZipped(ref inputStream);
+ break;
+ case "deflate":
+ DecompressDeflated(ref inputStream);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (IOException iox)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.Unknown, iox.ToString(), iox);
+ }
+ catch (WebException wx)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.Unknown, "Couldn't connect to server: " + wx, wx);
+ }
+ }
+
+ private void DecompressDeflated(ref Stream inputStream)
+ {
+ var tmp = new MemoryStream();
+ using (var decomp = new DeflateStream(inputStream, CompressionMode.Decompress))
+ {
+ decomp.CopyTo(tmp);
+ }
+ inputStream.Dispose();
+ inputStream = tmp;
+ inputStream.Seek(0, 0);
+ }
+
+ private void DecompressGZipped(ref Stream inputStream)
+ {
+ var tmp = new MemoryStream();
+ using (var decomp = new GZipStream(inputStream, CompressionMode.Decompress))
+ {
+ decomp.CopyTo(tmp);
+ }
+ inputStream.Dispose();
+ inputStream = tmp;
+ inputStream.Seek(0, 0);
+ }
+#endif
+ private HttpWebRequest CreateRequest()
+ {
+ HttpWebRequest connection = (HttpWebRequest)WebRequest.Create(uri);
+
+
+#if !SILVERLIGHT
+ // Adding certificates through code is not supported with WP7 Silverlight
+ // see "Windows Phone 7 and Certificates_FINAL_121610.pdf"
+ connection.ClientCertificates.AddRange(certificates);
+
+ if (connectTimeout > 0)
+ {
+ connection.Timeout = connectTimeout;
+ }
+ if (readTimeout > 0)
+ {
+ connection.ReadWriteTimeout = readTimeout;
+ }
+#endif
+ // Make the request
+ connection.ContentType = "application/x-thrift";
+ connection.Accept = "application/x-thrift";
+ connection.UserAgent = userAgent;
+ connection.Method = "POST";
+#if !SILVERLIGHT
+ connection.ProtocolVersion = HttpVersion.Version10;
+#endif
+
+ //add custom headers here
+ foreach (KeyValuePair<string, string> item in customHeaders)
+ {
+#if !SILVERLIGHT
+ connection.Headers.Add(item.Key, item.Value);
+#else
+ connection.Headers[item.Key] = item.Value;
+#endif
+ }
+
+#if !SILVERLIGHT
+ connection.Proxy = proxy;
+#endif
+
+ return connection;
+ }
+
+ public override IAsyncResult BeginFlush(AsyncCallback callback, object state)
+ {
+ // Extract request and reset buffer
+ var data = outputStream.ToArray();
+
+ //requestBuffer_ = new MemoryStream();
+
+ try
+ {
+ // Create connection object
+ var flushAsyncResult = new FlushAsyncResult(callback, state);
+ flushAsyncResult.Connection = CreateRequest();
+
+ flushAsyncResult.Data = data;
+
+
+ flushAsyncResult.Connection.BeginGetRequestStream(GetRequestStreamCallback, flushAsyncResult);
+ return flushAsyncResult;
+
+ }
+ catch (IOException iox)
+ {
+ throw new TTransportException(iox.ToString(), iox);
+ }
+ }
+
+ public override void EndFlush(IAsyncResult asyncResult)
+ {
+ try
+ {
+ var flushAsyncResult = (FlushAsyncResult)asyncResult;
+
+ if (!flushAsyncResult.IsCompleted)
+ {
+ var waitHandle = flushAsyncResult.AsyncWaitHandle;
+ waitHandle.WaitOne(); // blocking INFINITEly
+ waitHandle.Close();
+ }
+
+ if (flushAsyncResult.AsyncException != null)
+ {
+ throw flushAsyncResult.AsyncException;
+ }
+ }
+ finally
+ {
+ outputStream = new MemoryStream();
+ }
+
+ }
+
+ private void GetRequestStreamCallback(IAsyncResult asynchronousResult)
+ {
+ var flushAsyncResult = (FlushAsyncResult)asynchronousResult.AsyncState;
+ try
+ {
+ var reqStream = flushAsyncResult.Connection.EndGetRequestStream(asynchronousResult);
+ reqStream.Write(flushAsyncResult.Data, 0, flushAsyncResult.Data.Length);
+ reqStream.Flush();
+ reqStream.Close();
+
+ // Start the asynchronous operation to get the response
+ flushAsyncResult.Connection.BeginGetResponse(GetResponseCallback, flushAsyncResult);
+ }
+ catch (Exception exception)
+ {
+ flushAsyncResult.AsyncException = new TTransportException(exception.ToString(), exception);
+ flushAsyncResult.UpdateStatusToComplete();
+ flushAsyncResult.NotifyCallbackWhenAvailable();
+ }
+ }
+
+ private void GetResponseCallback(IAsyncResult asynchronousResult)
+ {
+ var flushAsyncResult = (FlushAsyncResult)asynchronousResult.AsyncState;
+ try
+ {
+ inputStream = flushAsyncResult.Connection.EndGetResponse(asynchronousResult).GetResponseStream();
+ }
+ catch (Exception exception)
+ {
+ flushAsyncResult.AsyncException = new TTransportException(exception.ToString(), exception);
+ }
+ flushAsyncResult.UpdateStatusToComplete();
+ flushAsyncResult.NotifyCallbackWhenAvailable();
+ }
+
+ // Based on http://msmvps.com/blogs/luisabreu/archive/2009/06/15/multithreading-implementing-the-iasyncresult-interface.aspx
+ class FlushAsyncResult : IAsyncResult
+ {
+ private volatile Boolean _isCompleted;
+ private ManualResetEvent _evt;
+ private readonly AsyncCallback _cbMethod;
+ private readonly object _state;
+
+ public FlushAsyncResult(AsyncCallback cbMethod, object state)
+ {
+ _cbMethod = cbMethod;
+ _state = state;
+ }
+
+ internal byte[] Data { get; set; }
+ internal HttpWebRequest Connection { get; set; }
+ internal TTransportException AsyncException { get; set; }
+
+ public object AsyncState
+ {
+ get { return _state; }
+ }
+ public WaitHandle AsyncWaitHandle
+ {
+ get { return GetEvtHandle(); }
+ }
+ public bool CompletedSynchronously
+ {
+ get { return false; }
+ }
+ public bool IsCompleted
+ {
+ get { return _isCompleted; }
+ }
+ private readonly object _locker = new object();
+ private ManualResetEvent GetEvtHandle()
+ {
+ lock (_locker)
+ {
+ if (_evt == null)
+ {
+ _evt = new ManualResetEvent(false);
+ }
+ if (_isCompleted)
+ {
+ _evt.Set();
+ }
+ }
+ return _evt;
+ }
+ internal void UpdateStatusToComplete()
+ {
+ _isCompleted = true; //1. set _iscompleted to true
+ lock (_locker)
+ {
+ if (_evt != null)
+ {
+ _evt.Set(); //2. set the event, when it exists
+ }
+ }
+ }
+
+ internal void NotifyCallbackWhenAvailable()
+ {
+ if (_cbMethod != null)
+ {
+ _cbMethod(this);
+ }
+ }
+ }
+
+ #region " IDisposable Support "
+ private bool _IsDisposed;
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!_IsDisposed)
+ {
+ if (disposing)
+ {
+ if (inputStream != null)
+ inputStream.Dispose();
+ if (outputStream != null)
+ outputStream.Dispose();
+ }
+ }
+ _IsDisposed = true;
+ }
+ #endregion
+ }
+}
diff --git a/lib/csharp/src/Transport/THttpHandler.cs b/lib/csharp/src/Transport/THttpHandler.cs
new file mode 100644
index 0000000..4115ef9
--- /dev/null
+++ b/lib/csharp/src/Transport/THttpHandler.cs
@@ -0,0 +1,102 @@
+/**
+ * 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.Web;
+using System.Net;
+using System.IO;
+
+using Thrift.Protocol;
+
+namespace Thrift.Transport
+{
+ public class THttpHandler : IHttpHandler
+ {
+ protected TProcessor processor;
+
+ protected TProtocolFactory inputProtocolFactory;
+ protected TProtocolFactory outputProtocolFactory;
+
+ protected const string contentType = "application/x-thrift";
+ protected System.Text.Encoding encoding = System.Text.Encoding.UTF8;
+
+ public THttpHandler(TProcessor processor)
+ : this(processor, new TBinaryProtocol.Factory())
+ {
+
+ }
+
+ public THttpHandler(TProcessor processor, TProtocolFactory protocolFactory)
+ : this(processor, protocolFactory, protocolFactory)
+ {
+
+ }
+
+ public THttpHandler(TProcessor processor, TProtocolFactory inputProtocolFactory, TProtocolFactory outputProtocolFactory)
+ {
+ this.processor = processor;
+ this.inputProtocolFactory = inputProtocolFactory;
+ this.outputProtocolFactory = outputProtocolFactory;
+ }
+
+ public void ProcessRequest(HttpListenerContext context)
+ {
+ context.Response.ContentType = contentType;
+ context.Response.ContentEncoding = encoding;
+ ProcessRequest(context.Request.InputStream, context.Response.OutputStream);
+ }
+
+ public void ProcessRequest(HttpContext context)
+ {
+ context.Response.ContentType = contentType;
+ context.Response.ContentEncoding = encoding;
+ ProcessRequest(context.Request.InputStream, context.Response.OutputStream);
+ }
+
+ public void ProcessRequest(Stream input, Stream output)
+ {
+ TTransport transport = new TStreamTransport(input,output);
+
+ try
+ {
+ var inputProtocol = inputProtocolFactory.GetProtocol(transport);
+ var outputProtocol = outputProtocolFactory.GetProtocol(transport);
+
+ while (processor.Process(inputProtocol, outputProtocol))
+ {
+ }
+ }
+ catch (TTransportException)
+ {
+ // Client died, just move on
+ }
+ finally
+ {
+ transport.Close();
+ }
+ }
+
+ public bool IsReusable
+ {
+ get { return true; }
+ }
+ }
+}
diff --git a/lib/csharp/src/Transport/THttpTaskAsyncHandler.cs b/lib/csharp/src/Transport/THttpTaskAsyncHandler.cs
new file mode 100644
index 0000000..e491f32
--- /dev/null
+++ b/lib/csharp/src/Transport/THttpTaskAsyncHandler.cs
@@ -0,0 +1,97 @@
+/**
+ * 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.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System.Threading.Tasks;
+using System.Web;
+using Thrift.Protocol;
+
+namespace Thrift.Transport
+{
+ /// <summary>
+ /// An async task based HTTP handler for processing thrift services.
+ /// </summary>
+ public class THttpTaskAsyncHandler : HttpTaskAsyncHandler
+ {
+ private readonly TAsyncProcessor _processor;
+ private readonly TProtocolFactory _inputProtocolFactory;
+ private readonly TProtocolFactory _outputProtocolFactory;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="THttpTaskAsyncHandler"/> class
+ /// using the <see cref="TBinaryProtocol.Factory"/> for both input and output streams.
+ /// </summary>
+ /// <param name="processor">The async processor implementation.</param>
+ public THttpTaskAsyncHandler(TAsyncProcessor processor)
+ : this(processor, new TBinaryProtocol.Factory())
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="THttpTaskAsyncHandler"/> class
+ /// using <paramref name="protocolFactory"/> for both input and output streams.
+ /// </summary>
+ /// <param name="processor">The async processor implementation.</param>
+ /// <param name="protocolFactory">The protocol factory.</param>
+ public THttpTaskAsyncHandler(TAsyncProcessor processor, TProtocolFactory protocolFactory)
+ : this(processor, protocolFactory, protocolFactory)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="THttpTaskAsyncHandler"/> class.
+ /// </summary>
+ /// <param name="processor">The async processor implementation.</param>
+ /// <param name="inputProtocolFactory">The input protocol factory.</param>
+ /// <param name="outputProtocolFactory">The output protocol factory.</param>
+ public THttpTaskAsyncHandler(TAsyncProcessor processor, TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory)
+ {
+ _processor = processor;
+ _inputProtocolFactory = inputProtocolFactory;
+ _outputProtocolFactory = outputProtocolFactory;
+ }
+
+ public override async Task ProcessRequestAsync(HttpContext context)
+ {
+ var transport = new TStreamTransport(context.Request.InputStream, context.Response.OutputStream);
+
+ try
+ {
+ var input = _inputProtocolFactory.GetProtocol(transport);
+ var output = _outputProtocolFactory.GetProtocol(transport);
+
+ while (await _processor.ProcessAsync(input, output))
+ {
+ }
+ }
+ catch (TTransportException)
+ {
+ // Client died, just move on
+ }
+ finally
+ {
+ transport.Close();
+ }
+ }
+ }
+}
diff --git a/lib/csharp/src/Transport/TMemoryBuffer.cs b/lib/csharp/src/Transport/TMemoryBuffer.cs
new file mode 100644
index 0000000..303d083
--- /dev/null
+++ b/lib/csharp/src/Transport/TMemoryBuffer.cs
@@ -0,0 +1,117 @@
+/**
+ * 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.IO;
+using System.Reflection;
+using Thrift.Protocol;
+
+namespace Thrift.Transport
+{
+ public class TMemoryBuffer : TTransport
+ {
+
+ private readonly MemoryStream byteStream;
+
+ public TMemoryBuffer()
+ {
+ byteStream = new MemoryStream();
+ }
+
+ public TMemoryBuffer(byte[] buf)
+ {
+ byteStream = new MemoryStream(buf);
+ }
+
+ public override void Open()
+ {
+ /** do nothing **/
+ }
+
+ public override void Close()
+ {
+ /** do nothing **/
+ }
+
+ public override int Read(byte[] buf, int off, int len)
+ {
+ return byteStream.Read(buf, off, len);
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ byteStream.Write(buf, off, len);
+ }
+
+ public byte[] GetBuffer()
+ {
+ return byteStream.ToArray();
+ }
+
+
+ public override bool IsOpen
+ {
+ get { return true; }
+ }
+
+ public static byte[] Serialize(TAbstractBase s)
+ {
+ var t = new TMemoryBuffer();
+ var p = new TBinaryProtocol(t);
+
+ s.Write(p);
+
+ return t.GetBuffer();
+ }
+
+ public static T DeSerialize<T>(byte[] buf) where T : TAbstractBase
+ {
+ var trans = new TMemoryBuffer(buf);
+ var p = new TBinaryProtocol(trans);
+ if (typeof(TBase).IsAssignableFrom(typeof(T)))
+ {
+ var method = typeof(T).GetMethod("Read", BindingFlags.Instance | BindingFlags.Public);
+ var t = Activator.CreateInstance<T>();
+ method.Invoke(t, new object[] { p });
+ return t;
+ }
+ else
+ {
+ var method = typeof(T).GetMethod("Read", BindingFlags.Static | BindingFlags.Public);
+ return (T)method.Invoke(null, new object[] { p });
+ }
+ }
+
+ private bool _IsDisposed;
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!_IsDisposed)
+ {
+ if (disposing)
+ {
+ if (byteStream != null)
+ byteStream.Dispose();
+ }
+ }
+ _IsDisposed = true;
+ }
+ }
+}
diff --git a/lib/csharp/src/Transport/TNamedPipeClientTransport.cs b/lib/csharp/src/Transport/TNamedPipeClientTransport.cs
new file mode 100644
index 0000000..49a50aa
--- /dev/null
+++ b/lib/csharp/src/Transport/TNamedPipeClientTransport.cs
@@ -0,0 +1,111 @@
+/**
+ * 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.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.IO.Pipes;
+using System.Threading;
+
+namespace Thrift.Transport
+{
+ public class TNamedPipeClientTransport : TTransport
+ {
+ private NamedPipeClientStream client;
+ private string ServerName;
+ private string PipeName;
+ private int ConnectTimeout;
+
+ public TNamedPipeClientTransport(string pipe, int timeout = Timeout.Infinite)
+ {
+ ServerName = ".";
+ PipeName = pipe;
+ ConnectTimeout = timeout;
+ }
+
+ public TNamedPipeClientTransport(string server, string pipe, int timeout = Timeout.Infinite)
+ {
+ ServerName = (server != "") ? server : ".";
+ PipeName = pipe;
+ ConnectTimeout = timeout;
+ }
+
+ 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(ConnectTimeout);
+ }
+
+ 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);
+ }
+
+ // if necessary, send the data in chunks
+ // there's a system limit around 0x10000 bytes that we hit otherwise
+ // MSDN: "Pipe write operations across a network are limited to 65,535 bytes per write. For more information regarding pipes, see the Remarks section."
+ var nBytes = Math.Min(len, 15 * 4096); // 16 would exceed the limit
+ while (nBytes > 0)
+ {
+ client.Write(buf, off, nBytes);
+
+ off += nBytes;
+ len -= nBytes;
+ nBytes = Math.Min(len, nBytes);
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ client.Dispose();
+ }
+ }
+}
diff --git a/lib/csharp/src/Transport/TNamedPipeServerTransport.cs b/lib/csharp/src/Transport/TNamedPipeServerTransport.cs
new file mode 100644
index 0000000..32215cf
--- /dev/null
+++ b/lib/csharp/src/Transport/TNamedPipeServerTransport.cs
@@ -0,0 +1,296 @@
+/**
+ * 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.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.IO.Pipes;
+using System.Threading;
+using System.Security.Principal;
+
+namespace Thrift.Transport
+{
+ public class TNamedPipeServerTransport : TServerTransport
+ {
+ /// <summary>
+ /// This is the address of the Pipe on the localhost.
+ /// </summary>
+ private readonly string pipeAddress;
+ private NamedPipeServerStream stream = null;
+ private bool asyncMode = true;
+
+ 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)
+ {
+ var direction = PipeDirection.InOut;
+ var maxconn = NamedPipeServerStream.MaxAllowedServerInstances;
+ var mode = PipeTransmissionMode.Byte;
+ var options = asyncMode ? PipeOptions.Asynchronous : PipeOptions.None;
+ const int INBUF_SIZE = 4096;
+ const int OUTBUF_SIZE = 4096;
+
+ // security
+ var security = new PipeSecurity();
+ security.AddAccessRule(
+ new PipeAccessRule(
+ new SecurityIdentifier(WellKnownSidType.WorldSid, null),
+ PipeAccessRights.Read | PipeAccessRights.Write | PipeAccessRights.Synchronize | PipeAccessRights.CreateNewInstance,
+ System.Security.AccessControl.AccessControlType.Allow
+ )
+ );
+
+ try
+ {
+ stream = new NamedPipeServerStream(pipeAddress, direction, maxconn, mode, options, INBUF_SIZE, OUTBUF_SIZE, security);
+ }
+ catch (NotImplementedException) // Mono still does not support async, fallback to sync
+ {
+ if (asyncMode)
+ {
+ options &= (~PipeOptions.Asynchronous);
+ stream = new NamedPipeServerStream(pipeAddress, direction, maxconn, mode, options, INBUF_SIZE, OUTBUF_SIZE, security);
+ asyncMode = false;
+ }
+ else
+ {
+ throw;
+ }
+ }
+
+ }
+ }
+
+ protected override TTransport AcceptImpl()
+ {
+ try
+ {
+ EnsurePipeInstance();
+
+ if (asyncMode)
+ {
+ var evt = new ManualResetEvent(false);
+ Exception eOuter = null;
+
+ stream.BeginWaitForConnection(asyncResult =>
+ {
+ try
+ {
+ if (stream != null)
+ stream.EndWaitForConnection(asyncResult);
+ else
+ eOuter = new TTransportException(TTransportException.ExceptionType.Interrupted);
+ }
+ catch (Exception e)
+ {
+ if (stream != null)
+ eOuter = e;
+ else
+ eOuter = new TTransportException(TTransportException.ExceptionType.Interrupted, e.Message, e);
+ }
+ evt.Set();
+ }, null);
+
+ evt.WaitOne();
+
+ if (eOuter != null)
+ throw eOuter; // rethrow exception
+ }
+ else
+ {
+ stream.WaitForConnection();
+ }
+
+ var trans = new ServerTransport(stream,asyncMode);
+ stream = null; // pass ownership to ServerTransport
+ return trans;
+ }
+ catch (TTransportException)
+ {
+ Close();
+ throw;
+ }
+ catch (Exception e)
+ {
+ Close();
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, e.Message, e);
+ }
+ }
+
+ private class ServerTransport : TTransport
+ {
+ private NamedPipeServerStream stream;
+ private bool asyncMode;
+
+ public ServerTransport(NamedPipeServerStream stream, bool asyncMode)
+ {
+ this.stream = stream;
+ this.asyncMode = asyncMode;
+ }
+
+ public override bool IsOpen
+ {
+ get { return stream != null && stream.IsConnected; }
+ }
+
+ public override void Open()
+ {
+ }
+
+ public override void Close()
+ {
+ if (stream != null)
+ stream.Close();
+ }
+
+ public override int Read(byte[] buf, int off, int len)
+ {
+ if (stream == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ if (asyncMode)
+ {
+ Exception eOuter = null;
+ var evt = new ManualResetEvent(false);
+ int retval = 0;
+
+ stream.BeginRead(buf, off, len, asyncResult =>
+ {
+ try
+ {
+ if (stream != null)
+ retval = stream.EndRead(asyncResult);
+ else
+ eOuter = new TTransportException(TTransportException.ExceptionType.Interrupted);
+ }
+ catch (Exception e)
+ {
+ if (stream != null)
+ eOuter = e;
+ else
+ eOuter = new TTransportException(TTransportException.ExceptionType.Interrupted, e.Message, e);
+ }
+ evt.Set();
+ }, null);
+
+ evt.WaitOne();
+
+ if (eOuter != null)
+ throw eOuter; // rethrow exception
+ else
+ return retval;
+ }
+ else
+ {
+ return stream.Read(buf, off, len);
+ }
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ if (stream == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen);
+ }
+
+ // if necessary, send the data in chunks
+ // there's a system limit around 0x10000 bytes that we hit otherwise
+ // MSDN: "Pipe write operations across a network are limited to 65,535 bytes per write. For more information regarding pipes, see the Remarks section."
+ var nBytes = Math.Min(len, 15 * 4096); // 16 would exceed the limit
+ while (nBytes > 0)
+ {
+
+ if (asyncMode)
+ {
+ Exception eOuter = null;
+ var evt = new ManualResetEvent(false);
+
+ stream.BeginWrite(buf, off, nBytes, asyncResult =>
+ {
+ try
+ {
+ if (stream != null)
+ stream.EndWrite(asyncResult);
+ else
+ eOuter = new TTransportException(TTransportException.ExceptionType.Interrupted);
+ }
+ catch (Exception e)
+ {
+ if (stream != null)
+ eOuter = e;
+ else
+ eOuter = new TTransportException(TTransportException.ExceptionType.Interrupted, e.Message, e);
+ }
+ evt.Set();
+ }, null);
+
+ evt.WaitOne();
+
+ if (eOuter != null)
+ throw eOuter; // rethrow exception
+ }
+ else
+ {
+ stream.Write(buf, off, nBytes);
+ }
+
+ off += nBytes;
+ len -= nBytes;
+ nBytes = Math.Min(len, nBytes);
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (stream != null)
+ stream.Dispose();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/csharp/src/Transport/TServerSocket.cs b/lib/csharp/src/Transport/TServerSocket.cs
new file mode 100644
index 0000000..d8ec62a
--- /dev/null
+++ b/lib/csharp/src/Transport/TServerSocket.cs
@@ -0,0 +1,176 @@
+/**
+ * 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.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.Net.Sockets;
+
+
+namespace Thrift.Transport
+{
+ public class TServerSocket : TServerTransport
+ {
+ /// <summary>
+ /// Underlying server with socket.
+ /// </summary>
+ private TcpListener server = null;
+
+ /// <summary>
+ /// Port to listen on.
+ /// </summary>
+ private int port = 0;
+
+ /// <summary>
+ /// Timeout for client sockets from accept.
+ /// </summary>
+ private int clientTimeout = 0;
+
+ /// <summary>
+ /// Whether or not to wrap new TSocket connections in buffers.
+ /// </summary>
+ private bool useBufferedSockets = false;
+
+ /// <summary>
+ /// Creates a server socket from underlying socket object.
+ /// </summary>
+ public TServerSocket(TcpListener listener)
+ : this(listener, 0)
+ {
+ }
+
+ /// <summary>
+ /// Creates a server socket from underlying socket object.
+ /// </summary>
+ public TServerSocket(TcpListener listener, int clientTimeout)
+ {
+ this.server = listener;
+ this.clientTimeout = clientTimeout;
+ }
+
+ /// <summary>
+ /// Creates just a port listening server socket.
+ /// </summary>
+ public TServerSocket(int port)
+ : this(port, 0)
+ {
+ }
+
+ /// <summary>
+ /// Creates just a port listening server socket.
+ /// </summary>
+ public TServerSocket(int port, int clientTimeout)
+ : this(port, clientTimeout, false)
+ {
+ }
+
+ public TServerSocket(int port, int clientTimeout, bool useBufferedSockets)
+ {
+ this.port = port;
+ this.clientTimeout = clientTimeout;
+ this.useBufferedSockets = useBufferedSockets;
+ try
+ {
+ // Make server socket
+ this.server = TSocketVersionizer.CreateTcpListener(this.port);
+ this.server.Server.NoDelay = true;
+ }
+ catch (Exception ex)
+ {
+ server = null;
+ throw new TTransportException("Could not create ServerSocket on port " + this.port + ".", ex);
+ }
+ }
+
+ public override void Listen()
+ {
+ // Make sure not to block on accept
+ if (server != null)
+ {
+ try
+ {
+ server.Start();
+ }
+ catch (SocketException sx)
+ {
+ throw new TTransportException("Could not accept on listening socket: " + sx.Message, sx);
+ }
+ }
+ }
+
+ protected override TTransport AcceptImpl()
+ {
+ if (server == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "No underlying server socket.");
+ }
+ try
+ {
+ TSocket result2 = null;
+ TcpClient result = server.AcceptTcpClient();
+ try
+ {
+ result2 = new TSocket(result);
+ result2.Timeout = clientTimeout;
+ if (useBufferedSockets)
+ {
+ TBufferedTransport result3 = new TBufferedTransport(result2);
+ return result3;
+ }
+ else
+ {
+ return result2;
+ }
+ }
+ catch (System.Exception)
+ {
+ // If a TSocket was successfully created, then let
+ // it do proper cleanup of the TcpClient object.
+ if (result2 != null)
+ result2.Dispose();
+ else // Otherwise, clean it up ourselves.
+ ((IDisposable)result).Dispose();
+ throw;
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new TTransportException(ex.ToString(), ex);
+ }
+ }
+
+ public override void Close()
+ {
+ if (server != null)
+ {
+ try
+ {
+ server.Stop();
+ }
+ catch (Exception ex)
+ {
+ throw new TTransportException("WARNING: Could not close server socket: " + ex, ex);
+ }
+ server = null;
+ }
+ }
+ }
+}
diff --git a/lib/csharp/src/Transport/TServerTransport.cs b/lib/csharp/src/Transport/TServerTransport.cs
new file mode 100644
index 0000000..e63880b
--- /dev/null
+++ b/lib/csharp/src/Transport/TServerTransport.cs
@@ -0,0 +1,44 @@
+/**
+ * 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.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+
+namespace Thrift.Transport
+{
+ public abstract class TServerTransport
+ {
+ public abstract void Listen();
+ public abstract void Close();
+ protected abstract TTransport AcceptImpl();
+
+ public TTransport Accept()
+ {
+ TTransport transport = AcceptImpl();
+ if (transport == null)
+ {
+ throw new TTransportException("accept() may not return NULL");
+ }
+ return transport;
+ }
+ }
+}
diff --git a/lib/csharp/src/Transport/TSilverlightSocket.cs b/lib/csharp/src/Transport/TSilverlightSocket.cs
new file mode 100644
index 0000000..40469ab
--- /dev/null
+++ b/lib/csharp/src/Transport/TSilverlightSocket.cs
@@ -0,0 +1,393 @@
+/**
+ * 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.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+/* only for silverlight */
+#if SILVERLIGHT
+
+using System;
+using System.Net.Sockets;
+using System.IO;
+using System.Net;
+using System.Threading;
+
+namespace Thrift.Transport
+{
+ public class TSilverlightSocket : TTransport
+ {
+ Socket socket = null;
+ static ManualResetEvent readAsyncComplete = new ManualResetEvent(false);
+ public event EventHandler<SocketAsyncEventArgs> connectHandler = null;
+
+ // memory stream for write cache.
+ private MemoryStream outputStream = new MemoryStream();
+
+ private string host = null;
+ private int port = 0;
+ private int timeout = 0;
+
+ // constructor
+ public TSilverlightSocket(string host, int port)
+ : this(host, port, 0)
+ {
+ }
+
+ // constructor
+ public TSilverlightSocket(string host, int port, int timeout)
+ {
+ this.host = host;
+ this.port = port;
+ this.timeout = timeout;
+
+ InitSocket();
+ }
+
+ private void InitSocket()
+ {
+ // Create a stream-based, TCP socket using the InterNetwork Address Family.
+ socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+ socket.NoDelay = true;
+ }
+
+ public int Timeout
+ {
+ set
+ {
+ timeout = value;
+ }
+ }
+
+ public string Host
+ {
+ get
+ {
+ return host;
+ }
+ }
+
+ public int Port
+ {
+ get
+ {
+ return port;
+ }
+ }
+
+ public override bool IsOpen
+ {
+ get
+ {
+ if (socket == null)
+ {
+ return false;
+ }
+
+ return socket.Connected;
+ }
+ }
+
+ 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 (socket == null)
+ {
+ InitSocket();
+ }
+
+ if (timeout == 0) // no timeout -> infinite
+ {
+ timeout = 10000; // set a default timeout for WP.
+ }
+
+ {
+ // Create DnsEndPoint. The hostName and port are passed in to this method.
+ DnsEndPoint hostEntry = new DnsEndPoint(this.host, this.port);
+
+ // Create a SocketAsyncEventArgs object to be used in the connection request
+ SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
+ socketEventArg.RemoteEndPoint = hostEntry;
+
+ // Inline event handler for the Completed event.
+ // Note: This event handler was implemented inline in order to make this method self-contained.
+ socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
+ {
+ if (connectHandler != null)
+ {
+ connectHandler(this, e);
+ }
+ });
+
+ // Make an asynchronous Connect request over the socket
+ socket.ConnectAsync(socketEventArg);
+ }
+ }
+
+ public override int Read(byte[] buf, int off, int len)
+ {
+ bool _timeout = true;
+ string _error = null;
+ int _recvBytes = -1;
+
+ if (socket == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Socket is not open");
+ }
+
+ // Create SocketAsyncEventArgs context object
+ SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
+ socketEventArg.RemoteEndPoint = socket.RemoteEndPoint;
+
+ // Setup the buffer to receive the data
+ socketEventArg.SetBuffer(buf, off, len);
+
+ // Inline event handler for the Completed event.
+ // Note: This even handler was implemented inline in order to make
+ // this method self-contained.
+ socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
+ {
+ _timeout = false;
+
+ if (e.SocketError == SocketError.Success)
+ {
+ _recvBytes = e.BytesTransferred;
+ }
+ else
+ {
+ _error = e.SocketError.ToString();
+ }
+
+ readAsyncComplete.Set();
+ });
+
+ // Sets the state of the event to nonsignaled, causing threads to block
+ readAsyncComplete.Reset();
+
+ // Make an asynchronous Receive request over the socket
+ socket.ReceiveAsync(socketEventArg);
+
+ // Block the UI thread for a maximum of TIMEOUT_MILLISECONDS milliseconds.
+ // If no response comes back within this time then proceed
+ readAsyncComplete.WaitOne(this.timeout);
+
+ if (_timeout)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.TimedOut, "Socket recv timeout");
+ }
+
+ if (_error != null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.Unknown, _error);
+ }
+
+ return _recvBytes;
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ outputStream.Write(buf, off, len);
+ }
+
+ private void beginFlush_Completed(object sender, SocketAsyncEventArgs e)
+ {
+ FlushAsyncResult flushAsyncResult = e.UserToken as FlushAsyncResult;
+ flushAsyncResult.UpdateStatusToComplete();
+ flushAsyncResult.NotifyCallbackWhenAvailable();
+
+ if (e.SocketError != SocketError.Success)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.Unknown, e.SocketError.ToString());
+ }
+ }
+
+ public override IAsyncResult BeginFlush(AsyncCallback callback, object state)
+ {
+ // Extract request and reset buffer
+ byte[] data = outputStream.ToArray();
+
+ FlushAsyncResult flushAsyncResult = new FlushAsyncResult(callback, state);
+
+ SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
+ socketEventArg.RemoteEndPoint = socket.RemoteEndPoint;
+ socketEventArg.UserToken = flushAsyncResult;
+
+ socketEventArg.Completed += beginFlush_Completed;
+ socketEventArg.SetBuffer(data, 0, data.Length);
+
+ socket.SendAsync(socketEventArg);
+
+ return flushAsyncResult;
+ }
+
+ public override void EndFlush(IAsyncResult asyncResult)
+ {
+ try
+ {
+ var flushAsyncResult = (FlushAsyncResult)asyncResult;
+
+ if (!flushAsyncResult.IsCompleted)
+ {
+ var waitHandle = flushAsyncResult.AsyncWaitHandle;
+ waitHandle.WaitOne();
+ waitHandle.Close();
+ }
+
+ if (flushAsyncResult.AsyncException != null)
+ {
+ throw flushAsyncResult.AsyncException;
+ }
+ }
+ finally
+ {
+ outputStream = new MemoryStream();
+ }
+ }
+
+ // Copy from impl from THttpClient.cs
+ // Based on http://msmvps.com/blogs/luisabreu/archive/2009/06/15/multithreading-implementing-the-iasyncresult-interface.aspx
+ class FlushAsyncResult : IAsyncResult
+ {
+ private volatile Boolean _isCompleted;
+ private ManualResetEvent _evt;
+ private readonly AsyncCallback _cbMethod;
+ private readonly object _state;
+
+ public FlushAsyncResult(AsyncCallback cbMethod, object state)
+ {
+ _cbMethod = cbMethod;
+ _state = state;
+ }
+
+ internal byte[] Data { get; set; }
+ internal Socket Connection { get; set; }
+ internal TTransportException AsyncException { get; set; }
+
+ public object AsyncState
+ {
+ get { return _state; }
+ }
+
+ public WaitHandle AsyncWaitHandle
+ {
+ get { return GetEvtHandle(); }
+ }
+
+ public bool CompletedSynchronously
+ {
+ get { return false; }
+ }
+
+ public bool IsCompleted
+ {
+ get { return _isCompleted; }
+ }
+
+ private readonly object _locker = new object();
+
+ private ManualResetEvent GetEvtHandle()
+ {
+ lock (_locker)
+ {
+ if (_evt == null)
+ {
+ _evt = new ManualResetEvent(false);
+ }
+ if (_isCompleted)
+ {
+ _evt.Set();
+ }
+ }
+ return _evt;
+ }
+
+ internal void UpdateStatusToComplete()
+ {
+ _isCompleted = true; //1. set _iscompleted to true
+ lock (_locker)
+ {
+ if (_evt != null)
+ {
+ _evt.Set(); //2. set the event, when it exists
+ }
+ }
+ }
+
+ internal void NotifyCallbackWhenAvailable()
+ {
+ if (_cbMethod != null)
+ {
+ _cbMethod(this);
+ }
+ }
+ }
+
+ public override void Close()
+ {
+ if (socket != null)
+ {
+ socket.Close();
+ socket = null;
+ }
+ }
+
+#region " IDisposable Support "
+ private bool _IsDisposed;
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!_IsDisposed)
+ {
+ if (disposing)
+ {
+ if (outputStream != null)
+ {
+ outputStream.Dispose();
+ }
+ outputStream = null;
+ if (socket != null)
+ {
+ ((IDisposable)socket).Dispose();
+ }
+ }
+ }
+ _IsDisposed = true;
+ }
+#endregion
+ }
+}
+
+
+#endif
diff --git a/lib/csharp/src/Transport/TSocket.cs b/lib/csharp/src/Transport/TSocket.cs
new file mode 100644
index 0000000..d8fa335
--- /dev/null
+++ b/lib/csharp/src/Transport/TSocket.cs
@@ -0,0 +1,245 @@
+/**
+ * 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.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.Net.Sockets;
+
+namespace Thrift.Transport
+{
+ public class TSocket : TStreamTransport
+ {
+ private TcpClient client = null;
+ private string host = null;
+ private int port = 0;
+ private int timeout = 0;
+
+ public TSocket(TcpClient client)
+ {
+ this.client = client;
+
+ if (IsOpen)
+ {
+ inputStream = client.GetStream();
+ outputStream = client.GetStream();
+ }
+ }
+
+ public TSocket(string host, int port)
+ : this(host, port, 0)
+ {
+ }
+
+ public TSocket(string host, int port, int timeout)
+ {
+ this.host = host;
+ this.port = port;
+ this.timeout = timeout;
+
+ InitSocket();
+ }
+
+ private void InitSocket()
+ {
+ this.client = TSocketVersionizer.CreateTcpClient();
+ this.client.ReceiveTimeout = client.SendTimeout = timeout;
+ this.client.Client.NoDelay = true;
+ }
+
+ public int Timeout
+ {
+ set
+ {
+ client.ReceiveTimeout = client.SendTimeout = timeout = value;
+ }
+ }
+
+ public TcpClient TcpClient
+ {
+ get
+ {
+ return client;
+ }
+ }
+
+ public string Host
+ {
+ get
+ {
+ return host;
+ }
+ }
+
+ public int Port
+ {
+ get
+ {
+ return port;
+ }
+ }
+
+ public override bool IsOpen
+ {
+ get
+ {
+ if (client == null)
+ {
+ return false;
+ }
+
+ return client.Connected;
+ }
+ }
+
+ 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();
+ }
+
+ if (timeout == 0) // no timeout -> infinite
+ {
+ client.Connect(host, port);
+ }
+ else // we have a timeout -> use it
+ {
+ ConnectHelper hlp = new ConnectHelper(client);
+ IAsyncResult asyncres = client.BeginConnect(host, port, new AsyncCallback(ConnectCallback), hlp);
+ bool bConnected = asyncres.AsyncWaitHandle.WaitOne(timeout) && client.Connected;
+ if (!bConnected)
+ {
+ lock (hlp.Mutex)
+ {
+ if (hlp.CallbackDone)
+ {
+ asyncres.AsyncWaitHandle.Close();
+ client.Close();
+ }
+ else
+ {
+ hlp.DoCleanup = true;
+ client = null;
+ }
+ }
+ throw new TTransportException(TTransportException.ExceptionType.TimedOut, "Connect timed out");
+ }
+ }
+
+ inputStream = client.GetStream();
+ outputStream = client.GetStream();
+ }
+
+
+ static void ConnectCallback(IAsyncResult asyncres)
+ {
+ ConnectHelper hlp = asyncres.AsyncState as ConnectHelper;
+ lock (hlp.Mutex)
+ {
+ hlp.CallbackDone = true;
+
+ try
+ {
+ if (hlp.Client.Client != null)
+ hlp.Client.EndConnect(asyncres);
+ }
+ catch (Exception)
+ {
+ // catch that away
+ }
+
+ if (hlp.DoCleanup)
+ {
+ try
+ {
+ asyncres.AsyncWaitHandle.Close();
+ }
+ catch (Exception) { }
+
+ try
+ {
+ if (hlp.Client is IDisposable)
+ ((IDisposable)hlp.Client).Dispose();
+ }
+ catch (Exception) { }
+ hlp.Client = null;
+ }
+ }
+ }
+
+ private class ConnectHelper
+ {
+ public object Mutex = new object();
+ public bool DoCleanup = false;
+ public bool CallbackDone = false;
+ public TcpClient Client;
+ public ConnectHelper(TcpClient client)
+ {
+ Client = client;
+ }
+ }
+
+ public override void Close()
+ {
+ base.Close();
+ if (client != null)
+ {
+ client.Close();
+ client = null;
+ }
+ }
+
+ #region " IDisposable Support "
+ private bool _IsDisposed;
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!_IsDisposed)
+ {
+ if (disposing)
+ {
+ if (client != null)
+ ((IDisposable)client).Dispose();
+ base.Dispose(disposing);
+ }
+ }
+ _IsDisposed = true;
+ }
+ #endregion
+ }
+}
diff --git a/lib/csharp/src/Transport/TSocketVersionizer.cs b/lib/csharp/src/Transport/TSocketVersionizer.cs
new file mode 100644
index 0000000..8c2f8e9
--- /dev/null
+++ b/lib/csharp/src/Transport/TSocketVersionizer.cs
@@ -0,0 +1,78 @@
+/**
+ * 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.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Sockets;
+using System.Reflection;
+using System.Text;
+#if NET45
+using System.Threading.Tasks;
+#endif
+
+namespace Thrift.Transport
+{
+ /// <summary>
+ /// PropertyInfo for the DualMode property of the System.Net.Sockets.Socket class. Used to determine if the sockets are capable of
+ /// automatic IPv4 and IPv6 handling. If DualMode is present the sockets automatically handle IPv4 and IPv6 connections.
+ /// If the DualMode is not available the system configuration determines whether IPv4 or IPv6 is used.
+ /// </summary>
+ internal static class TSocketVersionizer
+ {
+ /// <summary>
+ /// Creates a TcpClient according to the capabilities of the used framework.
+ /// </summary>
+ internal static TcpClient CreateTcpClient()
+ {
+ TcpClient client = null;
+
+#if NET45
+ client = new TcpClient(AddressFamily.InterNetworkV6);
+ client.Client.DualMode = true;
+#else
+ client = new TcpClient(AddressFamily.InterNetwork);
+#endif
+
+ return client;
+ }
+
+ /// <summary>
+ /// Creates a TcpListener according to the capabilities of the used framework.
+ /// </summary>
+ internal static TcpListener CreateTcpListener(Int32 port)
+ {
+ TcpListener listener = null;
+
+#if NET45
+ listener = new TcpListener(System.Net.IPAddress.IPv6Any, port);
+ listener.Server.DualMode = true;
+#else
+
+ listener = new TcpListener(System.Net.IPAddress.Any, port);
+#endif
+
+ return listener;
+ }
+ }
+}
diff --git a/lib/csharp/src/Transport/TStreamTransport.cs b/lib/csharp/src/Transport/TStreamTransport.cs
new file mode 100644
index 0000000..304599f
--- /dev/null
+++ b/lib/csharp/src/Transport/TStreamTransport.cs
@@ -0,0 +1,128 @@
+/**
+ * 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.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.IO;
+
+namespace Thrift.Transport
+{
+ public class TStreamTransport : TTransport
+ {
+ protected Stream inputStream;
+ protected Stream outputStream;
+
+ protected TStreamTransport()
+ {
+ }
+
+ public TStreamTransport(Stream inputStream, Stream outputStream)
+ {
+ this.inputStream = inputStream;
+ this.outputStream = outputStream;
+ }
+
+ public Stream OutputStream
+ {
+ get { return outputStream; }
+ }
+
+ public Stream InputStream
+ {
+ get { return inputStream; }
+ }
+
+ public override bool IsOpen
+ {
+ get { return true; }
+ }
+
+ public override void Open()
+ {
+ }
+
+ public override void Close()
+ {
+ if (inputStream != null)
+ {
+ inputStream.Close();
+ inputStream = null;
+ }
+ if (outputStream != null)
+ {
+ outputStream.Close();
+ outputStream = null;
+ }
+ }
+
+ public override int Read(byte[] buf, int off, int len)
+ {
+ if (inputStream == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot read from null inputstream");
+ }
+
+ return inputStream.Read(buf, off, len);
+ }
+
+ public override void Write(byte[] buf, int off, int len)
+ {
+ if (outputStream == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot write to null outputstream");
+ }
+
+ outputStream.Write(buf, off, len);
+ }
+
+ public override void Flush()
+ {
+ if (outputStream == null)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot flush null outputstream");
+ }
+
+ outputStream.Flush();
+ }
+
+
+ #region " IDisposable Support "
+ private bool _IsDisposed;
+
+ // IDisposable
+ protected override void Dispose(bool disposing)
+ {
+ if (!_IsDisposed)
+ {
+ if (disposing)
+ {
+ if (InputStream != null)
+ InputStream.Dispose();
+ if (OutputStream != null)
+ OutputStream.Dispose();
+ }
+ }
+ _IsDisposed = true;
+ }
+ #endregion
+ }
+}
diff --git a/lib/csharp/src/Transport/TTLSServerSocket.cs b/lib/csharp/src/Transport/TTLSServerSocket.cs
new file mode 100644
index 0000000..716a97c
--- /dev/null
+++ b/lib/csharp/src/Transport/TTLSServerSocket.cs
@@ -0,0 +1,223 @@
+/**
+ * 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 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 readonly int clientTimeout;
+
+ /// <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>
+ /// The function to validate the client certificate.
+ /// </summary>
+ private RemoteCertificateValidationCallback clientCertValidator;
+
+ /// <summary>
+ /// The function to determine which certificate to use.
+ /// </summary>
+ private LocalCertificateSelectionCallback localCertificateSelectionCallback;
+
+ /// <summary>
+ /// The SslProtocols value that represents the protocol used for authentication.
+ /// </summary>
+ private readonly SslProtocols sslProtocols;
+
+ /// <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>
+ /// <param name="clientCertValidator">The certificate validator.</param>
+ /// <param name="localCertificateSelectionCallback">The callback to select which certificate to use.</param>
+ /// <param name="sslProtocols">The SslProtocols value that represents the protocol used for authentication.</param>
+ public TTLSServerSocket(
+ int port,
+ int clientTimeout,
+ bool useBufferedSockets,
+ X509Certificate2 certificate,
+ RemoteCertificateValidationCallback clientCertValidator = null,
+ LocalCertificateSelectionCallback localCertificateSelectionCallback = null,
+ // TODO: Enable Tls11 and Tls12 (TLS 1.1 and 1.2) by default once we start using .NET 4.5+.
+ SslProtocols sslProtocols = SslProtocols.Tls)
+ {
+ if (!certificate.HasPrivateKey)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.Unknown, "Your server-certificate needs to have a private key");
+ }
+
+ this.port = port;
+ this.clientTimeout = clientTimeout;
+ this.serverCertificate = certificate;
+ this.useBufferedSockets = useBufferedSockets;
+ this.clientCertValidator = clientCertValidator;
+ this.localCertificateSelectionCallback = localCertificateSelectionCallback;
+ this.sslProtocols = sslProtocols;
+ try
+ {
+ // Create server socket
+ this.server = TSocketVersionizer.CreateTcpListener(this.port);
+ this.server.Server.NoDelay = true;
+ }
+ catch (Exception ex)
+ {
+ server = null;
+ throw new TTransportException("Could not create ServerSocket on port " + this.port + ".", ex);
+ }
+ }
+
+ /// <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, sx);
+ }
+ }
+ }
+
+ /// <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,
+ this.clientCertValidator,
+ this.localCertificateSelectionCallback,
+ this.sslProtocols);
+
+ socket.setupTLS();
+
+ if (useBufferedSockets)
+ {
+ TBufferedTransport trans = new TBufferedTransport(socket);
+ return trans;
+ }
+ else
+ {
+ return socket;
+ }
+
+ }
+ catch (Exception ex)
+ {
+ throw new TTransportException(ex.ToString(), ex);
+ }
+ }
+
+ /// <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, ex);
+ }
+ this.server = null;
+ }
+ }
+ }
+}
diff --git a/lib/csharp/src/Transport/TTLSSocket.cs b/lib/csharp/src/Transport/TTLSSocket.cs
new file mode 100644
index 0000000..06286dc
--- /dev/null
+++ b/lib/csharp/src/Transport/TTLSSocket.cs
@@ -0,0 +1,445 @@
+/**
+ * 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;
+
+ /// <summary>
+ /// The host
+ /// </summary>
+ private string host;
+
+ /// <summary>
+ /// The port
+ /// </summary>
+ private int port;
+
+ /// <summary>
+ /// The timeout for the connection
+ /// </summary>
+ private int timeout;
+
+ /// <summary>
+ /// Internal SSL Stream for IO
+ /// </summary>
+ private SslStream secureStream;
+
+ /// <summary>
+ /// Defines wheter or not this socket is a server socket<br/>
+ /// This is used for the TLS-authentication
+ /// </summary>
+ private bool isServer;
+
+ /// <summary>
+ /// The certificate
+ /// </summary>
+ private X509Certificate certificate;
+
+ /// <summary>
+ /// User defined certificate validator.
+ /// </summary>
+ private RemoteCertificateValidationCallback certValidator;
+
+ /// <summary>
+ /// The function to determine which certificate to use.
+ /// </summary>
+ private LocalCertificateSelectionCallback localCertificateSelectionCallback;
+
+ /// <summary>
+ /// The SslProtocols value that represents the protocol used for authentication.SSL protocols to be used.
+ /// </summary>
+ private readonly SslProtocols sslProtocols;
+
+ /// <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>
+ /// <param name="certValidator">User defined cert validator.</param>
+ /// <param name="localCertificateSelectionCallback">The callback to select which certificate to use.</param>
+ /// <param name="sslProtocols">The SslProtocols value that represents the protocol used for authentication.</param>
+ public TTLSSocket(
+ TcpClient client,
+ X509Certificate certificate,
+ bool isServer = false,
+ RemoteCertificateValidationCallback certValidator = null,
+ LocalCertificateSelectionCallback localCertificateSelectionCallback = null,
+ // TODO: Enable Tls11 and Tls12 (TLS 1.1 and 1.2) by default once we start using .NET 4.5+.
+ SslProtocols sslProtocols = SslProtocols.Tls)
+ {
+ this.client = client;
+ this.certificate = certificate;
+ this.certValidator = certValidator;
+ this.localCertificateSelectionCallback = localCertificateSelectionCallback;
+ this.sslProtocols = sslProtocols;
+ this.isServer = isServer;
+ if (isServer && certificate == null)
+ {
+ throw new ArgumentException("TTLSSocket needs certificate to be used for server", "certificate");
+ }
+
+ 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>
+ /// <param name="certValidator">User defined cert validator.</param>
+ /// <param name="localCertificateSelectionCallback">The callback to select which certificate to use.</param>
+ /// <param name="sslProtocols">The SslProtocols value that represents the protocol used for authentication.</param>
+ public TTLSSocket(
+ string host,
+ int port,
+ string certificatePath,
+ RemoteCertificateValidationCallback certValidator = null,
+ LocalCertificateSelectionCallback localCertificateSelectionCallback = null,
+ SslProtocols sslProtocols = SslProtocols.Tls)
+ : this(host, port, 0, X509Certificate.CreateFromCertFile(certificatePath), certValidator, localCertificateSelectionCallback, sslProtocols)
+ {
+ }
+
+ /// <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>
+ /// <param name="certValidator">User defined cert validator.</param>
+ /// <param name="localCertificateSelectionCallback">The callback to select which certificate to use.</param>
+ /// <param name="sslProtocols">The SslProtocols value that represents the protocol used for authentication.</param>
+ public TTLSSocket(
+ string host,
+ int port,
+ X509Certificate certificate = null,
+ RemoteCertificateValidationCallback certValidator = null,
+ LocalCertificateSelectionCallback localCertificateSelectionCallback = null,
+ SslProtocols sslProtocols = SslProtocols.Tls)
+ : this(host, port, 0, certificate, certValidator, localCertificateSelectionCallback, sslProtocols)
+ {
+ }
+
+ /// <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>
+ /// <param name="certValidator">User defined cert validator.</param>
+ /// <param name="localCertificateSelectionCallback">The callback to select which certificate to use.</param>
+ /// <param name="sslProtocols">The SslProtocols value that represents the protocol used for authentication.</param>
+ public TTLSSocket(
+ string host,
+ int port,
+ int timeout,
+ X509Certificate certificate,
+ RemoteCertificateValidationCallback certValidator = null,
+ LocalCertificateSelectionCallback localCertificateSelectionCallback = null,
+ SslProtocols sslProtocols = SslProtocols.Tls)
+ {
+ this.host = host;
+ this.port = port;
+ this.timeout = timeout;
+ this.certificate = certificate;
+ this.certValidator = certValidator;
+ this.localCertificateSelectionCallback = localCertificateSelectionCallback;
+ this.sslProtocols = sslProtocols;
+
+ InitSocket();
+ }
+
+ /// <summary>
+ /// Creates the TcpClient and sets the timeouts
+ /// </summary>
+ private void InitSocket()
+ {
+ client = TSocketVersionizer.CreateTcpClient();
+ 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="sslValidationErrors">An enum, which lists all the errors from the .NET certificate check.</param>
+ /// <returns></returns>
+ private bool DefaultCertificateValidator(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();
+ }
+
+ if (timeout == 0) // no timeout -> infinite
+ {
+ client.Connect(host, port);
+ }
+ else // we have a timeout -> use it
+ {
+ ConnectHelper hlp = new ConnectHelper(client);
+ IAsyncResult asyncres = client.BeginConnect(host, port, new AsyncCallback(ConnectCallback), hlp);
+ bool bConnected = asyncres.AsyncWaitHandle.WaitOne(timeout) && client.Connected;
+ if (!bConnected)
+ {
+ lock (hlp.Mutex)
+ {
+ if (hlp.CallbackDone)
+ {
+ asyncres.AsyncWaitHandle.Close();
+ client.Close();
+ }
+ else
+ {
+ hlp.DoCleanup = true;
+ client = null;
+ }
+ }
+ throw new TTransportException(TTransportException.ExceptionType.TimedOut, "Connect timed out");
+ }
+ }
+
+ setupTLS();
+ }
+
+ /// <summary>
+ /// Creates a TLS-stream and lays it over the existing socket
+ /// </summary>
+ public void setupTLS()
+ {
+ RemoteCertificateValidationCallback validator = this.certValidator ?? DefaultCertificateValidator;
+
+ if (this.localCertificateSelectionCallback != null)
+ {
+ this.secureStream = new SslStream(
+ this.client.GetStream(),
+ false,
+ validator,
+ this.localCertificateSelectionCallback
+ );
+ }
+ else
+ {
+ this.secureStream = new SslStream(
+ this.client.GetStream(),
+ false,
+ validator
+ );
+ }
+
+ try
+ {
+ if (isServer)
+ {
+ // Server authentication
+ this.secureStream.AuthenticateAsServer(this.certificate, this.certValidator != null, sslProtocols, true);
+ }
+ else
+ {
+ // Client authentication
+ X509CertificateCollection certs = certificate != null ? new X509CertificateCollection { certificate } : new X509CertificateCollection();
+ this.secureStream.AuthenticateAsClient(host, certs, sslProtocols, true);
+ }
+ }
+ catch (Exception)
+ {
+ this.Close();
+ throw;
+ }
+
+ inputStream = this.secureStream;
+ outputStream = this.secureStream;
+ }
+
+ static void ConnectCallback(IAsyncResult asyncres)
+ {
+ ConnectHelper hlp = asyncres.AsyncState as ConnectHelper;
+ lock (hlp.Mutex)
+ {
+ hlp.CallbackDone = true;
+
+ try
+ {
+ if (hlp.Client.Client != null)
+ hlp.Client.EndConnect(asyncres);
+ }
+ catch (Exception)
+ {
+ // catch that away
+ }
+
+ if (hlp.DoCleanup)
+ {
+ try
+ {
+ asyncres.AsyncWaitHandle.Close();
+ }
+ catch (Exception) { }
+
+ try
+ {
+ if (hlp.Client is IDisposable)
+ ((IDisposable)hlp.Client).Dispose();
+ }
+ catch (Exception) { }
+ hlp.Client = null;
+ }
+ }
+ }
+
+ private class ConnectHelper
+ {
+ public object Mutex = new object();
+ public bool DoCleanup = false;
+ public bool CallbackDone = false;
+ public TcpClient Client;
+ public ConnectHelper(TcpClient client)
+ {
+ Client = client;
+ }
+ }
+
+ /// <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;
+ }
+ }
+ }
+}
diff --git a/lib/csharp/src/Transport/TTransport.cs b/lib/csharp/src/Transport/TTransport.cs
new file mode 100644
index 0000000..5e4ac22
--- /dev/null
+++ b/lib/csharp/src/Transport/TTransport.cs
@@ -0,0 +1,146 @@
+/**
+ * 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.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+using System.IO;
+
+namespace Thrift.Transport
+{
+ public abstract class TTransport : IDisposable
+ {
+ public abstract bool IsOpen
+ {
+ get;
+ }
+
+ private byte[] _peekBuffer = new byte[1];
+ private bool _hasPeekByte;
+
+ public bool Peek()
+ {
+ //If we already have a byte read but not consumed, do nothing.
+ if (_hasPeekByte)
+ return true;
+
+ //If transport closed we can't peek.
+ if (!IsOpen)
+ return false;
+
+ //Try to read one byte. If succeeds we will need to store it for the next read.
+ try
+ {
+ int bytes = Read(_peekBuffer, 0, 1);
+ if (bytes == 0)
+ return false;
+ }
+ catch (IOException)
+ {
+ return false;
+ }
+
+ _hasPeekByte = true;
+ return true;
+ }
+
+ public abstract void Open();
+
+ public abstract void Close();
+
+ protected static void ValidateBufferArgs(byte[] buf, int off, int len)
+ {
+ if (buf == null)
+ throw new ArgumentNullException("buf");
+ if (off < 0)
+ throw new ArgumentOutOfRangeException("Buffer offset is smaller than zero.");
+ if (len < 0)
+ throw new ArgumentOutOfRangeException("Buffer length is smaller than zero.");
+ if (off + len > buf.Length)
+ throw new ArgumentOutOfRangeException("Not enough data.");
+ }
+
+ public abstract int Read(byte[] buf, int off, int len);
+
+ public int ReadAll(byte[] buf, int off, int len)
+ {
+ ValidateBufferArgs(buf, off, len);
+ int got = 0;
+
+ //If we previously peeked a byte, we need to use that first.
+ if (_hasPeekByte)
+ {
+ buf[off + got++] = _peekBuffer[0];
+ _hasPeekByte = false;
+ }
+
+ while (got < len)
+ {
+ int ret = Read(buf, off + got, len - got);
+ if (ret <= 0)
+ {
+ throw new TTransportException(
+ TTransportException.ExceptionType.EndOfFile,
+ "Cannot read, Remote side has closed");
+ }
+ got += ret;
+ }
+ return got;
+ }
+
+ public virtual void Write(byte[] buf)
+ {
+ Write(buf, 0, buf.Length);
+ }
+
+ public abstract void Write(byte[] buf, int off, int len);
+
+ public virtual void Flush()
+ {
+ }
+
+ public virtual IAsyncResult BeginFlush(AsyncCallback callback, object state)
+ {
+ throw new TTransportException(
+ TTransportException.ExceptionType.Unknown,
+ "Asynchronous operations are not supported by this transport.");
+ }
+
+ public virtual void EndFlush(IAsyncResult asyncResult)
+ {
+ throw new TTransportException(
+ TTransportException.ExceptionType.Unknown,
+ "Asynchronous operations are not supported by this transport.");
+ }
+
+ #region " IDisposable Support "
+ // IDisposable
+ protected abstract void Dispose(bool disposing);
+
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above.
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+ }
+}
diff --git a/lib/csharp/src/Transport/TTransportException.cs b/lib/csharp/src/Transport/TTransportException.cs
new file mode 100644
index 0000000..7f6cc18
--- /dev/null
+++ b/lib/csharp/src/Transport/TTransportException.cs
@@ -0,0 +1,69 @@
+/**
+ * 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.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+
+namespace Thrift.Transport
+{
+ public class TTransportException : TException
+ {
+ protected ExceptionType type;
+
+ public TTransportException()
+ : base()
+ {
+ }
+
+ public TTransportException(ExceptionType type)
+ : this()
+ {
+ this.type = type;
+ }
+
+ public TTransportException(ExceptionType type, string message, Exception inner = null)
+ : base(message, inner)
+ {
+ this.type = type;
+ }
+
+ public TTransportException(string message, Exception inner = null)
+ : base(message, inner)
+ {
+ }
+
+ public ExceptionType Type
+ {
+ get { return type; }
+ }
+
+ public enum ExceptionType
+ {
+ Unknown,
+ NotOpen,
+ AlreadyOpen,
+ TimedOut,
+ EndOfFile,
+ Interrupted
+ }
+ }
+}
diff --git a/lib/csharp/src/Transport/TTransportFactory.cs b/lib/csharp/src/Transport/TTransportFactory.cs
new file mode 100644
index 0000000..47a0c62
--- /dev/null
+++ b/lib/csharp/src/Transport/TTransportFactory.cs
@@ -0,0 +1,42 @@
+/**
+ * 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.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+using System;
+
+namespace Thrift.Transport
+{
+ /// <summary>
+ /// From Mark Slee & Aditya Agarwal of Facebook:
+ /// Factory class used to create wrapped instance of Transports.
+ /// This is used primarily in servers, which get Transports from
+ /// a ServerTransport and then may want to mutate them (i.e. create
+ /// a BufferedTransport from the underlying base transport)
+ /// </summary>
+ public class TTransportFactory
+ {
+ public virtual TTransport GetTransport(TTransport trans)
+ {
+ return trans;
+ }
+ }
+}