THRIFT-3000 .NET implementation has trouble with mixed IP modes
Client: C#
Patch: sharpdevel <icode666@github> & Jens Geyer
This closes #377
This closes #452
This commit effectively establishes .NET 4.5 as a requirement for C#. Trying to build for earlier platform versions will fail. If that turns out to become a problem we should address this with a subsequent ticket.
TcpListener and TcpClient are created based on the capabilities of the used runtime framework. For windows the changes automatically handle IPv4 and IPv6 sockets. In mono it behaves as before.
When using TcpListener and TcpClient it depends on the network configuration if IPv4 or IPv6 is used. By upgrading the framework to .NET 4.5 the DualMode can be set on the sockets of the listener and the client. The sockets then try to establish IPv6 sockets before they fallback to IPv4
diff --git a/configure.ac b/configure.ac
index 0032354..aae784a 100755
--- a/configure.ac
+++ b/configure.ac
@@ -194,6 +194,7 @@
AX_THRIFT_LIB(csharp, [C#], yes)
if test "$with_csharp" = "yes"; then
+ PKG_CHECK_MODULES(MONO, mono >= 3.0.0, net_4_5=yes, net_4_5=no)
PKG_CHECK_MODULES(MONO, mono >= 2.0.0, net_3_5=yes, net_3_5=no)
PKG_CHECK_MODULES(MONO, mono >= 1.2.4, have_mono=yes, have_mono=no)
if test "$have_mono" = "yes" ; then
@@ -202,6 +203,7 @@
fi
AM_CONDITIONAL(WITH_MONO, [test "$have_csharp" = "yes"])
AM_CONDITIONAL(NET_2_0, [test "$net_3_5" = "no"])
+AM_CONDITIONAL(NET_4_5, [test "$net_4_5" = "yes"])
AX_THRIFT_LIB(java, [Java], yes)
if test "$with_java" = "yes"; then
diff --git a/lib/csharp/Makefile.am b/lib/csharp/Makefile.am
index 5ce4276..7cdec05 100644
--- a/lib/csharp/Makefile.am
+++ b/lib/csharp/Makefile.am
@@ -52,6 +52,7 @@
src/Transport/TBufferedTransport.cs \
src/Transport/TTransport.cs \
src/Transport/TSocket.cs \
+ src/Transport/TSocketVersionizer.cs \
src/Transport/TTransportException.cs \
src/Transport/TStreamTransport.cs \
src/Transport/TFramedTransport.cs \
@@ -75,6 +76,10 @@
MONO_DEFINES=/d:NET_2_0
endif
+if NET_4_5
+MONO_DEFINES=/d:NET_4_5 -sdk:4.5
+endif
+
all-local: Thrift.dll
Thrift.dll: $(THRIFTCODE)
diff --git a/lib/csharp/ThriftMSBuildTask/ThriftMSBuildTask.csproj b/lib/csharp/ThriftMSBuildTask/ThriftMSBuildTask.csproj
index ae86081..1148eaa 100644
--- a/lib/csharp/ThriftMSBuildTask/ThriftMSBuildTask.csproj
+++ b/lib/csharp/ThriftMSBuildTask/ThriftMSBuildTask.csproj
@@ -28,7 +28,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ThriftMSBuildTask</RootNamespace>
<AssemblyName>ThriftMSBuildTask</AssemblyName>
- <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<FileUpgradeFlags>
</FileUpgradeFlags>
diff --git a/lib/csharp/src/Thrift.csproj b/lib/csharp/src/Thrift.csproj
index 195005a..dc0aa6d 100644
--- a/lib/csharp/src/Thrift.csproj
+++ b/lib/csharp/src/Thrift.csproj
@@ -27,7 +27,7 @@
<OutputType>Library</OutputType>
<NoStandardLibraries>false</NoStandardLibraries>
<AssemblyName>Thrift</AssemblyName>
- <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<RootNamespace>Thrift</RootNamespace>
<FileUpgradeFlags>
@@ -109,6 +109,7 @@
<Compile Include="TException.cs" />
<Compile Include="TApplicationException.cs" />
<Compile Include="TProcessor.cs" />
+ <Compile Include="Transport\TSocketVersionizer.cs" />
<Compile Include="Transport\TBufferedTransport.cs" />
<Compile Include="Transport\TFramedTransport.cs" />
<Compile Include="Transport\THttpClient.cs" />
@@ -147,4 +148,4 @@
<ProjectExtensions>
<VisualStudio AllowExistingFolder="true" />
</ProjectExtensions>
-</Project>
+</Project>
\ No newline at end of file
diff --git a/lib/csharp/src/Transport/TServerSocket.cs b/lib/csharp/src/Transport/TServerSocket.cs
index 82a367c..524003e 100644
--- a/lib/csharp/src/Transport/TServerSocket.cs
+++ b/lib/csharp/src/Transport/TServerSocket.cs
@@ -23,6 +23,7 @@
using System;
using System.Net.Sockets;
+using System.Reflection;
namespace Thrift.Transport
@@ -53,7 +54,7 @@
* Creates a server socket from underlying socket object
*/
public TServerSocket(TcpListener listener)
- :this(listener, 0)
+ : this(listener, 0)
{
}
@@ -78,7 +79,7 @@
* Creates just a port listening server socket
*/
public TServerSocket(int port, int clientTimeout)
- :this(port, clientTimeout, false)
+ : this(port, clientTimeout, false)
{
}
@@ -90,8 +91,8 @@
try
{
// Make server socket
- server = new TcpListener(System.Net.IPAddress.Any, this.port);
- server.Server.NoDelay = true;
+ this.server = TSocketVersionizer.CreateTcpListener(port);
+ this.server.Server.NoDelay = true;
}
catch (Exception)
{
diff --git a/lib/csharp/src/Transport/TServerTransport.cs b/lib/csharp/src/Transport/TServerTransport.cs
index 05d7d0f..8e84323 100644
--- a/lib/csharp/src/Transport/TServerTransport.cs
+++ b/lib/csharp/src/Transport/TServerTransport.cs
@@ -22,6 +22,8 @@
*/
using System;
+using System.Net.Sockets;
+using System.Reflection;
namespace Thrift.Transport
{
@@ -34,10 +36,11 @@
public TTransport Accept()
{
TTransport transport = AcceptImpl();
- if (transport == null) {
- throw new TTransportException("accept() may not return NULL");
+ if (transport == null)
+ {
+ throw new TTransportException("accept() may not return NULL");
}
return transport;
- }
+ }
}
}
diff --git a/lib/csharp/src/Transport/TSocket.cs b/lib/csharp/src/Transport/TSocket.cs
index cf1a440..0b47572 100644
--- a/lib/csharp/src/Transport/TSocket.cs
+++ b/lib/csharp/src/Transport/TSocket.cs
@@ -60,9 +60,9 @@
private void InitSocket()
{
- client = new TcpClient();
- client.ReceiveTimeout = client.SendTimeout = timeout;
- client.Client.NoDelay = true;
+ this.client = TSocketVersionizer.CreateTcpClient();
+ this.client.ReceiveTimeout = client.SendTimeout = timeout;
+ this.client.Client.NoDelay = true;
}
public int Timeout
@@ -132,7 +132,7 @@
InitSocket();
}
- if( timeout == 0) // no timeout -> infinite
+ if (timeout == 0) // no timeout -> infinite
{
client.Connect(host, port);
}
@@ -145,7 +145,7 @@
{
lock (hlp.Mutex)
{
- if( hlp.CallbackDone)
+ if (hlp.CallbackDone)
{
asyncres.AsyncWaitHandle.Close();
client.Close();
@@ -174,7 +174,7 @@
try
{
- if( hlp.Client.Client != null)
+ if (hlp.Client.Client != null)
hlp.Client.EndConnect(asyncres);
}
catch (Exception)
@@ -184,14 +184,18 @@
if (hlp.DoCleanup)
{
- try {
+ try
+ {
asyncres.AsyncWaitHandle.Close();
- } catch (Exception) {}
+ }
+ catch (Exception) { }
- try {
+ try
+ {
if (hlp.Client is IDisposable)
((IDisposable)hlp.Client).Dispose();
- } catch (Exception) {}
+ }
+ catch (Exception) { }
hlp.Client = null;
}
}
@@ -219,23 +223,23 @@
}
}
- #region " IDisposable Support "
- private bool _IsDisposed;
+ #region " IDisposable Support "
+ private bool _IsDisposed;
- // IDisposable
- protected override void Dispose(bool disposing)
- {
- if (!_IsDisposed)
- {
- if (disposing)
+ // IDisposable
+ protected override void Dispose(bool disposing)
{
- if (client != null)
- ((IDisposable)client).Dispose();
- base.Dispose(disposing);
+ if (!_IsDisposed)
+ {
+ if (disposing)
+ {
+ if (client != null)
+ ((IDisposable)client).Dispose();
+ base.Dispose(disposing);
+ }
+ }
+ _IsDisposed = true;
}
- }
- _IsDisposed = true;
+ #endregion
}
- #endregion
- }
}
diff --git a/lib/csharp/src/Transport/TSocketVersionizer.cs b/lib/csharp/src/Transport/TSocketVersionizer.cs
new file mode 100644
index 0000000..1928fac
--- /dev/null
+++ b/lib/csharp/src/Transport/TSocketVersionizer.cs
@@ -0,0 +1,92 @@
+/**
+ * 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.Linq;
+using System.Net.Sockets;
+using System.Reflection;
+using System.Text;
+
+namespace Thrift.Transport
+{
+ /**
+ * 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.
+ */
+ internal static class TSocketVersionizer
+ {
+ /*
+ * PropertyInfo for the DualMode property of System.Net.Sockets.Socket.
+ */
+ private static PropertyInfo DualModeProperty = typeof(Socket).GetProperty("DualMode");
+
+ /*
+ * Indicates whether the used framework supports DualMode on sockets or not.
+ */
+ internal static Boolean SupportsDualMode
+ {
+ get
+ {
+ return TSocketVersionizer.DualModeProperty != null;
+ }
+ }
+
+ /*
+ * Creates a TcpClient according to the capabilitites of the used framework
+ */
+ internal static TcpClient CreateTcpClient()
+ {
+ TcpClient client = null;
+
+ if (TSocketVersionizer.SupportsDualMode)
+ {
+ client = new TcpClient(AddressFamily.InterNetworkV6);
+ TSocketVersionizer.DualModeProperty.SetValue(client.Client, true);
+ }
+ else
+ {
+ client = new TcpClient(AddressFamily.InterNetwork);
+ }
+
+ return client;
+ }
+
+ /*
+ * Creates a TcpListener according to the capabilitites of the used framework
+ */
+ internal static TcpListener CreateTcpListener(Int32 port)
+ {
+ TcpListener listener = null;
+
+ if (TSocketVersionizer.SupportsDualMode)
+ {
+ listener = new TcpListener(System.Net.IPAddress.IPv6Any, port);
+ TSocketVersionizer.DualModeProperty.SetValue(listener.Server, true);
+ }
+ else
+ {
+ listener = new TcpListener(System.Net.IPAddress.Any, port);
+ }
+
+ return listener;
+ }
+ }
+}
diff --git a/lib/csharp/src/Transport/TTLSServerSocket.cs b/lib/csharp/src/Transport/TTLSServerSocket.cs
index 631a593..e56c66c 100644
--- a/lib/csharp/src/Transport/TTLSServerSocket.cs
+++ b/lib/csharp/src/Transport/TTLSServerSocket.cs
@@ -115,8 +115,8 @@
try
{
// Create server socket
- server = new TcpListener(System.Net.IPAddress.Any, this.port);
- server.Server.NoDelay = true;
+ this.server = TSocketVersionizer.CreateTcpListener(port);
+ this.server.Server.NoDelay = true;
}
catch (Exception)
{
diff --git a/lib/csharp/src/Transport/TTLSSocket.cs b/lib/csharp/src/Transport/TTLSSocket.cs
index 5652556..d48b7d5 100644
--- a/lib/csharp/src/Transport/TTLSSocket.cs
+++ b/lib/csharp/src/Transport/TTLSSocket.cs
@@ -172,7 +172,7 @@
/// </summary>
private void InitSocket()
{
- this.client = new TcpClient();
+ this.client = TSocketVersionizer.CreateTcpClient();
client.ReceiveTimeout = client.SendTimeout = timeout;
client.Client.NoDelay = true;
}
@@ -286,7 +286,7 @@
public void setupTLS()
{
RemoteCertificateValidationCallback validator = this.certValidator ?? DefaultCertificateValidator;
-
+
if( this.localCertificateSelectionCallback != null)
{
this.secureStream = new SslStream(
@@ -304,7 +304,7 @@
validator
);
}
-
+
try
{
if (isServer)
diff --git a/lib/csharp/src/Transport/TTransport.cs b/lib/csharp/src/Transport/TTransport.cs
index 2811399..d4977f1 100644
--- a/lib/csharp/src/Transport/TTransport.cs
+++ b/lib/csharp/src/Transport/TTransport.cs
@@ -55,7 +55,7 @@
}
catch( IOException)
{
- return false;
+ return false;
}
_hasPeekByte = true;
diff --git a/lib/csharp/test/Multiplex/Client/MultiplexClient.csproj b/lib/csharp/test/Multiplex/Client/MultiplexClient.csproj
index 6221e14..ff78723 100644
--- a/lib/csharp/test/Multiplex/Client/MultiplexClient.csproj
+++ b/lib/csharp/test/Multiplex/Client/MultiplexClient.csproj
@@ -28,7 +28,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MultiplexClient</RootNamespace>
<AssemblyName>MultiplexClient</AssemblyName>
- <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<IsWebBootstrapper>false</IsWebBootstrapper>
<FileUpgradeFlags>
diff --git a/lib/csharp/test/Multiplex/Server/MultiplexServer.csproj b/lib/csharp/test/Multiplex/Server/MultiplexServer.csproj
index dc1d123..6a8177e 100644
--- a/lib/csharp/test/Multiplex/Server/MultiplexServer.csproj
+++ b/lib/csharp/test/Multiplex/Server/MultiplexServer.csproj
@@ -28,7 +28,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MultiplexServer</RootNamespace>
<AssemblyName>MultiplexServer</AssemblyName>
- <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<IsWebBootstrapper>false</IsWebBootstrapper>
<FileUpgradeFlags>
diff --git a/lib/csharp/test/ThriftTest/TestClient.cs b/lib/csharp/test/ThriftTest/TestClient.cs
index ec0696a..b54d09d 100644
--- a/lib/csharp/test/ThriftTest/TestClient.cs
+++ b/lib/csharp/test/ThriftTest/TestClient.cs
@@ -185,14 +185,14 @@
// linear distribution, unless random is requested
if (!randomDist) {
- for (var i = 0; i < initLen; ++i) {
+ for (var i = 0; i < initLen; ++i) {
retval[i] = (byte)i;
}
return retval;
}
// random distribution
- for (var i = 0; i < initLen; ++i) {
+ for (var i = 0; i < initLen; ++i) {
retval[i] = (byte)0;
}
var rnd = new Random();
@@ -270,13 +270,13 @@
if (binIn[ofs] != binOut[ofs])
throw new Exception("testBinary: content mismatch at offset " + ofs.ToString());
}
- catch (Thrift.TApplicationException e)
+ catch (Thrift.TApplicationException e)
{
Console.Write("testBinary(" + BytesToHex(binOut) + "): "+e.Message);
}
// binary equals? only with hashcode option enabled ...
- if( typeof(CrazyNesting).GetMethod("Equals").DeclaringType == typeof(CrazyNesting))
+ if( typeof(CrazyNesting).GetMethod("Equals").DeclaringType == typeof(CrazyNesting))
{
CrazyNesting one = new CrazyNesting();
CrazyNesting two = new CrazyNesting();
diff --git a/lib/csharp/test/ThriftTest/ThriftTest.csproj b/lib/csharp/test/ThriftTest/ThriftTest.csproj
index d671997..5f9f151 100644
--- a/lib/csharp/test/ThriftTest/ThriftTest.csproj
+++ b/lib/csharp/test/ThriftTest/ThriftTest.csproj
@@ -28,7 +28,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ThriftTest</RootNamespace>
<AssemblyName>ThriftTest</AssemblyName>
- <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<IsWebBootstrapper>false</IsWebBootstrapper>
<FileUpgradeFlags>
@@ -136,6 +136,6 @@
for %25%25I in ("%25OUTPUT_DIR%25") do set SHORT_DIR=%25%25~fsI
for %25%25I in ("%25THRIFT_FILE%25") do set THRIFT_SHORT=%25%25~fsI
"$(ProjectDir)\..\..\..\..\compiler\cpp\thrift.exe" --gen csharp -o %25SHORT_DIR%25 %25THRIFT_SHORT%25
-$(MSBuildToolsPath)\Csc.exe /t:library /out:"$(ProjectDir)ThriftImpl.dll" /recurse:"$(ProjectDir)gen-csharp"\* /reference:"$(ProjectDir)..\..\src\bin\Debug\Thrift.dll"</PreBuildEvent>
+"$(MSBuildToolsPath)\Csc.exe" /t:library /out:"$(ProjectDir)ThriftImpl.dll" /recurse:"$(ProjectDir)gen-csharp"\* /reference:"$(ProjectDir)..\..\src\bin\Debug\Thrift.dll"</PreBuildEvent>
</PropertyGroup>
</Project>
\ No newline at end of file