THRIFT-5832 Drop net6 support and add net9 instead
Client: netstd
Patch: Jens Geyer
diff --git a/tutorial/netstd/Client/Client.csproj b/tutorial/netstd/Client/Client.csproj
index 732943f..df3f6c9 100644
--- a/tutorial/netstd/Client/Client.csproj
+++ b/tutorial/netstd/Client/Client.csproj
@@ -19,12 +19,13 @@
   -->
 
   <PropertyGroup>
-    <TargetFramework>net8.0</TargetFramework>
+    <TargetFramework>net9.0</TargetFramework>
     <LangVersion>latestMajor</LangVersion>
     <AssemblyName>Client</AssemblyName>
     <PackageId>Client</PackageId>
     <OutputType>Exe</OutputType>
     <Version>0.22.0.0</Version>
+    <Nullable>enable</Nullable>
     <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
     <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
     <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
@@ -32,7 +33,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
+    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/tutorial/netstd/Client/Program.cs b/tutorial/netstd/Client/Program.cs
index f1c5236..93175fd 100644
--- a/tutorial/netstd/Client/Program.cs
+++ b/tutorial/netstd/Client/Program.cs
@@ -15,8 +15,8 @@
 // specific language governing permissions and limitations
 // under the License.
 
+using Microsoft.Extensions.Logging;
 using System;
-using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
 using System.Linq;
@@ -25,16 +25,12 @@
 using System.Security.Cryptography.X509Certificates;
 using System.Threading;
 using System.Threading.Tasks;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.DependencyInjection;
 using Thrift;
 using Thrift.Protocol;
 using Thrift.Transport;
 using Thrift.Transport.Client;
 using tutorial;
-using shared;
 
-#pragma warning disable IDE0063  // using
 #pragma warning disable IDE0057  // substr
 
 namespace Client
@@ -58,7 +54,7 @@
     public class Program
     {
         private static readonly ILogger Logger = LoggingHelper.CreateLogger<Program>();
-        private static readonly TConfiguration Configuration = null;  // new TConfiguration() if  needed
+        private static readonly TConfiguration Configuration = new();
 
         private static void DisplayHelp()
         {
@@ -97,11 +93,12 @@
 ");
         }
 
-        public static void Main(string[] args)
+        public static async Task Main(string[] args)
         {
-            args ??= Array.Empty<string>();
+            args ??= [];
 
-            if (args.Any(x => x.StartsWith("-help", StringComparison.OrdinalIgnoreCase)))
+            // -help is rather unusual but we leave it for compatibility
+            if (args.Any(x => x.Equals("-help") || x.Equals("--help") || x.Equals("-h") || x.Equals("-?")))
             {
                 DisplayHelp();
                 return;
@@ -109,10 +106,8 @@
 
             Logger.LogInformation("Starting client...");
 
-            using (var source = new CancellationTokenSource())
-            {
-                RunAsync(args, source.Token).GetAwaiter().GetResult();
-            }
+            using var source = new CancellationTokenSource();
+            await RunAsync(args, source.Token);
         }
 
         
@@ -150,7 +145,7 @@
 
         private static Protocol GetProtocol(string[] args)
         {
-            var protocol = args.FirstOrDefault(x => x.StartsWith("-pr"))?.Split(':')?[1];
+            var protocol = args.FirstOrDefault(x => x.StartsWith("-pr"))?.Split(':').Skip(1).Take(1).FirstOrDefault();
             if (string.IsNullOrEmpty(protocol))
                 return Protocol.Binary;
 
@@ -163,7 +158,7 @@
 
         private static Buffering GetBuffering(string[] args)
         {
-            var buffering = args.FirstOrDefault(x => x.StartsWith("-bf"))?.Split(":")?[1];
+            var buffering = args.FirstOrDefault(x => x.StartsWith("-bf"))?.Split(':').Skip(1).Take(1).FirstOrDefault();
             if (string.IsNullOrEmpty(buffering))
                 return Buffering.None;
 
@@ -176,7 +171,7 @@
 
         private static Transport GetTransport(string[] args)
         {
-            var transport = args.FirstOrDefault(x => x.StartsWith("-tr"))?.Split(':')?[1];
+            var transport = args.FirstOrDefault(x => x.StartsWith("-tr"))?.Split(':').Skip(1).Take(1).FirstOrDefault();
             if (string.IsNullOrEmpty(transport))
                 return Transport.Tcp;
 
@@ -191,7 +186,7 @@
         private static TTransport MakeTransport(string[] args)
         {
             // construct endpoint transport
-            TTransport transport = null;
+            TTransport? transport = null;
             Transport selectedTransport = GetTransport(args);
             {
                 switch (selectedTransport)
@@ -241,7 +236,7 @@
 
         private static int GetNumberOfClients(string[] args)
         {
-            var numClients = args.FirstOrDefault(x => x.StartsWith("-mc"))?.Split(':')?[1];
+            var numClients = args.FirstOrDefault(x => x.StartsWith("-mc"))?.Split(':').Skip(1).Take(1).FirstOrDefault();
 
             Logger.LogInformation("Selected # of clients: {numClients}", numClients);
 
@@ -254,35 +249,42 @@
         private static X509Certificate2 GetCertificate()
         {
             // due to files location in net core better to take certs from top folder
-            var certFile = GetCertPath(Directory.GetParent(Directory.GetCurrentDirectory()));
-            return new X509Certificate2(certFile, "ThriftTest");
+            var dir = Directory.GetParent(Directory.GetCurrentDirectory());
+            if (dir != null)
+            {
+                var certFile = GetCertPath(dir);
+                //return new X509Certificate2(certFile, "ThriftTest");
+                return X509CertificateLoader.LoadPkcs12FromFile(certFile, "ThriftTest");
+            }
+            else
+            {
+                Logger.LogError("Root path of {path} not found", Directory.GetCurrentDirectory());
+                throw new Exception($"Root path of {Directory.GetCurrentDirectory()} not found");
+            }
         }
 
-        private static string GetCertPath(DirectoryInfo di, int maxCount = 6)
+        private static string GetCertPath(DirectoryInfo? di, int maxCount = 6)
         {
             var topDir = di;
-            var certFile =
-                topDir.EnumerateFiles("ThriftTest.pfx", SearchOption.AllDirectories)
-                    .FirstOrDefault();
+            var certFile = topDir?.EnumerateFiles("ThriftTest.pfx", SearchOption.AllDirectories).FirstOrDefault();
             if (certFile == null)
             {
                 if (maxCount == 0)
                     throw new FileNotFoundException("Cannot find file in directories");
-                return GetCertPath(di.Parent, maxCount - 1);
+                return GetCertPath(di?.Parent, --maxCount);
             }
 
             return certFile.FullName;
         }
 
-        private static X509Certificate LocalCertificateSelectionCallback(object sender,
+        private static X509Certificate2 LocalCertificateSelectionCallback(object sender,
             string targetHost, X509CertificateCollection localCertificates,
-            X509Certificate remoteCertificate, string[] acceptableIssuers)
+            X509Certificate? remoteCertificate, string[] acceptableIssuers)
         {
             return GetCertificate();
         }
 
-        private static bool CertValidator(object sender, X509Certificate certificate,
-            X509Chain chain, SslPolicyErrors sslPolicyErrors)
+        private static bool CertValidator(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors)
         {
             return true;
         }
diff --git a/tutorial/netstd/Interfaces/Interfaces.csproj b/tutorial/netstd/Interfaces/Interfaces.csproj
index 2791dc8..9a243ea 100644
--- a/tutorial/netstd/Interfaces/Interfaces.csproj
+++ b/tutorial/netstd/Interfaces/Interfaces.csproj
@@ -19,10 +19,11 @@
   -->
 
   <PropertyGroup>
-    <TargetFramework>net8.0</TargetFramework>
+    <TargetFramework>net9.0</TargetFramework>
     <AssemblyName>Interfaces</AssemblyName>
     <PackageId>Interfaces</PackageId>
     <Version>0.22.0.0</Version>
+    <Nullable>enable</Nullable>
     <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
     <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
     <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
@@ -34,15 +35,15 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="System.ServiceModel.Primitives" Version="8.0.0" />
+    <PackageReference Include="System.ServiceModel.Primitives" Version="8.1.0" />
   </ItemGroup>
 
   <Target Name="PreBuild" BeforeTargets="_GenerateRestoreProjectSpec;Restore;Compile">
     <Exec Condition="'$(OS)' == 'Windows_NT'" Command="where thrift" ConsoleToMSBuild="true">
       <Output TaskParameter="ConsoleOutput" PropertyName="PathToThrift" />
     </Exec>
-    <Exec Condition="Exists('$(PathToThrift)')" Command="$(PathToThrift) -out $(ProjectDir) -gen netstd:wcf,union,serial,net8 -r ./../../tutorial.thrift" />
-    <Exec Condition="Exists('thrift')" Command="thrift -out $(ProjectDir) -gen netstd:wcf,union,serial,net8 -r ./../../tutorial.thrift" />
-    <Exec Condition="Exists('./../../../compiler/cpp/thrift')" Command="./../../../compiler/cpp/thrift -out $(ProjectDir) -gen netstd:wcf,union,serial,net8 -r ./../../tutorial.thrift" />
+    <Exec Condition="Exists('$(PathToThrift)')" Command="$(PathToThrift) -out $(ProjectDir) -gen netstd:wcf,union,serial,net9 -r ./../../tutorial.thrift" />
+    <Exec Condition="Exists('thrift')" Command="thrift -out $(ProjectDir) -gen netstd:wcf,union,serial,net9 -r ./../../tutorial.thrift" />
+    <Exec Condition="Exists('./../../../compiler/cpp/thrift')" Command="./../../../compiler/cpp/thrift -out $(ProjectDir) -gen netstd:wcf,union,serial,net9 -r ./../../tutorial.thrift" />
   </Target>
 </Project>
diff --git a/tutorial/netstd/Server/Program.cs b/tutorial/netstd/Server/Program.cs
index 01e7336..a3b12fc 100644
--- a/tutorial/netstd/Server/Program.cs
+++ b/tutorial/netstd/Server/Program.cs
@@ -15,6 +15,12 @@
 // specific language governing permissions and limitations
 // under the License.
 
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using shared;
 using System;
 using System.Collections.Generic;
 using System.IO;
@@ -23,20 +29,13 @@
 using System.Security.Cryptography.X509Certificates;
 using System.Threading;
 using System.Threading.Tasks;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
 using Thrift;
+using Thrift.Processor;
 using Thrift.Protocol;
 using Thrift.Server;
 using Thrift.Transport;
 using Thrift.Transport.Server;
 using tutorial;
-using shared;
-using Thrift.Processor;
-using System.Diagnostics;
 
 #pragma warning disable IDE0057  // substr
 
@@ -61,27 +60,25 @@
     public class Program
     {
         private static readonly ILogger Logger = LoggingHelper.CreateLogger<Program>();
-        private static readonly TConfiguration Configuration = null;  // new TConfiguration() if  needed
+        private static readonly TConfiguration Configuration = new();
 
-        public static void Main(string[] args)
+        public static async Task Main(string[] args)
         {
-            args ??= Array.Empty<string>();
+            args ??= [];
 
-            if (args.Any(x => x.StartsWith("-help", StringComparison.OrdinalIgnoreCase)))
+            // -help is rather unusual but we leave it for compatibility
+            if (args.Any(x => x.Equals("-help") || x.Equals("--help") || x.Equals("-h") || x.Equals("-?")))
             {
                 DisplayHelp();
                 return;
             }
 
-            using (var source = new CancellationTokenSource())
-            {
-                RunAsync(args, source.Token).GetAwaiter().GetResult();
+            using var source = new CancellationTokenSource();
+            await RunAsync(args, source.Token);
 
-                Logger.LogInformation("Press any key to stop...");
-
-                Console.ReadLine();
-                source.Cancel();
-            }
+            Logger.LogInformation("Press any key to stop...");
+            Console.ReadLine();
+            source.Cancel();
 
             Logger.LogInformation("Server stopped");
         }
@@ -149,7 +146,7 @@
 
         private static Protocol GetProtocol(string[] args)
         {
-            var protocol = args.FirstOrDefault(x => x.StartsWith("-pr"))?.Split(':')?[1];
+            var protocol = args.FirstOrDefault(x => x.StartsWith("-pr"))?.Split(':').Skip(1).Take(1).FirstOrDefault();
             if (string.IsNullOrEmpty(protocol))
                 return Protocol.Binary;
 
@@ -162,7 +159,7 @@
 
         private static Buffering GetBuffering(string[] args)
         {
-            var buffering = args.FirstOrDefault(x => x.StartsWith("-bf"))?.Split(":")?[1];
+            var buffering = args.FirstOrDefault(x => x.StartsWith("-bf"))?.Split(':').Skip(1).Take(1).FirstOrDefault();
             if (string.IsNullOrEmpty(buffering))
                 return Buffering.None;
 
@@ -175,7 +172,7 @@
 
         private static Transport GetTransport(string[] args)
         {
-            var transport = args.FirstOrDefault(x => x.StartsWith("-tr"))?.Split(':')?[1];
+            var transport = args.FirstOrDefault(x => x.StartsWith("-tr"))?.Split(':').Skip(1).Take(1).FirstOrDefault();
             if (string.IsNullOrEmpty(transport))
                 return Transport.Tcp;
 
@@ -196,7 +193,7 @@
                 _ => throw new ArgumentException("unsupported value $transport", nameof(transport)),
             };
 
-            TTransportFactory transportFactory = buffering switch
+            TTransportFactory? transportFactory = buffering switch
             {
                 Buffering.Buffered => new TBufferedTransport.Factory(),
                 Buffering.Framed => new TFramedTransport.Factory(),
@@ -258,34 +255,32 @@
         {
             // due to files location in net core better to take certs from top folder
             var certFile = GetCertPath(Directory.GetParent(Directory.GetCurrentDirectory()));
-            return new X509Certificate2(certFile, "ThriftTest");
+            //return new X509Certificate2(certFile, "ThriftTest");
+            return X509CertificateLoader.LoadPkcs12FromFile(certFile, "ThriftTest");
         }
 
-        private static string GetCertPath(DirectoryInfo di, int maxCount = 6)
+        private static string GetCertPath(DirectoryInfo? di, int maxCount = 6)
         {
             var topDir = di;
-            var certFile =
-                topDir.EnumerateFiles("ThriftTest.pfx", SearchOption.AllDirectories)
-                    .FirstOrDefault();
+            var certFile = topDir?.EnumerateFiles("ThriftTest.pfx", SearchOption.AllDirectories).FirstOrDefault();
             if (certFile == null)
             {
                 if (maxCount == 0)
                     throw new FileNotFoundException("Cannot find file in directories");
-                return GetCertPath(di.Parent, maxCount - 1);
+                return GetCertPath(di?.Parent, --maxCount);
             }
 
             return certFile.FullName;
         }
 
-        private static X509Certificate LocalCertificateSelectionCallback(object sender,
+        private static X509Certificate2 LocalCertificateSelectionCallback(object sender,
             string targetHost, X509CertificateCollection localCertificates,
-            X509Certificate remoteCertificate, string[] acceptableIssuers)
+            X509Certificate? remoteCertificate, string[] acceptableIssuers)
         {
             return GetCertificate();
         }
 
-        private static bool ClientCertValidator(object sender, X509Certificate certificate,
-            X509Chain chain, SslPolicyErrors sslPolicyErrors)
+        private static bool ClientCertValidator(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors)
         {
             return true;
         }
@@ -369,7 +364,7 @@
 
         public class CalculatorAsyncHandler : Calculator.IAsync
         {
-            private readonly Dictionary<int, SharedStruct> _log = new();
+            private readonly Dictionary<int, SharedStruct> _log = [];
 
             public CalculatorAsyncHandler()
             {
@@ -394,12 +389,12 @@
                 return await Task.FromResult(num1 + num2);
             }
 
-            public async Task<int> calculate(int logid, Work w, CancellationToken cancellationToken)
+            public async Task<int> calculate(int logid, Work? w, CancellationToken cancellationToken)
             {
-                Logger.LogInformation("Calculate({logid}, [{w.Op},{w.Num1},{w.Num2}])", logid, w.Op, w.Num1, w.Num2);
+                Logger.LogInformation("Calculate({logid}, [{w.Op},{w.Num1},{w.Num2}])", logid, w?.Op, w?.Num1, w?.Num2);
 
                 int val;
-                switch (w.Op)
+                switch (w?.Op)
                 {
                     case Operation.ADD:
                         val = w.Num1 + w.Num2;
@@ -431,7 +426,7 @@
                     {
                         var io = new InvalidOperation
                         {
-                            WhatOp = (int) w.Op,
+                            WhatOp = ((int?)w?.Op) ?? -1,
                             Why = "Unknown operation"
                         };
 
diff --git a/tutorial/netstd/Server/Server.csproj b/tutorial/netstd/Server/Server.csproj
index 16b72e2..b50b69c 100644
--- a/tutorial/netstd/Server/Server.csproj
+++ b/tutorial/netstd/Server/Server.csproj
@@ -19,12 +19,13 @@
   -->
 
   <PropertyGroup>
-    <TargetFramework>net8.0</TargetFramework>
+    <TargetFramework>net9.0</TargetFramework>
     <LangVersion>latestMajor</LangVersion>
     <AssemblyName>Server</AssemblyName>
     <PackageId>Server</PackageId>
     <OutputType>Exe</OutputType>
     <Version>0.22.0.0</Version>
+    <Nullable>enable</Nullable>
     <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
     <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
     <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>