Repository: tylerjensen/ServiceWire Branch: master Commit: be54ced4c019 Files: 117 Total size: 384.5 KB Directory structure: gitextract_ub_i6dpw/ ├── .gitattributes ├── .github/ │ └── workflows/ │ └── build-and-test.yml ├── .gitignore ├── README.md └── src/ ├── Benchmarks/ │ └── ServiceWire.Benchmarks/ │ ├── ConnectionBenchmarks.cs │ ├── INetTester.cs │ ├── NamedPipesBenchmarks.cs │ ├── NewtonSoftSerializer.cs │ ├── Program.cs │ ├── ServiceWire.Benchmarks.csproj │ └── TcpBenchmarks.cs ├── Demo/ │ ├── DemoClient/ │ │ ├── App.config │ │ ├── DemoClient.csproj │ │ └── Program.cs │ ├── DemoCommon/ │ │ ├── Contracts.cs │ │ └── DemoCommon.csproj │ └── DemoHost/ │ ├── App.config │ ├── DemoHost.csproj │ └── Program.cs ├── License.txt ├── Serializers/ │ └── ServiceWire.Serializers/ │ ├── BinaryFormatterSerializer.cs │ └── ServiceWire.Serializers.csproj ├── ServiceWire/ │ ├── Aspects/ │ │ ├── CrossCuttingConcerns.cs │ │ ├── InterceptChannel.cs │ │ ├── InterceptPoint.cs │ │ └── Interceptor.cs │ ├── Channel.cs │ ├── DefaultCompressor.cs │ ├── DefaultSerializer.cs │ ├── DefaultTypeMaker.cs │ ├── Host.cs │ ├── IChannelIdentifier.cs │ ├── ICompressor.cs │ ├── IDvChannel.cs │ ├── ILog.cs │ ├── ISerializer.cs │ ├── IStats.cs │ ├── LogLevel.cs │ ├── LogOptions.cs │ ├── LogRollOptions.cs │ ├── Logger.cs │ ├── LoggerBase.cs │ ├── MemoryDetail.cs │ ├── MessageType.cs │ ├── MethodSyncInfo.cs │ ├── NamedPipes/ │ │ ├── DefaultNamedPipeServerStreamFactory.cs │ │ ├── INamedPipeServerStreamFactory.cs │ │ ├── NpChannel.cs │ │ ├── NpChannelIdentifier.cs │ │ ├── NpClient.cs │ │ ├── NpEndPoint.cs │ │ ├── NpHost.cs │ │ ├── NpListener.cs │ │ ├── NpProxy.cs │ │ ├── PipeClientConnectionEventArgs.cs │ │ ├── ReadFileToStream.cs │ │ └── StreamString.cs │ ├── NetExtensions.cs │ ├── NullLogger.cs │ ├── NullStats.cs │ ├── ParameterTransferHelper.cs │ ├── ParameterTypes.cs │ ├── PooledDictionary.cs │ ├── ProxyBuilder.cs │ ├── ProxyFactory.cs │ ├── SerializationValidator.cs │ ├── ServiceInstance.cs │ ├── ServiceSyncInfo.cs │ ├── ServiceSyncInfoCacheKey.cs │ ├── ServiceWire.csproj │ ├── Stats.cs │ ├── StatsBag.cs │ ├── StreamingChannel.cs │ ├── TcpIp/ │ │ ├── TcpChannel.cs │ │ ├── TcpChannelIdentifier.cs │ │ ├── TcpClient.cs │ │ ├── TcpEndPoint.cs │ │ ├── TcpHost.cs │ │ ├── TcpProxy.cs │ │ └── TcpZkEndPoint.cs │ ├── ZeroKnowledge/ │ │ ├── IZkRepository.cs │ │ ├── ZkBigInt.cs │ │ ├── ZkCrypto.cs │ │ ├── ZkExt.cs │ │ ├── ZkPasswordHash.cs │ │ ├── ZkProtocol.cs │ │ ├── ZkSafePrimes.cs │ │ └── ZkSession.cs │ └── _license.txt ├── ServiceWire.sln ├── ServiceWireTestHostPlusClient/ │ ├── Program.cs │ └── ServiceWireTestHostPlusClient.csproj ├── Tests/ │ ├── Integration/ │ │ ├── ServiceWireTestClient1/ │ │ │ ├── App.config │ │ │ ├── Program.cs │ │ │ └── ServiceWireTestClient1.csproj │ │ ├── ServiceWireTestClient2/ │ │ │ ├── App.config │ │ │ ├── Program.cs │ │ │ └── ServiceWireTestClient2.csproj │ │ └── ServiceWireTestHost/ │ │ ├── App.config │ │ ├── Program.cs │ │ └── ServiceWireTestHost.csproj │ ├── ServiceWireTestCommon/ │ │ ├── ServiceWireTestCommon.csproj │ │ └── TestContracts.cs │ ├── Unit/ │ │ └── ServiceWireTests/ │ │ ├── AsyncTests.cs │ │ ├── INetTester.cs │ │ ├── InterceptionTests.cs │ │ ├── NewtonsoftSerializer.cs │ │ ├── NpTests.cs │ │ ├── ParameterTransferHelperTests.cs │ │ ├── ProtobufSerializer.cs │ │ ├── SequentialCollection.cs │ │ ├── ServiceWireTests.csproj │ │ ├── TcpTests.cs │ │ ├── TcpZkTests.cs │ │ └── ZkProtocolTests.cs │ └── testing_readme.txt └── output.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ # Auto detect text files and perform LF normalization * text=auto # Custom for Visual Studio *.cs diff=csharp *.sln merge=union *.csproj merge=union *.vbproj merge=union *.fsproj merge=union *.dbproj merge=union # Standard to msysgit *.doc diff=astextplain *.DOC diff=astextplain *.docx diff=astextplain *.DOCX diff=astextplain *.dot diff=astextplain *.DOT diff=astextplain *.pdf diff=astextplain *.PDF diff=astextplain *.rtf diff=astextplain *.RTF diff=astextplain ================================================ FILE: .github/workflows/build-and-test.yml ================================================ name: .NET Build and Tests in Linux and Windows on: push: branches: [ "master" ] pull_request: branches: [ "master" ] defaults: run: working-directory: src jobs: build_linux: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: | 6.0.x 8.0.x - name: Restore dependencies run: dotnet restore - name: Build run: dotnet build --no-restore - name: Test run: dotnet test --no-build --verbosity normal build_windows: runs-on: windows-latest steps: - uses: actions/checkout@v4 - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: | 6.0.x 8.0.x - name: Restore dependencies run: dotnet restore - name: Build run: dotnet build --no-restore - name: Test run: dotnet test --no-build --verbosity normal ================================================ FILE: .gitignore ================================================ ################# ## Eclipse ################# *.pydevproject .project .metadata bin/ tmp/ *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath # External tool builders .externalToolBuilders/ # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ################# ## Visual Studio ################# ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.sln.docstates # Build results [Dd]ebug/ [Rr]elease/ *_i.c *_p.c *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.vspscc .builds *.dotCover # NuGet Package Restore enabled packages/ !packages/repositories.config # Visual C++ cache files ipch/ *.aps *.ncb *.opensdf *.sdf # Visual Studio profiler *.psess *.vsp # ReSharper is a .NET coding add-in _ReSharper* # Installshield output folder [Ee]xpress # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish # Others [Bb]in [Oo]bj sql TestResults *.Cache ClientBin stylecop.* ~$* *.dbmdl Generated_Code #added for RIA/Silverlight projects # Backup & report files from converting an old project file to a newer # Visual Studio version. Backup files are not needed, because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML ############ ## Windows ############ # Windows image file caches Thumbs.db # Folder config file Desktop.ini ############# ## Python ############# *.py[co] # Packages *.egg *.egg-info dist build eggs parts bin var sdist develop-eggs .installed.cfg # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox #Translations *.mo #Mr Developer .mr.developer.cfg # Mac crap .DS_Store /src/.vs/config /src/.vs/ProjectEvaluation src/pack/lib/net35 src/pack/lib/net461 /src/.vs/ServiceWire #.VS & VSCode .vs/ .vscode/ /src/Benchmarks/ServiceWire.Benchmarks/BenchmarkDotNet.Artifacts ================================================ FILE: README.md ================================================ [![.NET](https://github.com/tylerjensen/ServiceWire/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/tylerjensen/ServiceWire/actions/workflows/build-and-test.yml) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/tylerjensen) ServiceWire =========== ### A Lightweight Services Library for .NET. ServiceWire is a very fast and light weight services host and dynamic client library that simplifies the development and use of high performance remote procedure call (RPC) communication between .NET processes over Named Pipes or TCP/IP. Find "how to use" examples in the tests code. [ServiceWire documentation][] is available on the wiki. ### Important ServiceWire's dynamically generated proxy will NOT run as x86 on an x64 system. This ususally occurs when you use Visual Studio to create a console application with the default "prefer x86" in project properties selected. Just be sure to choose AnyCPU or the specific target (x86 or x64) so that you do not run 32bit in WOW on an x64 machine. ### Get It on Nuget Get the [NuGet package here][]. ### Using the library is easy. 1. Code your interface 2. Code your implementation 3. Host the implementation 4. Use dynamic proxy of your interface on the client side ### This unique library supports: - TCP and NamedPipes protocols - ByRef (out and ref) parameters (except for non-primitive value types) - Dynamic client proxy generation from service interface - Very fast serialization of most native types and arrays of those types - Multiple service interface hosting on the same endpoint - Aspect oriented interception with pre-, post- and exception handling cross cutting - Hosting of single service implementation singleton on multiple endpoints and protocols - Protocol, serialization and execution strategy extension Portions of this library (dynamic proxy) are a derivative of RemotingLite by Frank Thomsen. [NuGet package here]: http://www.nuget.org/packages/ServiceWire/ [RemotingLite by Frank Thomsen]: https://codeplexarchive.org/codeplex/project/RemotingLite [ServiceWire documentation]: https://github.com/tylerjensen/ServiceWire/wiki ## History ### NamedPipeServerStreamFactory and Other Improvements 5.6.0 1. Contributed fix where accepting TCP clients synchronously may block new clients from being accepted until the terminating request is received on the synchronous client. 1. Contributed NamedPipeServerStreamFactory to allow greater level of permissions control in using named pipes. 1. Introducted injectable ILog and IStats across channels and clients with default NullLogger and NullStats, making InjectLoggerStats obsolete. 1. Code improvements for code consistency and eliminating outdated frameworks from tests and supporting projects. 1. Updated several dependencies in supporting projects. 1. Updated System.Text.Json to 9.0.0 to resolve known vulnerabilities in previous versions. ### Support for Enum by Ref 5.5.4 1. Contributed support for proper async exceptions. ### Support for Enum by Ref 5.5.3 1. Contributed support for Enum by ref parameters. ### Bug Fix for Important Edge Case 5.5.2 1. Contributed fix to case service on a host with same interface was called previously on a different host. ### Replaces BinaryFormatter with System.Text.Json 5.5.0 1. Replaces BinaryFormatter in DefaultSerializer with System.Text.Json. Improves performance and reduces allocations in serializing small object graphs which is the most common use case in any RPC library. 2. Fixes null value in string array bug #50. 3. See source for former DefaultSerializer in ServiceWire.Serializers in BinaryFormatterSerializer. Use that code as a custom injected serializer if this version breaks your serialization. 4. Using ServiceWire in an ASP.NET app no longer requires the use of the EnableUnsafeBinaryFormatterSerialization flag in your project file. ### Capture serialization error bug fix in 5.4.2 1. Single target of NetStandard 2.0 for a smaller NuGet package. 2. Fix to a NamedPipes performance issue. 3. Elimination of NET462 code differences. ### Capture serialization error bug fix in 5.4.1 1. In .NET 5+, the BinaryFormatter is marked obsolete and prohibited in ASP.NET apps. 2. This bug caused an end of stream error rather than capturing it properly. This version fixes that bug and exposes the limitation introduced in .NET 5+ on ASP.NET apps. 3. Using ServiceWire in an ASP.NET app is still possible but requires the use of the [EnableUnsafeBinaryFormatterSerialization](https://docs.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/5.0/binaryformatter-serialization-obsolete) flag in your project file. Use this carefully and be sure you understand the risks. ### ICompression added for injecting compression strategy in 5.4.0 1. Added ICompression for injecting custom compression into usage. 2. Added .NET 5.0 as target back in. ### AssemblyQualified names from user defined types in 5.3.6 1. Fix for AssemblyQualified names from user defined types. ### Multiple Framework Targets and void Return Types 5.3.5 1. Updated all projects to target .net462, .net48, netcoreapp3.1, and net6.0 only. 2. Corrected multiple targets for multiple OS in projects for those using Linux. 3. Updated NuGet package version. ### BugFix + Test cases + 48 1. Throwing the original error through an Intercept would fail for interface methods that have a void return type 2. Updated framework references from .net462 to .net48 ### .NET Framework to .NET Core and Serializer Bug Fixes 5.3.4 1. Support for .NET Framework to .NET Core core parameter types to eliminate exceptions when a Framework client is talking to a Core host or vice versa. 2. Serializer injection bug fixed. ### .NET 4.62 added back in version 5.3.3 1. Added .NET Framework 4.62 build in package to prevent permissions issue in named pipes. 2. Fixed custom serializer issue. 3. .NET Standard 2.0 and 2.1 builds remain. 4. Resolved parallel Zk test issues. Note: Use of async/await and Task not recommended. Use of Task return type not supported. While the syntax of Task return type is supported, apparently it is not marked as Serializable. In fact async/await is not really supported. Under the covers the task type is stripped away over the wire and the method is executed on a worker thread on the server synchronously. If you think about it, you will understand that it's two separate processes, so the Task Parallel Library is not going to be able to manage the thread context across the processes. RPC is inherently synchronous but the handling of each request on the host is done on thread pools. ### .NET Standard 2.0 and 2.1 in version 5.3.2 1. Changed library build to only .NET Standard 2.0 and 2.1. 2. This breaks users of named pipes in .NET 4.6.2 -- DO NOT UPGRADE until we resolve that issue. ### Bug Fixes in version 5.3.1 1. Fixed bug related to complex type serialization that occurred when using output parameters. ### BREAKING CHANGES in version 5.3.0 1. Injectable serialization (see project library tests for examples). 2. Removes dependency on Newtonsoft.Json and uses BinaryFormatter for default serialization which means wire data classes must be marked [Serializable]. 3. Internal classes are attributed to support protobuf-net serialization as well. ### Changes in version 5.2.0 1. Adds support for return types of Task and Task to support async / await across the wire. ### Changes including some breaking changes in version 5.1.0 1. Dropped strong named assembly. 2. Support for NetCoreApp 2.0, 2.2 and .NET Framework 4.62. Dropped support for .NET 3.5. 3. Modified projects and NuGet package generation from Visual Studio 2017. 4. Dropped separate projects used to build different targets. 5. Converted test projects to XUnit with multiple targets to allow "dotnet test" run of all targets. ### Breaking Changes in version 4.0.1 1. Switched ServiceWire (and ServiceMq) to Newtonsoft.Json for serialization. Eliminates use of BinaryFormatter and its required Serializable attribute. Also eliminates ServiceStack.Text 3 dependency which has problems serializing structs. 2. Relaxed assembly version matching to allow additive changes without breaking the client or requiring an immediate client update. 3. Strong name added to allow the library to be used by strong named applications and libraries. 4. Added .NET 3.5 support to allow legacy applications to use the library. This adds a Framework specific dependency on TaskParallelLibrary 1.0.2856.0. 5. For the .NET 4.0 and 3.5 versions, changed to "Client Profile" for the target framework. 6. Removed dependency on System.Numerics in order to support .NET 3.5 and introduced ZkBigInt class taken from Scott Garland's BigInteger class. See license text for full attribution. ### Updated Benchmarks (12/5/2024) with latest contribution NOTE: In this and previous runs of the benchmarks, .NET 8 is consistently 21% faster than .NET 6 when the benchmark differences are averaged. ``` BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4460/23H2/2023Update/SunValley3) AMD Ryzen Threadripper PRO 5975WX 32-Cores, 1 CPU, 64 logical and 32 physical cores .NET SDK 9.0.100 [Host] : .NET 8.0.11 (8.0.1124.51707), X64 RyuJIT AVX2 .NET 6.0 : .NET 6.0.36 (6.0.3624.51421), X64 RyuJIT AVX2 .NET 8.0 : .NET 8.0.11 (8.0.1124.51707), X64 RyuJIT AVX2 | Type | Method | Job | Runtime | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Allocated | Alloc Ratio | |--------------------- |------------- |------------------- |------------------- |-------------:|-----------:|-----------:|------:|--------:|-------:|-------:|----------:|------------:| | ConnectionBenchmarks | TcpConn | .NET 6.0 | .NET 6.0 | 15,400.91 us | 204.477 us | 191.267 us | 0.99 | 0.01 | - | - | 62874 B | 1.00 | | ConnectionBenchmarks | TcpConn | .NET 8.0 | .NET 8.0 | 15,505.96 us | 76.118 us | 71.201 us | 1.00 | 0.01 | - | - | 62893 B | 1.00 | | | | | | | | | | | | | | | | NamedPipesBenchmarks | NpSim | .NET 6.0 | .NET 6.0 | 21.64 us | 0.132 us | 0.110 us | 1.37 | 0.04 | 0.0305 | - | 568 B | 0.89 | | NamedPipesBenchmarks | NpSim | .NET 8.0 | .NET 8.0 | 15.86 us | 0.311 us | 0.415 us | 1.00 | 0.04 | 0.0305 | - | 640 B | 1.00 | | | | | | | | | | | | | | | | TcpBenchmarks | TcpSim | .NET 6.0 | .NET 6.0 | 27.60 us | 0.215 us | 0.201 us | 1.11 | 0.02 | 0.0305 | - | 568 B | 0.89 | | TcpBenchmarks | TcpSim | .NET 8.0 | .NET 8.0 | 24.94 us | 0.484 us | 0.497 us | 1.00 | 0.03 | 0.0305 | - | 640 B | 1.00 | | | | | | | | | | | | | | | | ConnectionBenchmarks | NpConn | .NET 6.0 | .NET 6.0 | 235.46 us | 3.600 us | 3.192 us | 1.16 | 0.03 | 4.3945 | 0.4883 | 68798 B | 1.02 | | ConnectionBenchmarks | NpConn | .NET 8.0 | .NET 8.0 | 203.27 us | 3.865 us | 3.969 us | 1.00 | 0.03 | 4.3945 | 0.4883 | 67196 B | 1.00 | | | | | | | | | | | | | | | | NamedPipesBenchmarks | NpSimJson | .NET 6.0 | .NET 6.0 | 21.44 us | 0.150 us | 0.125 us | 1.35 | 0.02 | 0.0305 | - | 568 B | 0.89 | | NamedPipesBenchmarks | NpSimJson | .NET 8.0 | .NET 8.0 | 15.94 us | 0.306 us | 0.286 us | 1.00 | 0.02 | 0.0305 | - | 640 B | 1.00 | | | | | | | | | | | | | | | | TcpBenchmarks | TcpSimJson | .NET 6.0 | .NET 6.0 | 27.80 us | 0.550 us | 0.564 us | 1.14 | 0.03 | 0.0305 | - | 568 B | 0.89 | | TcpBenchmarks | TcpSimJson | .NET 8.0 | .NET 8.0 | 24.44 us | 0.299 us | 0.280 us | 1.00 | 0.02 | 0.0305 | - | 640 B | 1.00 | | | | | | | | | | | | | | | | NamedPipesBenchmarks | NpRg | .NET 6.0 | .NET 6.0 | 61.25 us | 0.710 us | 0.554 us | 1.38 | 0.03 | 0.8545 | - | 15416 B | 1.05 | | NamedPipesBenchmarks | NpRg | .NET 8.0 | .NET 8.0 | 44.48 us | 0.800 us | 1.122 us | 1.00 | 0.03 | 0.9766 | - | 14737 B | 1.00 | | | | | | | | | | | | | | | | TcpBenchmarks | TcpRg | .NET 6.0 | .NET 6.0 | 72.16 us | 1.325 us | 1.174 us | 1.37 | 0.03 | 0.8545 | - | 15416 B | 1.05 | | TcpBenchmarks | TcpRg | .NET 8.0 | .NET 8.0 | 52.70 us | 0.834 us | 0.739 us | 1.00 | 0.02 | 0.7324 | - | 14737 B | 1.00 | | | | | | | | | | | | | | | | NamedPipesBenchmarks | NpRgJson | .NET 6.0 | .NET 6.0 | 68.63 us | 0.631 us | 0.590 us | 1.39 | 0.04 | 1.7090 | - | 27193 B | 1.09 | | NamedPipesBenchmarks | NpRgJson | .NET 8.0 | .NET 8.0 | 49.40 us | 0.979 us | 1.239 us | 1.00 | 0.03 | 1.4648 | - | 24881 B | 1.00 | | | | | | | | | | | | | | | | TcpBenchmarks | TcpRgJson | .NET 6.0 | .NET 6.0 | 73.17 us | 1.181 us | 0.986 us | 1.38 | 0.03 | 0.8545 | - | 15416 B | 1.05 | | TcpBenchmarks | TcpRgJson | .NET 8.0 | .NET 8.0 | 53.03 us | 0.824 us | 0.771 us | 1.00 | 0.02 | 0.7324 | - | 14737 B | 1.00 | | | | | | | | | | | | | | | | NamedPipesBenchmarks | NpCxOut | .NET 6.0 | .NET 6.0 | 54.18 us | 0.444 us | 0.416 us | 1.25 | 0.02 | 0.3662 | - | 6928 B | 0.86 | | NamedPipesBenchmarks | NpCxOut | .NET 8.0 | .NET 8.0 | 43.36 us | 0.608 us | 0.569 us | 1.00 | 0.02 | 0.3662 | - | 8064 B | 1.00 | | | | | | | | | | | | | | | | TcpBenchmarks | TcpCxOut | .NET 6.0 | .NET 6.0 | 64.20 us | 1.172 us | 1.565 us | 1.36 | 0.04 | 0.3662 | - | 6929 B | 0.86 | | TcpBenchmarks | TcpCxOut | .NET 8.0 | .NET 8.0 | 47.21 us | 0.782 us | 0.732 us | 1.00 | 0.02 | 0.3662 | - | 8064 B | 1.00 | | | | | | | | | | | | | | | | NamedPipesBenchmarks | NpCxOutJson | .NET 6.0 | .NET 6.0 | 58.34 us | 0.904 us | 0.801 us | 1.34 | 0.02 | 0.6104 | - | 10856 B | 0.91 | | NamedPipesBenchmarks | NpCxOutJson | .NET 8.0 | .NET 8.0 | 43.61 us | 0.655 us | 0.581 us | 1.00 | 0.02 | 0.7324 | - | 11888 B | 1.00 | | | | | | | | | | | | | | | | TcpBenchmarks | TcpCxOutJson | .NET 6.0 | .NET 6.0 | 63.73 us | 1.143 us | 1.069 us | 1.34 | 0.03 | 0.3662 | - | 6929 B | 0.86 | | TcpBenchmarks | TcpCxOutJson | .NET 8.0 | .NET 8.0 | 47.49 us | 0.641 us | 0.600 us | 1.00 | 0.02 | 0.3662 | - | 8064 B | 1.00 | ``` ================================================ FILE: src/Benchmarks/ServiceWire.Benchmarks/ConnectionBenchmarks.cs ================================================ using System; using ServiceWire.NamedPipes; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Jobs; using ServiceWire.TcpIp; using System.Net; namespace ServiceWire.Benchmarks { [SimpleJob(RuntimeMoniker.Net80, baseline: true)] [SimpleJob(RuntimeMoniker.Net60)] [SimpleJob(RuntimeMoniker.Net48)] [MemoryDiagnoser] [HtmlExporter] public class ConnectionBenchmarks { private INetTester _tester; private Random _rnd; private readonly string PipeName = "ServiceWireBenchmarkHost"; private IPAddress _ipAddress; private const int Port = 8084; private IPEndPoint CreateTcpEndPoint(int portOffset) { return new IPEndPoint(_ipAddress, Port + portOffset); } private NpEndPoint CreateNpEndPoint(string offset) { return new NpEndPoint(PipeName + offset); } public ConnectionBenchmarks() { _rnd = new Random(); _tester = new NetTester(); _ipAddress = IPAddress.Parse("127.0.0.1"); } [Benchmark] public void TcpConn() { using (var tcpHost = new TcpHost(CreateTcpEndPoint(0))) { tcpHost.AddService(_tester); tcpHost.Open(); using (var tcpClient = new TcpClient(CreateTcpEndPoint(0))) { var a = _rnd.Next(0, 100); var b = _rnd.Next(0, 100); var result = tcpClient.Proxy.Min(a, b); } } } [Benchmark] public void NpConn() { using (var npHost = new NpHost(PipeName)) { npHost.AddService(_tester); npHost.Open(); using (var npClient = new NpClient(CreateNpEndPoint(string.Empty))) { var a = _rnd.Next(0, 100); var b = _rnd.Next(0, 100); var result = npClient.Proxy.Min(a, b); } } } } } ================================================ FILE: src/Benchmarks/ServiceWire.Benchmarks/INetTester.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace ServiceWire.Benchmarks { public interface INetTester { int Min(int a, int b); Dictionary Range(int start, int count); TestResponse Get(Guid id, string label, double weight, out int quantity); Task CalculateAsync(int a, int b); } [Serializable] public struct TestResponse { public Guid Id { get; set; } public string Label { get; set; } public long Quantity { get; set; } public IList Values { get; set; } } public class NetTester : INetTester { public int Min(int a, int b) { return Math.Min(a, b); } public Dictionary Range(int start, int count) { return Enumerable.Range(start, count).ToDictionary(key => key, el => el); } public Task CalculateAsync(int a, int b) { return Task.FromResult(a + b); } public TestResponse Get(Guid id, string label, double weight, out int quantity) { quantity = 44; return new TestResponse { Id = id, Label = "MyLabel", Quantity = quantity, Values = new List { "one", "two", "three", "four" } }; } } } ================================================ FILE: src/Benchmarks/ServiceWire.Benchmarks/NamedPipesBenchmarks.cs ================================================ using System; using ServiceWire.NamedPipes; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Jobs; namespace ServiceWire.Benchmarks { [SimpleJob(RuntimeMoniker.Net80, baseline: true)] [SimpleJob(RuntimeMoniker.Net60)] [SimpleJob(RuntimeMoniker.Net48)] [MemoryDiagnoser] [HtmlExporter] public class NamedPipesBenchmarks { private INetTester _tester; private NpHost _nphost; private NpHost _nphostJson; private NpClient _npClient = null; private NpClient _npClientJson = null; private Random _rnd; private readonly string PipeName = "ServiceWireBenchmarkHost"; private NpEndPoint CreateNpEndPoint(string offset) { return new NpEndPoint(PipeName + offset); } public NamedPipesBenchmarks() { _rnd = new Random(); _tester = new NetTester(); _nphost = new NpHost(PipeName); _nphost.AddService(_tester); _nphost.Open(); _nphostJson = new NpHost(PipeName + "Json", serializer: new NewtonsoftSerializer()); _nphostJson.AddService(_tester); _nphostJson.Open(); } [GlobalSetup] public void GlobalSetup() { _npClient = new NpClient(CreateNpEndPoint(string.Empty)); _npClientJson = new NpClient(CreateNpEndPoint("Json"), new NewtonsoftSerializer()); } [GlobalCleanup] public void GlobalCleanup() { _npClient.Dispose(); _npClientJson.Dispose(); _nphost.Close(); _nphostJson.Close(); } [Benchmark] public void NpSim() { var a = _rnd.Next(0, 100); var b = _rnd.Next(0, 100); var result = _npClient.Proxy.Min(a, b); } [Benchmark] public void NpSimJson() { var a = _rnd.Next(0, 100); var b = _rnd.Next(0, 100); var result = _npClientJson.Proxy.Min(a, b); } [Benchmark] public void NpRg() { var result = _npClient.Proxy.Range(0, 50); for (var i = 0; i < 50; i++) { int temp; result.TryGetValue(i, out temp); } } [Benchmark] public void NpRgJson() { var result = _npClientJson.Proxy.Range(0, 50); for (var i = 0; i < 50; i++) { int temp; result.TryGetValue(i, out temp); } } [Benchmark] public void NpCxOut() { int quantity = 0; var result = _npClient.Proxy.Get(Guid.NewGuid(), "SomeLabel", 45.65, out quantity); } [Benchmark] public void NpCxOutJson() { int quantity = 0; var result = _npClientJson.Proxy.Get(Guid.NewGuid(), "SomeLabel", 45.65, out quantity); } } } ================================================ FILE: src/Benchmarks/ServiceWire.Benchmarks/NewtonSoftSerializer.cs ================================================ using System; using System.Text; using Newtonsoft.Json; namespace ServiceWire.Benchmarks { public class NewtonsoftSerializer : ISerializer { private JsonSerializerSettings settings = new JsonSerializerSettings { ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore }; public T Deserialize(byte[] bytes) { if (null == bytes || bytes.Length == 0) return default(T); var json = Encoding.UTF8.GetString(bytes); return JsonConvert.DeserializeObject(json, settings); } public object Deserialize(byte[] bytes, string typeConfigName) { if (null == typeConfigName) throw new ArgumentNullException(nameof(typeConfigName)); var type = typeConfigName.ToType(); if (null == typeConfigName || null == bytes || bytes.Length == 0) return type.GetDefault(); var json = Encoding.UTF8.GetString(bytes); return JsonConvert.DeserializeObject(json, type, settings); } public byte[] Serialize(T obj) { if (null == obj) return null; var json = JsonConvert.SerializeObject(obj, settings); return Encoding.UTF8.GetBytes(json); } public byte[] Serialize(object obj, string typeConfigName) { if (null == obj) return null; var type = typeConfigName.ToType(); var json = JsonConvert.SerializeObject(obj, type, settings); return Encoding.UTF8.GetBytes(json); } } } ================================================ FILE: src/Benchmarks/ServiceWire.Benchmarks/Program.cs ================================================ using System; using System.Diagnostics; using System.Runtime.InteropServices; using BenchmarkDotNet.Running; namespace ServiceWire.Benchmarks { public class Program { public static void Main(string[] args) { // These can be helpful if you're just trying to work on a single benchmark //BenchmarkRunner.Run(); //BenchmarkRunner.Run(); //BenchmarkRunner.Run(); var switcher = BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly); // Run with command line args if provided if (args.Length > 0) { switcher.Run(args); return; } // Otherwise run them all and combine the results into a single report (command line, HTML) var summary = switcher.RunAllJoined(); // Launching the html report in the browser, makes it nice and easy to see the results string htmlReportPath = System.IO.Path.Combine(summary.ResultsDirectoryPath, $"{summary.Title}-report.html"); OpenUrl(htmlReportPath); } private static void OpenUrl(string url) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { url = url.Replace("&", "^&"); Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { // Detects to see if the user is running in a "desktop environment"/GUI, or if they are running in a terminal session. // Won't be able to launch a web browser without a GUI // https://en.wikipedia.org/wiki/Desktop_environment var currDesktopEnvironment = Environment.GetEnvironmentVariable("XDG_CURRENT_DESKTOP"); if (String.IsNullOrEmpty(currDesktopEnvironment)) { return; } Process.Start("xdg-open", url); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { Process.Start("open", url); } } } } ================================================ FILE: src/Benchmarks/ServiceWire.Benchmarks/ServiceWire.Benchmarks.csproj ================================================  net8.0;net6.0;net48 Exe true ================================================ FILE: src/Benchmarks/ServiceWire.Benchmarks/TcpBenchmarks.cs ================================================ using System; using ServiceWire.TcpIp; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Jobs; using System.Net; namespace ServiceWire.Benchmarks { [SimpleJob(RuntimeMoniker.Net80, baseline: true)] [SimpleJob(RuntimeMoniker.Net60)] [SimpleJob(RuntimeMoniker.Net48)] [MemoryDiagnoser] [HtmlExporter] public class TcpBenchmarks { private INetTester _tester; private Random _rnd; private TcpHost _tcphost; private TcpHost _tcphostJson; private TcpClient _tcpClient; private TcpClient _tcpClientJson; private IPAddress _ipAddress; private const int Port = 8084; private IPEndPoint CreateTcpEndPoint(int portOffset) { return new IPEndPoint(_ipAddress, Port + portOffset); } public TcpBenchmarks() { _rnd = new Random(); _tester = new NetTester(); _ipAddress = IPAddress.Parse("127.0.0.1"); _tcphost = new TcpHost(CreateTcpEndPoint(0)); _tcphost.AddService(_tester); _tcphost.Open(); _tcphostJson = new TcpHost(CreateTcpEndPoint(1)); _tcphostJson.AddService(_tester); _tcphostJson.Open(); } [GlobalSetup] public void GlobalSetup() { _tcpClient = new TcpClient(CreateTcpEndPoint(0)); _tcpClientJson = new TcpClient(CreateTcpEndPoint(1)); } [GlobalCleanup] public void GlobalCleanup() { _tcpClient.Dispose(); _tcpClientJson.Dispose(); _tcphost.Close(); _tcphostJson.Close(); } [Benchmark] public void TcpSim() { var a = _rnd.Next(0, 100); var b = _rnd.Next(0, 100); var result = _tcpClient.Proxy.Min(a, b); } [Benchmark] public void TcpSimJson() { var a = _rnd.Next(0, 100); var b = _rnd.Next(0, 100); var result = _tcpClientJson.Proxy.Min(a, b); } [Benchmark] public void TcpRg() { var result = _tcpClient.Proxy.Range(0, 50); for (var i = 0; i < 50; i++) { int temp; result.TryGetValue(i, out temp); } } [Benchmark] public void TcpRgJson() { var result = _tcpClientJson.Proxy.Range(0, 50); for (var i = 0; i < 50; i++) { int temp; result.TryGetValue(i, out temp); } } [Benchmark] public void TcpCxOut() { int quantity = 0; var result = _tcpClient.Proxy.Get(Guid.NewGuid(), "SomeLabel", 45.65, out quantity); } [Benchmark] public void TcpCxOutJson() { int quantity = 0; var result = _tcpClientJson.Proxy.Get(Guid.NewGuid(), "SomeLabel", 45.65, out quantity); } } } ================================================ FILE: src/Demo/DemoClient/App.config ================================================ ================================================ FILE: src/Demo/DemoClient/DemoClient.csproj ================================================  net6.0;net8.0 net6.0;net8.0;net48 Exe true ================================================ FILE: src/Demo/DemoClient/Program.cs ================================================ using DemoCommon; using ServiceWire; using ServiceWire.TcpIp; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net; using System.Threading.Tasks; namespace DemoClient { class Program { private static async Task Main(string[] args) { var logger = new Logger(logLevel: LogLevel.Debug); var stats = new Stats(); var addr = new[] { "127.0.0.1", "8098" }; //defaults if (null != args && args.Length > 0) { var parts = args[0].Split(':'); if (parts.Length > 1) addr[1] = parts[1]; addr[0] = parts[0]; } var ip = addr[0]; var port = Convert.ToInt32(addr[1]); //var zkEndpoint = new TcpZkEndPoint("username", "password", // new IPEndPoint(IPAddress.Parse(ip), port), connectTimeOutMs: 250000); var zkEndpoint = new IPEndPoint(IPAddress.Parse(ip), port); Console.WriteLine("Iteration 1"); await RunTest(zkEndpoint, ip, logger, stats); Console.WriteLine("Iteration 2"); await RunTest(zkEndpoint, ip, logger, stats); Console.ReadLine(); } private static async Task RunTest(IPEndPoint zkEndpoint, string ip, Logger logger, Stats stats) { var sw = Stopwatch.StartNew(); using (var client = new TcpClient(zkEndpoint)) { client.InjectLoggerStats(logger, stats); await client.Proxy.SetAsync(1); int value = await client.Proxy.GetAsync(); } using (var client = new TcpClient(zkEndpoint)) { client.InjectLoggerStats(logger, stats); decimal abc = client.Proxy.GetDecimal(4.5m); bool result = client.Proxy.OutDecimal(abc); } using (var client = new TcpClient(zkEndpoint)) { client.InjectLoggerStats(logger, stats); var id = client.Proxy.GetId("test1", 3.314, 42, DateTime.Now); long q = 3; var response = client.Proxy.Get(id, "mirror", 4.123, out q); var list = client.Proxy.GetItems(id); } using (var client = new TcpClient(zkEndpoint)) { client.InjectLoggerStats(logger, stats); var response = client.Proxy.GetData(); Console.WriteLine(response.First()); } Console.WriteLine("elapsed ms: {0}", sw.ElapsedMilliseconds); } private static async Task RunTest(TcpZkEndPoint zkEndpoint, string ip, Logger logger, Stats stats) { var sw = Stopwatch.StartNew(); using (var client = new TcpClient(zkEndpoint)) { client.InjectLoggerStats(logger, stats); await client.Proxy.SetAsync(1); int value = await client.Proxy.GetAsync(); } using (var client = new TcpClient(zkEndpoint)) { client.InjectLoggerStats(logger, stats); decimal abc = client.Proxy.GetDecimal(4.5m); bool result = client.Proxy.OutDecimal(abc); } using (var client = new TcpClient(zkEndpoint)) { client.InjectLoggerStats(logger, stats); var id = client.Proxy.GetId("test1", 3.314, 42, DateTime.Now); long q = 3; var response = client.Proxy.Get(id, "mirror", 4.123, out q); var list = client.Proxy.GetItems(id); } using (var client = new TcpClient(zkEndpoint)) { client.InjectLoggerStats(logger, stats); var response = client.Proxy.GetData(); Console.WriteLine(response.First()); } Console.WriteLine("elapsed ms: {0}", sw.ElapsedMilliseconds); } } } ================================================ FILE: src/Demo/DemoCommon/Contracts.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Tasks; namespace DemoCommon { public interface ITest { Task SetAsync(int a); Task GetAsync(); } public interface IDataContract { decimal GetDecimal(decimal input); bool OutDecimal(decimal val); } public interface IIPCBridge { List GetData(); } public interface IComplexDataContract { Guid GetId(string source, double weight, int quantity, DateTime dt); ComplexResponse Get(Guid id, string label, double weight, out long quantity); long TestLong(out long id1, out long id2); List GetItems(Guid id); } [Serializable] public struct ComplexResponse { public Guid Id { get; set; } public string Label { get; set; } public long Quantity { get; set; } } } ================================================ FILE: src/Demo/DemoCommon/DemoCommon.csproj ================================================  net6.0;net8.0 net6.0;net8.0;net48 ================================================ FILE: src/Demo/DemoHost/App.config ================================================ ================================================ FILE: src/Demo/DemoHost/DemoHost.csproj ================================================  net6.0;net8.0 net6.0;net8.0;net48 Exe true ================================================ FILE: src/Demo/DemoHost/Program.cs ================================================ using DemoCommon; using ServiceWire; using ServiceWire.TcpIp; using ServiceWire.ZeroKnowledge; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; namespace DemoHost { class Program { static void Main(string[] args) { var logger = new Logger(logLevel: LogLevel.Debug); var stats = new Stats(); var addr = new[] { "127.0.0.1", "8098" }; //defaults if (null != args && args.Length > 0) { var parts = args[0].Split(':'); if (parts.Length > 1) addr[1] = parts[1]; addr[0] = parts[0]; } var ip = addr[0]; var port = Convert.ToInt32(addr[1]); var ipEndpoint = new IPEndPoint(IPAddress.Any, port); var useCompression = false; var compressionThreshold = 131072; //128KB var zkRepository = new DemoZkRepository(); //var tcphost = new TcpHost(ipEndpoint, logger, stats, zkRepository); var tcphost = new TcpHost(ipEndpoint, logger, stats); tcphost.UseCompression = useCompression; tcphost.CompressionThreshold = compressionThreshold; var simpleContract = new DataContractImpl(); tcphost.AddService(simpleContract); var complexContract = new ComplexDataContractImpl(); tcphost.AddService(complexContract); var bridge = new IPCBridge(); tcphost.AddService(bridge); var test = new Test(); tcphost.AddService(test); tcphost.Open(); Console.WriteLine("Press Enter to stop the dual host test."); Console.ReadLine(); tcphost.Close(); Console.WriteLine("Press Enter to quit."); Console.ReadLine(); } } public class IPCBridge : IIPCBridge { public List GetData() { List list = new List(); list.Add(99.625F); return list; } } public class Test : ITest { public Task SetAsync(int a) { return Task.CompletedTask; } public Task GetAsync() { return Task.FromResult(1); } } public class DataContractImpl : IDataContract { public decimal GetDecimal(decimal input) { return input += 456.44m; } public bool OutDecimal(decimal val) { val = 45.66m; return true; } } public class ComplexDataContractImpl : IComplexDataContract { public Guid GetId(string source, double weight, int quantity, DateTime dt) { return Guid.NewGuid(); } public ComplexResponse Get(Guid id, string label, double weight, out long quantity) { quantity = 42; return new ComplexResponse { Id = id, Label = "Hello, world.", Quantity = quantity }; } public List GetItems(Guid id) { var list = new List(); list.Add("42"); list.Add(id.ToString()); list.Add("Test"); return list; } public long TestLong(out long id1, out long id2) { id1 = 23; id2 = 24; return 25; } } public class DemoZkRepository : IZkRepository { private string password = "password"; private ZkProtocol _protocol = new ZkProtocol(); private ZkPasswordHash _hash = null; public ZkPasswordHash GetPasswordHashSet(string username) { if (_hash == null) _hash = _protocol.HashCredentials(username, password); return _hash; } } } ================================================ FILE: src/License.txt ================================================ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * COPYRIGHT NOTICES AND OPEN SOURCE LICENSES * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Title: ServiceWire Description: Very fast and light weight services host and dynamic client library that simplifies the development and use of high performance remote procedure call (RPC) communication between .NET processes over Named Pipes or TCP/IP. Copyright (C) Tyler Jensen Author: Tyler Jensen Source: https://github.com/tylerjensen/ServiceWire Licensed 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. This notice, including the DERIVITIVE WORK NOTCE below, must be included in compiled and source form of this work whenever distributed. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * NAME CHANGE NOTICE The code in this work was originally published under the name DuoVia.Net. This work is a continuation of that project under the same copyright holder and author. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * DERIVATIVE WORK NOTICE Some of the source code in this work is derived from RemotingLite version 1.2.5 available at http://remotinglite.codeplex.com. The author of RemotingLite provides no endorsement, explicitly or implied, of this work. The original source is published under the following license terms: New BSD License (BSD) Copyright (c) 2008, Frank Thomsen All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Sector 0 nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. In addition, the following license information was part of the original source files but has been moved here for code brevity: RemotingLite ------ A light framework for making remote method invocations using TCP/IP. It is based loosely on Windows Communication Foundation, and is meant to provide programmers with the same API regardless of whether they write software for the Microsoft .NET platform or the Mono .NET platform. Consult the documentation and example applications for information about how to use this API. Author : Frank Thomsen http : http://sector0.dk Concact : http://sector0.dk/?q=contact Information : http://sector0.dk/?q=node/27 Licence : Free. If you use this, please let me know. Please feel free to contact me with ideas, bugs or improvements. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * DERIVATIVE WORK NOTICE This work includes a private branch of Scott Garland's BigInteger renamed as ZkBigInt. The MIT License (MIT) Copyright (c) 2007 Scott Garland Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: src/Serializers/ServiceWire.Serializers/BinaryFormatterSerializer.cs ================================================ using System; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; namespace ServiceWire.Serializers { public class BinaryFormatterSerializer : ISerializer { private readonly IFormatter _formatter = new BinaryFormatter(); public byte[] Serialize(T obj) { if (null == obj) return null; using (var ms = new MemoryStream()) { _formatter.Serialize(ms, obj); return ms.ToArray(); } } public byte[] Serialize(object obj, string typeConfigName) { if (null == obj) return null; using (var ms = new MemoryStream()) { var type = typeConfigName.ToType(); var objT = Convert.ChangeType(obj, type); _formatter.Serialize(ms, objT); return ms.ToArray(); } } public T Deserialize(byte[] bytes) { if (null == bytes || bytes.Length == 0) return default(T); using (var ms = new MemoryStream(bytes)) { return (T)_formatter.Deserialize(ms); } } public object Deserialize(byte[] bytes, string typeConfigName) { if (null == typeConfigName) throw new ArgumentNullException(nameof(typeConfigName)); var type = typeConfigName.ToType(); if (null == typeConfigName || null == bytes || bytes.Length == 0) return type.GetDefault(); using (var ms = new MemoryStream(bytes)) { var obj = _formatter.Deserialize(ms); return Convert.ChangeType(obj, type); } } } } ================================================ FILE: src/Serializers/ServiceWire.Serializers/ServiceWire.Serializers.csproj ================================================ netstandard2.0 ================================================ FILE: src/ServiceWire/Aspects/CrossCuttingConcerns.cs ================================================ using System; namespace ServiceWire.Aspects { public class CrossCuttingConcerns { /// /// Takes instanceId, methodName and method parameters as object array. /// public Action PreInvoke { get; set; } /// /// Takes instanceId, methodName, method parameters as object array, and exception thrown. Return true to call throw and raise exception. /// public Func ExceptionHandler { get; set; } /// /// Takes instanceId, methodName and object array returned by execution of method. /// public Action PostInvoke { get; set; } } } ================================================ FILE: src/ServiceWire/Aspects/InterceptChannel.cs ================================================ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Reflection; namespace ServiceWire.Aspects { public class InterceptChannel : Channel { private InterceptPoint _interceptPoint; private ServiceInstance _serviceInstance; public InterceptPoint InterceptPoint { get { return _interceptPoint; } } public InterceptChannel(Type interceptedType, InterceptPoint interceptPoint, ISerializer serializer, ICompressor compressor, ILog logger = null, IStats stats = null) : base(serializer, compressor, logger, stats) { _serviceType = interceptedType; _interceptPoint = interceptPoint; CreateMethodMap(); } protected override void SyncInterface(Type interceptedType, string username = null, string password = null) { //do nothing in this channel } /// /// Loads all methods from interfaces and assigns an identifier /// to each. These are later synchronized with the client. /// private void CreateMethodMap() { _serviceInstance = new ServiceInstance() { KeyIndex = 0, //only one per intercepted interface InterfaceType = _serviceType, InterfaceMethods = new ConcurrentDictionary(), MethodParametersByRef = new ConcurrentDictionary(), SingletonInstance = _interceptPoint.Target }; var currentMethodIdent = 0; if (_serviceType.IsInterface) { var methodInfos = _serviceType.GetMethods(); foreach (var mi in methodInfos) { _serviceInstance.InterfaceMethods.TryAdd(currentMethodIdent, mi); var parameterInfos = mi.GetParameters(); var isByRef = new bool[parameterInfos.Length]; for (int i = 0; i < isByRef.Length; i++) isByRef[i] = parameterInfos[i].ParameterType.IsByRef; _serviceInstance.MethodParametersByRef.TryAdd(currentMethodIdent, isByRef); currentMethodIdent++; } } var interfaces = _serviceType.GetInterfaces(); foreach (var interfaceType in interfaces) { var methodInfos = interfaceType.GetMethods(); foreach (var mi in methodInfos) { _serviceInstance.InterfaceMethods.TryAdd(currentMethodIdent, mi); var parameterInfos = mi.GetParameters(); var isByRef = new bool[parameterInfos.Length]; for (int i = 0; i < isByRef.Length; i++) isByRef[i] = parameterInfos[i].ParameterType.IsByRef; _serviceInstance.MethodParametersByRef.TryAdd(currentMethodIdent, isByRef); currentMethodIdent++; } } //Create a list of sync infos from the dictionary var syncSyncInfos = new List(); foreach (var kvp in _serviceInstance.InterfaceMethods) { var parameters = kvp.Value.GetParameters(); var parameterTypes = new string[parameters.Length]; for (var i = 0; i < parameters.Length; i++) parameterTypes[i] = parameters[i].ParameterType.ToConfigName(); syncSyncInfos.Add(new MethodSyncInfo { MethodIdent = kvp.Key, MethodName = kvp.Value.Name, MethodReturnType = kvp.Value.ReturnType.ToConfigName(), ParameterTypes = parameterTypes }); } var serviceSyncInfo = new ServiceSyncInfo { ServiceKeyIndex = 0, CompressionThreshold = 131072, UseCompression = false, MethodInfos = syncSyncInfos.ToArray() }; _serviceInstance.ServiceSyncInfo = serviceSyncInfo; } protected override object[] InvokeMethod(string metaData, params object[] parameters) { object[] returnParameters = null; Type returnType = null; try { var mdata = metaData.Split('|'); var ident = -1; for (int index = 0; index < _serviceInstance.ServiceSyncInfo.MethodInfos.Length; index++) { var si = _serviceInstance.ServiceSyncInfo.MethodInfos[index]; //first of all the method names must match if (si.MethodName == mdata[0]) { //second of all the parameter types and -count must match if (mdata.Length - 1 == si.ParameterTypes.Length) { var matchingParameterTypes = true; for (int i = 0; i < si.ParameterTypes.Length; i++) { if (!mdata[i + 1].Equals(si.ParameterTypes[i])) { matchingParameterTypes = false; break; } } if (matchingParameterTypes) { ident = si.MethodIdent; break; } } } } if (ident < 0) throw new Exception(string.Format("Cannot match method '{0}' to its implementation.", mdata[0])); if (_serviceInstance.InterfaceMethods.ContainsKey(ident)) { MethodInfo method; _serviceInstance.InterfaceMethods.TryGetValue(ident, out method); bool[] isByRef; _serviceInstance.MethodParametersByRef.TryGetValue(ident, out isByRef); returnType = (null == method) ? null : method.ReturnType; //invoke the method try { if (null != _interceptPoint.Cut && null != _interceptPoint.Cut.PreInvoke) { _interceptPoint.Cut.PreInvoke(_interceptPoint.Id, mdata[0], parameters); } object returnValue = method.Invoke(_serviceInstance.SingletonInstance, parameters); //the result to the client is the return value (null if void) and the input parameters returnParameters = new object[1 + parameters.Length]; returnParameters[0] = returnValue; for (int i = 0; i < parameters.Length; i++) returnParameters[i + 1] = isByRef[i] ? parameters[i] : null; } catch (Exception ex) { Exception exceptionOfConcern = ex; if (exceptionOfConcern is TargetInvocationException && null != exceptionOfConcern.InnerException) { exceptionOfConcern = exceptionOfConcern.InnerException; } bool shouldThrow = true; if (null != _interceptPoint.Cut && null != _interceptPoint.Cut.ExceptionHandler) { shouldThrow = _interceptPoint.Cut.ExceptionHandler(_interceptPoint.Id, mdata[0], parameters, exceptionOfConcern); } if (shouldThrow) { returnParameters = new object[] { exceptionOfConcern }; throw exceptionOfConcern; } else { returnParameters = new object[] { returnType == typeof(void)? null: returnType.GetDefault() }; } } finally { if (null != _interceptPoint.Cut && null != _interceptPoint.Cut.PostInvoke) { _interceptPoint.Cut.PostInvoke(_interceptPoint.Id, mdata[0], returnParameters); } } return returnParameters; } throw new Exception(string.Format("Cannot match method '{0}' to its implementation.", mdata[0])); } catch { //log? throw; } } protected override void Dispose(bool disposing) { if (null != _interceptPoint && null != _interceptPoint.Target && _interceptPoint.Target is IDisposable) { ((IDisposable)_interceptPoint.Target).Dispose(); } } } } ================================================ FILE: src/ServiceWire/Aspects/InterceptPoint.cs ================================================ namespace ServiceWire.Aspects { public class InterceptPoint { public int Id { get; set; } public object Target { get; set; } public CrossCuttingConcerns Cut { get; set; } } } ================================================ FILE: src/ServiceWire/Aspects/Interceptor.cs ================================================ using System; namespace ServiceWire.Aspects { public static class Interceptor { public static TTarget Intercept(TTarget target, CrossCuttingConcerns crossCuttingConcerns, ISerializer serializer = null, ICompressor compressor = null, ILog logger = null, IStats stats = null) where TTarget : class { return Intercept(0, target, crossCuttingConcerns, serializer, compressor, logger, stats); } public static TTarget Intercept(int id, TTarget target, CrossCuttingConcerns crossCuttingConcerns, ISerializer serializer = null, ICompressor compressor = null, ILog logger = null, IStats stats = null) where TTarget : class { if (!typeof(TTarget).IsInterface) throw new ArgumentException("TTarget not an interface"); if (null == target) throw new ArgumentNullException("target"); if (null == serializer) serializer = new DefaultSerializer(); if (null == compressor) compressor = new DefaultCompressor(); TTarget interceptedTarget = ProxyFactory.CreateProxy(typeof(InterceptChannel), typeof(InterceptPoint), new InterceptPoint { Id = id, Target = target, Cut = crossCuttingConcerns }, serializer, compressor, logger, stats); return interceptedTarget; } } } ================================================ FILE: src/ServiceWire/Channel.cs ================================================ using System; namespace ServiceWire { public abstract class Channel : IDisposable { protected Type _serviceType; protected ILog _logger; protected IStats _stats; internal readonly ISerializer _serializer; internal readonly ICompressor _compressor; public Channel(ISerializer serializer, ICompressor compressor, ILog logger = null, IStats stats = null) { _serializer = serializer ?? new DefaultSerializer(); _compressor = compressor ?? new DefaultCompressor(); _logger = logger ?? new NullLogger(); _stats = stats ?? new NullStats(); } [Obsolete] public void InjectLoggerStats(ILog logger, IStats stats) { _logger = logger; _stats = stats; } /// /// Invokes the method with the specified parameters. /// /// Parameters for the method call /// An array of objects containing the return value (index 0) and the parameters used to call /// the method, including any marked as "ref" or "out" protected abstract object[] InvokeMethod(string metaData, params object[] parameters); /// /// Channel must implement an interface synchronization method. /// This method asks the server for a list of identifiers paired with method /// names and -parameter types. This is used when invoking methods server side. /// When username and password supplied, zero knowledge encryption is used. /// protected abstract void SyncInterface(Type serviceType, string username = null, string password = null); #region IDisposable Members protected bool _disposed = false; public void Dispose() { //MS recommended dispose pattern - prevents GC from disposing again Dispose(true); GC.SuppressFinalize(this); } protected abstract void Dispose(bool disposing); #endregion } } ================================================ FILE: src/ServiceWire/DefaultCompressor.cs ================================================ using System.IO; using System.IO.Compression; namespace ServiceWire { public class DefaultCompressor : ICompressor { public byte[] Compress(byte[] data) { using (var msCompressed = new MemoryStream()) { using (var msObj = new MemoryStream(data)) { using (GZipStream gzs = new GZipStream(msCompressed, CompressionMode.Compress)) { msObj.CopyTo(gzs); } } return msCompressed.ToArray(); } } public byte[] DeCompress(byte[] compressedBytes) { using (var msObj = new MemoryStream()) { using (var msCompressed = new MemoryStream(compressedBytes)) using (var gzs = new GZipStream(msCompressed, CompressionMode.Decompress)) { gzs.CopyTo(msObj); } msObj.Seek(0, SeekOrigin.Begin); return msObj.ToArray(); } } } } ================================================ FILE: src/ServiceWire/DefaultSerializer.cs ================================================ using System; using System.IO; using System.Text.Json; namespace ServiceWire { public class DefaultSerializer : ISerializer { public byte[] Serialize(T obj) { if (null == obj) return null; return JsonSerializer.SerializeToUtf8Bytes(obj); } public byte[] Serialize(object obj, string typeConfigName) { if (null == obj) return null; return JsonSerializer.SerializeToUtf8Bytes(obj, typeConfigName.ToType()); } public T Deserialize(byte[] bytes) { if (null == bytes || bytes.Length == 0) return default(T); return JsonSerializer.Deserialize(bytes); } public object Deserialize(byte[] bytes, string typeConfigName) { if (null == typeConfigName) throw new ArgumentNullException(nameof(typeConfigName)); var type = typeConfigName.ToType(); if (null == typeConfigName || null == bytes || bytes.Length == 0) return type.GetDefault(); return JsonSerializer.Deserialize(bytes, typeConfigName.ToType()); } } } ================================================ FILE: src/ServiceWire/DefaultTypeMaker.cs ================================================ using System; namespace ServiceWire { internal class DefaultTypeMaker { public object GetDefault(Type t) { return this.GetType().GetMethod("GetDefaultGeneric").MakeGenericMethod(t).Invoke(this, null); } public T GetDefaultGeneric() { return default(T); } } } ================================================ FILE: src/ServiceWire/Host.cs ================================================ using ServiceWire.ZeroKnowledge; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Threading.Tasks; namespace ServiceWire { public abstract class Host : IDisposable { protected volatile bool _isOpen; protected volatile bool _continueListening = true; protected bool _useCompression = false; //default is false protected int _compressionThreshold = 131072; //128KB protected ILog _log = new NullLogger(); protected IStats _stats = new NullStats(); protected readonly ISerializer _serializer; protected readonly ICompressor _compressor; protected IZkRepository _zkRepository = new ZkNullRepository(); private volatile bool _requireZk = false; protected ConcurrentDictionary _serviceKeys = new ConcurrentDictionary(); protected ConcurrentDictionary _services = new ConcurrentDictionary(); protected readonly ParameterTransferHelper _parameterTransferHelper; public Host(ISerializer serializer, ICompressor compressor) { _serializer = serializer ?? new DefaultSerializer(); _compressor = compressor ?? new DefaultCompressor(); _parameterTransferHelper = new ParameterTransferHelper(_serializer, _compressor); } public IZkRepository ZkRepository { get { return _zkRepository; } set { _zkRepository = value; if (_zkRepository is ZkNullRepository) _requireZk = false; else _requireZk = true; } } public IStats Stats { get { return _stats; } set { _stats = value ?? _stats; } } public ILog Log { get { return _log; } set { _log = value ?? _log; } } protected bool Continue { get { return _continueListening; } set { _continueListening = value; } } /// /// Enable parameter compression. Default is false. There is a performance penalty /// when using compression that should be weighed against network transmission /// costs of large data parameters being serialized across the wire. /// public bool UseCompression { get { return _useCompression; } set { _useCompression = value; } } /// /// Compression, if enabled, occurs once a parameter exceeds this value /// in the number of bytes. Strings, byte and char arrays, and complex serialized types. /// The minimum is 1024 bytes. The default is 128KB. /// public int CompressionThreshold { get { return _compressionThreshold; } set { _compressionThreshold = value; if (_compressionThreshold < 1024) _compressionThreshold = 1024; } } /// /// Add this service implementation to the host. /// /// /// The singleton implementation. public void AddService(TService service) where TService : class { try { if (_isOpen) throw new Exception("Service cannot be added after the host is opened."); var serviceType = typeof(TService); if (!serviceType.IsInterface) throw new ArgumentException("TService must be an interface.", "TService"); //serviceType.ValidateServiceInterface(); //throws if one class in the interface or its members is not serializable var serviceKey = serviceType.ToConfigName(); // serviceType.AssemblyQualifiedName ?? serviceType.FullName; if (_serviceKeys.ContainsKey(serviceKey)) throw new Exception("Service already added. Only one instance allowed."); int keyIndex = _serviceKeys.Count; _serviceKeys.TryAdd(serviceKey, keyIndex); var instance = CreateMethodMap(keyIndex, serviceType, service); _services.TryAdd(keyIndex, instance); } catch (Exception e) { _log.Fatal("AddServive exception on {0}. Error: {1}", service.GetType(), e.ToString().Flatten()); throw; } } /// /// Loads all methods from interfaces and assigns an identifier /// to each. These are later synchronized with the client. /// private ServiceInstance CreateMethodMap(int keyIndex, Type serviceType, object service) { var instance = new ServiceInstance() { KeyIndex = keyIndex, InterfaceType = serviceType, InterfaceMethods = new ConcurrentDictionary(), MethodParametersByRef = new ConcurrentDictionary(), SingletonInstance = service }; var currentMethodIdent = 0; if (serviceType.IsInterface) { var methodInfos = serviceType.GetMethods(); foreach (var mi in methodInfos) { instance.InterfaceMethods.TryAdd(currentMethodIdent, mi); var parameterInfos = mi.GetParameters(); var isByRef = new bool[parameterInfos.Length]; for (int i = 0; i < isByRef.Length; i++) isByRef[i] = parameterInfos[i].ParameterType.IsByRef; instance.MethodParametersByRef.TryAdd(currentMethodIdent, isByRef); currentMethodIdent++; } } var interfaces = serviceType.GetInterfaces(); foreach (var interfaceType in interfaces) { var methodInfos = interfaceType.GetMethods(); foreach (var mi in methodInfos) { instance.InterfaceMethods.TryAdd(currentMethodIdent, mi); var parameterInfos = mi.GetParameters(); var isByRef = new bool[parameterInfos.Length]; for (int i = 0; i < isByRef.Length; i++) isByRef[i] = parameterInfos[i].ParameterType.IsByRef; instance.MethodParametersByRef.TryAdd(currentMethodIdent, isByRef); currentMethodIdent++; } } //Create a list of sync infos from the dictionary var syncSyncInfos = new List(); foreach (var kvp in instance.InterfaceMethods) { var parameters = kvp.Value.GetParameters(); var parameterTypes = new string[parameters.Length]; for (var i = 0; i < parameters.Length; i++) parameterTypes[i] = parameters[i].ParameterType.ToConfigName(); syncSyncInfos.Add(new MethodSyncInfo { MethodIdent = kvp.Key, MethodName = kvp.Value.Name, MethodReturnType = kvp.Value.ReturnType.ToConfigName(), ParameterTypes = parameterTypes }); } var serviceSyncInfo = new ServiceSyncInfo { ServiceKeyIndex = keyIndex, CompressionThreshold = _compressionThreshold, UseCompression = _useCompression, MethodInfos = syncSyncInfos.ToArray() }; instance.ServiceSyncInfo = serviceSyncInfo; return instance; } /// /// Opens the host and starts a listener. This listener spawns a new thread (or uses a /// thread pool thread) for each incoming connection. /// public void Open() { _isOpen = true; StartListener(); } protected abstract void StartListener(); /// /// Closes the host and calls Dispose(). /// public void Close() { Dispose(); } protected void ProcessRequest(Stream stream) { if (null == stream || (!stream.CanWrite && !stream.CanRead)) { _log.Error("Cannot process a request on a stream that is not read/write."); return; } ProcessRequest(stream, stream); } /// /// This method handles all requests from a single client. /// There is one thread running this method for each connected client. /// /// The read/write stream. /// The read/write stream. protected virtual void ProcessRequest(Stream readStream, Stream writeStream) { if (null == readStream || null == writeStream) return; var binReader = new BinaryReader(readStream); var binWriter = new BinaryWriter(writeStream); bool doContinue = true; try { ZkSession zkSession = null; do { var sw = Stopwatch.StartNew(); try { //read message type var messageType = (MessageType)binReader.ReadInt32(); switch (messageType) { case MessageType.ZkInitiate: zkSession = new ZkSession(_zkRepository, _log, _stats); doContinue = zkSession.ProcessZkInitiation(binReader, binWriter, sw); break; case MessageType.ZkProof: if (null == zkSession) throw new NullReferenceException("session null"); doContinue = zkSession.ProcessZkProof(binReader, binWriter, sw); break; case MessageType.SyncInterface: ProcessSync(zkSession, binReader, binWriter, sw); break; case MessageType.MethodInvocation: ProcessInvocation(zkSession, binReader, binWriter, sw); break; case MessageType.TerminateConnection: doContinue = false; break; default: doContinue = false; break; } } catch (Exception e) //do not resume operation on this thread if any errors are unhandled. { _log.Error("Error in ProcessRequest: {0}", e.ToString().Flatten()); doContinue = false; } sw.Stop(); } while (doContinue); } catch (Exception fatalException) { _log.Fatal("Fatal error in ProcessRequest: {0}", fatalException.ToString().Flatten()); } finally { binReader.Close(); binWriter.Close(); } } private void ProcessSync(ZkSession session, BinaryReader binReader, BinaryWriter binWriter, Stopwatch sw) { var syncCat = "Sync"; string serviceTypeName; if (_requireZk) { //use session and encryption - if throws should not have gotten this far var len = binReader.ReadInt32(); var bytes = binReader.ReadBytes(len); var data = session.Crypto.Decrypt(bytes); serviceTypeName = data.ConverToString(); } else { serviceTypeName = binReader.ReadString(); } int serviceKey; if (_serviceKeys.TryGetValue(serviceTypeName, out serviceKey)) { ServiceInstance instance; if (_services.TryGetValue(serviceKey, out instance)) { syncCat = instance.InterfaceType.Name; //Create a list of sync infos from the dictionary byte[] syncBytes = null; try { //if the serializer fails, we need to send 0 to client to indicate sync error syncBytes = _serializer.Serialize(instance.ServiceSyncInfo); } catch (Exception e) { //return zero to indicate failure to client to avoid EOS error on client binWriter.Write(0); _log.Debug("SyncInterface error {0}.", e); } if (null != syncBytes) { if (_requireZk) { _log.Debug("Unencrypted data sent to server: {0}", Convert.ToBase64String(syncBytes)); var encData = session.Crypto.Encrypt(syncBytes); binWriter.Write(encData.Length); binWriter.Write(encData); _log.Debug("Encrypted data sent server: {0}", Convert.ToBase64String(encData)); } else { binWriter.Write(syncBytes.Length); binWriter.Write(syncBytes); } } } } else { //return zero to indicate type or version of type not found binWriter.Write(0); } binWriter.Flush(); _log.Debug("SyncInterface for {0} in {1}ms.", syncCat, sw.ElapsedMilliseconds); } private void ProcessInvocation(ZkSession session, BinaryReader binReader, BinaryWriter binWriter, Stopwatch sw) { //read service instance key var cat = "unknown"; var stat = "MethodInvocation"; int invokedServiceKey = binReader.ReadInt32(); ServiceInstance invokedInstance; if (_services.TryGetValue(invokedServiceKey, out invokedInstance)) { cat = invokedInstance.InterfaceType.Name; //read the method identifier int methodHashCode = binReader.ReadInt32(); if (invokedInstance.InterfaceMethods.ContainsKey(methodHashCode)) { MethodInfo method; invokedInstance.InterfaceMethods.TryGetValue(methodHashCode, out method); stat = method.Name; bool[] isByRef; invokedInstance.MethodParametersByRef.TryGetValue(methodHashCode, out isByRef); //read parameter data object[] parameters; if (_requireZk) { var len = binReader.ReadInt32(); var encData = binReader.ReadBytes(len); _log.Debug("Encrypted data received from server: {0}", Convert.ToBase64String(encData)); var data = session.Crypto.Decrypt(encData); _log.Debug("Decrypted data received from server: {0}", Convert.ToBase64String(data)); using (var ms = new MemoryStream(data)) using (var br = new BinaryReader(ms)) { parameters = _parameterTransferHelper.ReceiveParameters(br); } } else { parameters = _parameterTransferHelper.ReceiveParameters(binReader); } //invoke the method object[] returnParameters; var returnMessageType = MessageType.ReturnValues; try { object returnValue = method.Invoke(invokedInstance.SingletonInstance, parameters); if (returnValue is Task task) { task.GetAwaiter().GetResult(); var prop = task.GetType().GetProperty("Result"); returnValue = prop?.GetValue(task); } //the result to the client is the return value (null if void) and the input parameters returnParameters = new object[1 + parameters.Length]; returnParameters[0] = returnValue; for (int i = 0; i < parameters.Length; i++) returnParameters[i + 1] = isByRef[i] ? parameters[i] : null; } catch (Exception ex) { //an exception was caught. Rethrow it client side returnParameters = new object[] { (ex is TargetInvocationException && ex.InnerException != null) ? ex.InnerException : ex }; returnMessageType = MessageType.ThrowException; } //send the result back to the client // (1) write the message type binWriter.Write((int)returnMessageType); // (2) write the return parameters if (_requireZk) { byte[] data; using (var ms = new MemoryStream()) using (var bw = new BinaryWriter(ms)) { _parameterTransferHelper.SendParameters( invokedInstance.ServiceSyncInfo.UseCompression, invokedInstance.ServiceSyncInfo.CompressionThreshold, bw, returnParameters); data = ms.ToArray(); } _log.Debug("Unencrypted data sent server: {0}", Convert.ToBase64String(data)); var encData = session.Crypto.Encrypt(data); _log.Debug("Encrypted data sent server: {0}", Convert.ToBase64String(encData)); binWriter.Write(encData.Length); binWriter.Write(encData); } else { _parameterTransferHelper.SendParameters( invokedInstance.ServiceSyncInfo.UseCompression, invokedInstance.ServiceSyncInfo.CompressionThreshold, binWriter, returnParameters); } } else { binWriter.Write((int)MessageType.UnknownMethod); } } else { binWriter.Write((int)MessageType.UnknownMethod); } //flush binWriter.Flush(); _stats.Log(cat, stat, sw.ElapsedMilliseconds); } #region IDisposable Members private bool _disposed = false; public void Dispose() { //MS recommended dispose pattern - prevents GC from disposing again Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (_disposed) return; _disposed = true; //prevent second call to Dispose if (disposing) { if (_log is Logger log) log.FlushLog(); if (_stats is Stats stat) stat.FlushLog(); _isOpen = false; Continue = false; foreach (var instance in _services) { if (instance.Value.SingletonInstance is IDisposable disposable) disposable.Dispose(); } } } #endregion } } ================================================ FILE: src/ServiceWire/IChannelIdentifier.cs ================================================ namespace ServiceWire { public interface IChannelIdentifier { } } ================================================ FILE: src/ServiceWire/ICompressor.cs ================================================ namespace ServiceWire { public interface ICompressor { byte[] Compress(byte[] data); byte[] DeCompress(byte[] compressedBytes); } } ================================================ FILE: src/ServiceWire/IDvChannel.cs ================================================ namespace ServiceWire { /// /// The channel behavior exposed on the client level. /// public interface IDvChannel { /// /// Returns true if client is connected to the server. /// bool IsConnected { get; } } } ================================================ FILE: src/ServiceWire/ILog.cs ================================================ namespace ServiceWire { public interface ILog { void Debug(string formattedMessage, params object[] args); void Info(string formattedMessage, params object[] args); void Warn(string formattedMessage, params object[] args); void Error(string formattedMessage, params object[] args); void Fatal(string formattedMessage, params object[] args); } } ================================================ FILE: src/ServiceWire/ISerializer.cs ================================================ namespace ServiceWire { public interface ISerializer { byte[] Serialize(T obj); byte[] Serialize(object obj, string typeConfigName); T Deserialize(byte[] bytes); object Deserialize(byte[] bytes, string typeConfigName); } } ================================================ FILE: src/ServiceWire/IStats.cs ================================================ namespace ServiceWire { public interface IStats { void Log(string name, float value); void Log(string category, string name, float value); } } ================================================ FILE: src/ServiceWire/LogLevel.cs ================================================ namespace ServiceWire { public enum LogLevel { None = 0, Fatal = 1, Error = 2, Warn = 3, Info = 4, Debug = 5 } } ================================================ FILE: src/ServiceWire/LogOptions.cs ================================================ namespace ServiceWire { public enum LogOptions { LogOnlyToFile, LogOnlyToConsole, LogToBoth } } ================================================ FILE: src/ServiceWire/LogRollOptions.cs ================================================ namespace ServiceWire { public enum LogRollOptions { Daily, Hourly, Size } } ================================================ FILE: src/ServiceWire/Logger.cs ================================================ using System.IO; using System.Reflection; using System.Threading.Tasks; namespace ServiceWire { public class Logger : LoggerBase, ILog { private LogLevel _logLevel = LogLevel.Error; /// /// Set to Debug for all logging on. To None for no logging. /// Order is: None, Fatal, Error, Warn, Info, Debug /// public LogLevel LogLevel { get { return _logLevel; } set { _logLevel = value; } } private const string LogFilePrefixDefault = "log-"; private const string LogFileExtensionDefault = ".txt"; public Logger(string logDirectory = null, string logFilePrefix = null, string logFileExtension = null, LogLevel logLevel = LogLevel.Error, int messageBufferSize = 32, LogOptions options = LogOptions.LogOnlyToFile, LogRollOptions rollOptions = LogRollOptions.Daily, int rollMaxMegaBytes = 1024, bool useUtcTimeStamp = false) { _logDirectory = logDirectory ?? Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "logs"); Directory.CreateDirectory(_logDirectory); //will throw if unable - does not throw if already exists _logFilePrefix = logFilePrefix ?? LogFilePrefixDefault; _logFileExtension = logFileExtension ?? LogFileExtensionDefault; _logLevel = logLevel; _messageBufferSize = messageBufferSize; _rollOptions = rollOptions; _rollMaxMegaBytes = rollMaxMegaBytes; _useUtcTimeStamp = useUtcTimeStamp; LogOptions = options; //setter validates if (_messageBufferSize < 1) _messageBufferSize = 1; if (_messageBufferSize > 4096) _messageBufferSize = 4096; if (_rollOptions == LogRollOptions.Size) { if (_rollMaxMegaBytes < 1) _rollMaxMegaBytes = 1; if (_rollMaxMegaBytes < 4096) _rollMaxMegaBytes = 4096; } } private void WriteMessage(LogLevel logLevel, string formattedMessage, params object[] args) { if (null == formattedMessage) return; //do nothing if ((int) logLevel <= (int) _logLevel) { string msg = (null != args && args.Length > 0) ? string.Format(formattedMessage, args) : formattedMessage; _logQueue.Enqueue(new string[] { string.Format("{0}\t{1}\t{2}", GetTimeStamp(), logLevel, msg) }); if (_logQueue.Count >= _messageBufferSize) Task.Factory.StartNew(() => WriteBuffer(_messageBufferSize)); } } public void Debug(string formattedMessage, params object[] args) { WriteMessage(LogLevel.Debug, formattedMessage, args); } public void Info(string formattedMessage, params object[] args) { WriteMessage(LogLevel.Info, formattedMessage, args); } public void Warn(string formattedMessage, params object[] args) { WriteMessage(LogLevel.Warn, formattedMessage, args); } public void Error(string formattedMessage, params object[] args) { WriteMessage(LogLevel.Error, formattedMessage, args); } public void Fatal(string formattedMessage, params object[] args) { WriteMessage(LogLevel.Fatal, formattedMessage, args); } } } ================================================ FILE: src/ServiceWire/LoggerBase.cs ================================================ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; namespace ServiceWire { public abstract class LoggerBase { protected object _syncRoot = new object(); protected string _logDirectory = null; protected string _logFilePrefix = null; protected string _logFileExtension = null; protected bool _useUtcTimeStamp = false; protected ConcurrentQueue _logQueue = new ConcurrentQueue(); protected LogOptions _options = LogOptions.LogOnlyToFile; protected LogRollOptions _rollOptions = LogRollOptions.Daily; protected string _logFileNameFormat = null; protected int _messageBufferSize = 32; protected int _rollMaxMegaBytes = 1024; public LogOptions LogOptions { get { return _options; } set { _options = value; if (_options != LogOptions.LogOnlyToConsole) { if (!_logFileExtension.StartsWith(".")) _logFileExtension = "." + _logFileExtension; _logFileNameFormat = Path.Combine(_logDirectory, _logFilePrefix + "{0}" + _logFileExtension); } } } public virtual void FlushLog() { WriteBuffer(int.MaxValue); } protected const string TimeStampPattern = "yyyy-MM-ddTHH:mm:ss.fff"; protected string GetTimeStamp() { return _useUtcTimeStamp ? DateTime.UtcNow.ToString(TimeStampPattern) : DateTime.Now.ToString(TimeStampPattern); } protected string GetTimeStamp(DateTime dt) { return _useUtcTimeStamp ? dt.ToUniversalTime().ToString(TimeStampPattern) : dt.ToString(TimeStampPattern); } protected void WriteBuffer(int count) { var list = new List(); for (int i = 0; i < count; i++) { string[] msg; _logQueue.TryDequeue(out msg); if (null == msg) break; list.AddRange(msg); } if (list.Count == 0) return; //nothing to log var lines = list.ToArray(); if (_options == LogOptions.LogOnlyToConsole || _options == LogOptions.LogToBoth) { Console.Write(lines); } if (_options == LogOptions.LogOnlyToFile || _options == LogOptions.LogToBoth) { WriteToFile(lines); } } private void WriteToFile(string[] lines) { lock (_syncRoot) { try { var fileName = GetFileName(); File.AppendAllLines(fileName, lines); } catch { //todo ? } } } private string _currentFileName = null; private int _currentFileOffset = -1; private int _currentWritesCount = 0; private string GetFileName() { //by size if (_rollOptions == LogRollOptions.Size) { if (_currentFileOffset < 0) _currentFileOffset = GetCurrentFileOffset(); if (null == _currentFileName) { _currentFileName = GetSizeFileName(_currentFileOffset); return _currentFileName; } //should we check size? if (_currentWritesCount * _messageBufferSize >= 3200) //100 writes at 32 per { _currentWritesCount = 0; //reset var fi = new FileInfo(_currentFileName); if (fi.Length > _rollMaxMegaBytes * 1024 * 1024) { _currentFileOffset++; _currentFileName = GetSizeFileName(_currentFileOffset); } } return _currentFileName; } //based on roll options if (_rollOptions == LogRollOptions.Hourly) { if (_useUtcTimeStamp) return string.Format(_logFileNameFormat, DateTime.UtcNow.ToString(ToDateHourPattern)); return string.Format(_logFileNameFormat, DateTime.Now.ToString(ToDateHourPattern)); } if (_useUtcTimeStamp) return string.Format(_logFileNameFormat, DateTime.UtcNow.ToString(ToDateOnlyPattern)); return string.Format(_logFileNameFormat, DateTime.Now.ToString(ToDateOnlyPattern)); } private const string ToDateOnlyPattern = "yyyyMMdd"; private const string ToDateHourPattern = "yyyyMMdd-HH"; private int GetCurrentFileOffset() { return Directory.GetFiles(_logDirectory, _logFilePrefix + "*").Length; } private const string FileNameFormatSizePattern = "00000000"; private string GetSizeFileName(int offset) { return string.Format(_logFileNameFormat, offset.ToString(FileNameFormatSizePattern)); } } } ================================================ FILE: src/ServiceWire/MemoryDetail.cs ================================================ using System; namespace ServiceWire { public class MemoryDetail { public ulong TotalVisibleMemorySize { get; set; } public ulong TotalVirtualMemorySize { get; set; } public ulong FreePhysicalMemory { get; set; } public ulong FreeVirtualMemory { get; set; } } } ================================================ FILE: src/ServiceWire/MessageType.cs ================================================ namespace ServiceWire { public enum MessageType { TerminateConnection = 0, MethodInvocation = 1, ReturnValues = 2, UnknownMethod = 3, ThrowException = 4, SyncInterface = 5, ZkInitiate = 20, ZkProof = 21 }; } ================================================ FILE: src/ServiceWire/MethodSyncInfo.cs ================================================ using System; using System.Runtime.Serialization; namespace ServiceWire { [Serializable, DataContract] public class MethodSyncInfo { [DataMember(Order = 1)] public int MethodIdent { get; set; } [DataMember(Order = 2)] public string MethodName { get; set; } [DataMember(Order = 3)] public string MethodReturnType { get; set; } [DataMember(Order = 4)] public string[] ParameterTypes { get; set; } } } ================================================ FILE: src/ServiceWire/NamedPipes/DefaultNamedPipeServerStreamFactory.cs ================================================ using System.IO.Pipes; namespace ServiceWire.NamedPipes { public class DefaultNamedPipeServerStreamFactory : INamedPipeServerStreamFactory { public NamedPipeServerStream Create(string pipeName, PipeDirection direction, int maxNumberOfServerInstances, PipeTransmissionMode transmissionMode, PipeOptions options, int inBufferSize, int outBufferSize) { return new NamedPipeServerStream(pipeName, direction, maxNumberOfServerInstances, transmissionMode, options, inBufferSize, outBufferSize); } } } ================================================ FILE: src/ServiceWire/NamedPipes/INamedPipeServerStreamFactory.cs ================================================ using System.IO.Pipes; namespace ServiceWire.NamedPipes { public interface INamedPipeServerStreamFactory { NamedPipeServerStream Create(string pipeName, PipeDirection direction, int maxNumberOfServerInstances, PipeTransmissionMode transmissionMode, PipeOptions options, int inBufferSize, int outBufferSize); } } ================================================ FILE: src/ServiceWire/NamedPipes/NpChannel.cs ================================================ using System; using System.IO; using System.IO.Pipes; namespace ServiceWire.NamedPipes { public class NpChannel : StreamingChannel { private readonly NamedPipeClientStream _clientStream; private readonly NpChannelIdentifier _channelIdentifier; /// /// Creates a connection to the concrete object handling method calls on the pipeName server side /// /// /// /// Inject your own serializer for complex objects and avoid using the Newtonsoft JSON DefaultSerializer. public NpChannel(Type serviceType, NpEndPoint npEndPoint, ISerializer serializer, ICompressor compressor, ILog logger = null, IStats stats = null) : base(serializer, compressor, logger, stats) { _serviceType = serviceType; _channelIdentifier = new NpChannelIdentifier(npEndPoint); _clientStream = new NamedPipeClientStream(npEndPoint.ServerName, npEndPoint.PipeName, PipeDirection.InOut); _clientStream.Connect(npEndPoint.ConnectTimeOutMs); _stream = new BufferedStream(_clientStream); _binReader = new BinaryReader(_clientStream); _binWriter = new BinaryWriter(_clientStream); try { SyncInterface(_serviceType); } catch (Exception) { this.Dispose(true); throw; } } protected override IChannelIdentifier ChannelIdentifier => _channelIdentifier; public override bool IsConnected { get { return (null != _clientStream) && _clientStream.IsConnected; } } } } ================================================ FILE: src/ServiceWire/NamedPipes/NpChannelIdentifier.cs ================================================ using System; namespace ServiceWire.NamedPipes { internal class NpChannelIdentifier : IChannelIdentifier, IEquatable { public string ServerName { get; private set; } public string PipeName { get; private set; } public NpChannelIdentifier(NpEndPoint npEndPoint) { ServerName = npEndPoint.ServerName; PipeName = npEndPoint.PipeName; } public bool Equals(NpChannelIdentifier other) { return ServerName == other.ServerName && PipeName == other.PipeName; } public override bool Equals(object obj) { if (obj is NpChannelIdentifier other) { return Equals(other); } return false; } public override int GetHashCode() { unchecked { return ServerName.GetHashCode() + PipeName.GetHashCode(); } } } } ================================================ FILE: src/ServiceWire/NamedPipes/NpClient.cs ================================================ using System; namespace ServiceWire.NamedPipes { public class NpClient : IDisposable where TInterface : class { private readonly TInterface _proxy; public TInterface Proxy { get { return _proxy; } } public bool IsConnected { get { return (_proxy != null) && (_proxy as NpChannel).IsConnected; } } /// /// Create a named pipes client. /// /// /// Inject your own serializer for complex objects and avoid using the Newtonsoft JSON DefaultSerializer. /// Inject your own compressor and avoid using the standard GZIP DefaultCompressor. public NpClient(NpEndPoint npAddress, ISerializer serializer = null, ICompressor compressor = null, ILog logger = null, IStats stats = null) { if (null == serializer) serializer = new DefaultSerializer(); if (null == compressor) compressor = new DefaultCompressor(); if (null == logger) logger = new NullLogger(); if (null == stats) stats = new NullStats(); _proxy = NpProxy.CreateProxy(npAddress, serializer, compressor, logger, stats); } #region IDisposable Members private bool _disposed = false; public void Dispose() { //MS recommended dispose pattern - prevents GC from disposing again Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!_disposed) { _disposed = true; //prevent second call to Dispose if (disposing) { (_proxy as NpChannel).Dispose(); } } } #endregion } } ================================================ FILE: src/ServiceWire/NamedPipes/NpEndPoint.cs ================================================ namespace ServiceWire.NamedPipes { public class NpEndPoint { public NpEndPoint(string pipeName, int connectTimeOutMs = 2500) : this(".", pipeName, connectTimeOutMs) { } public NpEndPoint(string serverName, string pipeName, int connectTimeOutMs = 2500) { this.ServerName = serverName; this.PipeName = pipeName; this.ConnectTimeOutMs = connectTimeOutMs; } public string ServerName { get; private set; } public string PipeName { get; private set; } public int ConnectTimeOutMs { get; private set; } } } ================================================ FILE: src/ServiceWire/NamedPipes/NpHost.cs ================================================ using System; using System.IO; namespace ServiceWire.NamedPipes { public class NpHost : Host { private readonly NpListener _listener; private readonly string _pipeName; private bool _useThreadPool = false; /// /// Get or set whether the host should use regular or thread pool threads. /// public bool UseThreadPool { get { return _useThreadPool; } set { if (_isOpen) throw new Exception("The host is already open"); _useThreadPool = value; } } /// /// Constructs an instance of the host and starts listening for incoming connections. /// All listener threads are regular background threads. /// /// NOTE: the instance is not automatically thread safe! /// /// The pipe name for incoming requests /// /// /// Inject your own serializer for complex objects and avoid using the Newtonsoft JSON DefaultSerializer. /// Inject your own compressor and avoid using the standard GZIP DefaultCompressor. /// Inject your own factory for creating the named pipe server. Can be used with NamedPipeServerStreamAcl to set the ACL on Windows platforms. public NpHost(string pipeName, ILog log = null, IStats stats = null, ISerializer serializer = null, ICompressor compressor = null, INamedPipeServerStreamFactory streamFactory = null) : base(serializer, compressor) { base.Log = log; base.Stats = stats; _pipeName = pipeName; _listener = new NpListener(_pipeName, log: base.Log, stats: base.Stats, streamFactory: streamFactory); _listener.RequestReieved += ClientConnectionMade; } /// /// Gets the end point this host is listening on /// public string PipeName { get { return _pipeName; } } protected override void StartListener() { _listener.Start(); //start listening in the background } /// /// This method handles all requests on separate thread per client connection. /// There is one thread running this method for each connected client. /// /// /// private void ClientConnectionMade(object sender, PipeClientConnectionEventArgs args) { var stream = new BufferedStream(args.PipeStream); base.ProcessRequest(stream); } #region IDisposable Members private bool _disposed = false; protected override void Dispose(bool disposing) { if (!_disposed) { _disposed = true; //prevent second call to Dispose if (disposing) { _listener.Stop(); } } } #endregion } } ================================================ FILE: src/ServiceWire/NamedPipes/NpListener.cs ================================================ using System; using System.IO.Pipes; using System.Threading; using System.Threading.Tasks; namespace ServiceWire.NamedPipes { public class NpListener { private bool _running; private readonly EventWaitHandle _terminateHandle = new EventWaitHandle(false, EventResetMode.AutoReset); private readonly int _maxConnections = 254; private readonly ILog _log; private readonly IStats _stats; private readonly INamedPipeServerStreamFactory _streamFactory; public string PipeName { get; set; } public event EventHandler RequestReieved; public NpListener(string pipeName, int maxConnections = 254, ILog log = null, IStats stats = null, INamedPipeServerStreamFactory streamFactory = null) { _log = log ?? new NullLogger(); _stats = stats ?? new NullStats(); if (maxConnections > 254) maxConnections = 254; _maxConnections = maxConnections; this.PipeName = pipeName; _streamFactory = streamFactory ?? new DefaultNamedPipeServerStreamFactory(); } public void Start() { _running = true; Task.Factory.StartNew(() => ServerLoop(), TaskCreationOptions.LongRunning); } public void Stop() { if (_running) { _running = false; //make fake connection to terminate the waiting stream try { using (var client = new NamedPipeClientStream(PipeName)) { client.Connect(50); } } catch (Exception e) { _log.Error("Stop error: {0}", e.ToString().Flatten()); } _terminateHandle.WaitOne(); } } private void ServerLoop() { try { while (_running) { ProcessNextClient(); } } catch (Exception e) { _log.Fatal("ServerLoop fatal error: {0}", e.ToString().Flatten()); } finally { _terminateHandle.Set(); } } private void ProcessClientThread(NamedPipeServerStream pipeStream) { try { if (this.RequestReieved != null) //has event subscribers { var args = new PipeClientConnectionEventArgs(pipeStream); RequestReieved(this, args); } } catch (Exception e) { _log.Error("ProcessClientThread error: {0}", e.ToString().Flatten()); } finally { if (pipeStream.IsConnected) pipeStream.Close(); pipeStream.Dispose(); } } public void ProcessNextClient() { try { var pipeStream = _streamFactory.Create(PipeName, PipeDirection.InOut, _maxConnections, PipeTransmissionMode.Byte, PipeOptions.None, 512, 512); pipeStream.WaitForConnection(); //Task.Factory.StartNew(() => ProcessClientThread(pipeStream), TaskCreationOptions.LongRunning); Task.Factory.StartNew(() => ProcessClientThread(pipeStream)); } catch (Exception e) { //If there are no more avail connections (254 is in use already) then just keep looping until one is avail _log.Error("ProcessNextClient error: {0}", e.ToString().Flatten()); } } } // Defines the data protocol for reading and writing strings on our stream // Contains the method executed in the context of the impersonated user } ================================================ FILE: src/ServiceWire/NamedPipes/NpProxy.cs ================================================ namespace ServiceWire.NamedPipes { public class NpProxy { public static TInterface CreateProxy(NpEndPoint npAddress, ISerializer serializer, ICompressor compressor, ILog logger, IStats stats) where TInterface : class { return ProxyFactory.CreateProxy(typeof(NpChannel), typeof(NpEndPoint), npAddress, serializer, compressor, logger, stats); } } } ================================================ FILE: src/ServiceWire/NamedPipes/PipeClientConnectionEventArgs.cs ================================================ using System; using System.IO.Pipes; namespace ServiceWire.NamedPipes { public class PipeClientConnectionEventArgs : EventArgs { public PipeClientConnectionEventArgs(NamedPipeServerStream pipeStream) { this.PipeStream = pipeStream; } public NamedPipeServerStream PipeStream { get; private set; } } } ================================================ FILE: src/ServiceWire/NamedPipes/ReadFileToStream.cs ================================================ using System.IO; namespace ServiceWire.NamedPipes { public class ReadFileToStream { private readonly string _fileName; private readonly StreamString _streamString; public ReadFileToStream(StreamString str, string filename) { _fileName = filename; _streamString = str; } public void Start() { _streamString.WriteString(File.ReadAllText(_fileName)); } } } ================================================ FILE: src/ServiceWire/NamedPipes/StreamString.cs ================================================ using System; using System.IO; using System.Text; namespace ServiceWire.NamedPipes { public class StreamString { private readonly Stream _ioStream; private readonly UnicodeEncoding _streamEncoding; public StreamString(Stream ioStream) { this._ioStream = ioStream; _streamEncoding = new UnicodeEncoding(); } public string ReadString() { int len = 0; len = _ioStream.ReadByte() * 256; len += _ioStream.ReadByte(); byte[] inBuffer = new byte[len]; _ioStream.Read(inBuffer, 0, len); return _streamEncoding.GetString(inBuffer); } public int WriteString(string outString) { byte[] outBuffer = _streamEncoding.GetBytes(outString); int len = outBuffer.Length; if (len > UInt16.MaxValue) { len = (int)UInt16.MaxValue; } _ioStream.WriteByte((byte)(len / 256)); _ioStream.WriteByte((byte)(len & 255)); _ioStream.Write(outBuffer, 0, len); _ioStream.Flush(); return outBuffer.Length + 2; } } } ================================================ FILE: src/ServiceWire/NetExtensions.cs ================================================ using System; using System.Text.RegularExpressions; namespace ServiceWire { public static class NetExtensions { private const string _netFwCoreLib = "mscorlib"; private const string _netCoreCoreLib = "System.Private.CoreLib"; public static string ToConfigName(this Type t) { // Do not qualify types from mscorlib/System.Private.CoreLib otherwise calling between process running with different frameworks won't work // i.e. "System.String, mscorlib" (.NET FW) != "System.String, System.Private.CoreLib" (.NET CORE) var name = t.Assembly.GetName().Name == _netFwCoreLib || t.Assembly.GetName().Name == _netCoreCoreLib ? t.FullName : t.AssemblyQualifiedName; // But since an mscorlib generic container can contain fully qualified types we always need to clean up the name name = Regex.Replace(name, @", Version=\d+.\d+.\d+.\d+", string.Empty); name = Regex.Replace(name, @", Culture=\w+", string.Empty); name = Regex.Replace(name, @", PublicKeyToken=\w+", string.Empty); name = name.Replace(", " + _netFwCoreLib, string.Empty); name = name.Replace(", " + _netCoreCoreLib, string.Empty); return name; } public static Type ToType(this string configName) { try { var result = Type.GetType(configName); return result; } catch (Exception e) { Console.WriteLine(e); } return null; } /// /// Returns true if Type inherits from baseType. /// /// The Type extended by this method. /// The base type to find in the inheritance hierarchy. /// True if baseType is found. False if not. public static bool InheritsFrom(this Type t, Type baseType) { Type cur = t.BaseType; while (cur != null) { if (cur.Equals(baseType)) return true; cur = cur.BaseType; } return false; } public static object GetDefault(this Type t) { var tm = new DefaultTypeMaker(); return tm.GetDefault(t); } public static string Flatten(this string src) { return src.Replace("\r", ":").Replace("\n", ":"); } } } ================================================ FILE: src/ServiceWire/NullLogger.cs ================================================ namespace ServiceWire { internal class NullLogger : ILog { public void Debug(string formattedMessage, params object[] args) { } public void Info(string formattedMessage, params object[] args) { } public void Warn(string formattedMessage, params object[] args) { } public void Error(string formattedMessage, params object[] args) { } public void Fatal(string formattedMessage, params object[] args) { } } } ================================================ FILE: src/ServiceWire/NullStats.cs ================================================ namespace ServiceWire { internal class NullStats : IStats { public void Log(string name, float value) { } public void Log(string category, string name, float value) { } public void LogSys() { } } } ================================================ FILE: src/ServiceWire/ParameterTransferHelper.cs ================================================ using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Text; namespace ServiceWire { public sealed class ParameterTransferHelper { [ThreadStatic] private static Dictionary _parameterTypes; private const string NULL_STRING = "\u2811\u16D8\u270C"; //3 chars from 3 different unicode sets private readonly ISerializer _serializer; private readonly ICompressor _compressor; public ParameterTransferHelper(ISerializer serializer, ICompressor compressor) { _serializer = serializer ?? new DefaultSerializer(); _compressor = compressor ?? new DefaultCompressor(); } public void SendParameters(bool useCompression, int compressionThreshold, BinaryWriter writer, params object[] parameters) { //write how many parameters are coming writer.Write(parameters.Length); //write data for each parameter foreach (object parameter in parameters) { if (parameter == null) { writer.Write(ParameterTypes.Null); } else { Type type = parameter.GetType(); byte typeByte = GetParameterType(type); byte[] dataBytes = null; switch (typeByte) { case ParameterTypes.ByteArray: dataBytes = (byte[])parameter; break; case ParameterTypes.Unknown: dataBytes = _serializer.Serialize(parameter, type.ToConfigName()); break; } if (useCompression) { //check for compressable values and compress if required switch (typeByte) { case ParameterTypes.ByteArray: if (dataBytes.LongLength > compressionThreshold) { typeByte = ParameterTypes.CompressedByteArray; dataBytes = _compressor.Compress(dataBytes); } break; case ParameterTypes.CharArray: char[] charArray = (char[])parameter; if (charArray.LongLength > compressionThreshold) { typeByte = ParameterTypes.CompressedCharArray; dataBytes = _compressor.Compress(Encoding.UTF8.GetBytes(charArray)); } break; case ParameterTypes.String: if (((string)parameter).Length > compressionThreshold) { typeByte = ParameterTypes.CompressedString; dataBytes = _compressor.Compress(Encoding.UTF8.GetBytes(((string)parameter))); } break; case ParameterTypes.ArrayString: var array = (string[])parameter; var total = (from n in array select n.Length).Sum(); if (total > compressionThreshold) { typeByte = ParameterTypes.Unknown; dataBytes = _compressor.Compress(_serializer.Serialize(array, type.ToConfigName())); } break; case ParameterTypes.Unknown: if (dataBytes.Length > compressionThreshold) { typeByte = ParameterTypes.CompressedUnknown; dataBytes = _compressor.Compress(dataBytes); } break; } } //write the type byte writer.Write(typeByte); //write the parameter switch (typeByte) { case ParameterTypes.Bool: writer.Write((bool)parameter); break; case ParameterTypes.Byte: writer.Write((byte)parameter); break; case ParameterTypes.Char: writer.Write((char)parameter); break; case ParameterTypes.CharArray: char[] charArray = (char[])parameter; writer.Write(charArray.Length); writer.Write(charArray); break; case ParameterTypes.Decimal: writer.Write((decimal)parameter); break; case ParameterTypes.Double: writer.Write((double)parameter); break; case ParameterTypes.Float: writer.Write((float)parameter); break; case ParameterTypes.Int: writer.Write((int)parameter); break; case ParameterTypes.Long: writer.Write((long)parameter); break; case ParameterTypes.SByte: writer.Write((sbyte)parameter); break; case ParameterTypes.Short: writer.Write((short)parameter); break; case ParameterTypes.String: writer.Write((string)parameter); break; case ParameterTypes.UInt: writer.Write((uint)parameter); break; case ParameterTypes.ULong: writer.Write((ulong)parameter); break; case ParameterTypes.UShort: writer.Write((ushort)parameter); break; case ParameterTypes.Type: writer.Write(type.ToConfigName()); break; case ParameterTypes.Guid: writer.Write(((Guid)parameter).ToByteArray()); break; case ParameterTypes.DateTime: writer.Write(((DateTime)parameter).ToString("o")); break; case ParameterTypes.ArrayBool: var bools = (bool[])parameter; writer.Write(bools.Length); foreach (var b in bools) writer.Write(b); break; case ParameterTypes.ArraySByte: var sbytes = (sbyte[])parameter; writer.Write(sbytes.Length); foreach (var sb in sbytes) writer.Write(sb); break; case ParameterTypes.ArrayDecimal: var decs = (decimal[])parameter; writer.Write(decs.Length); foreach (var d in decs) writer.Write(d); break; case ParameterTypes.ArrayDouble: var dbls = (double[])parameter; writer.Write(dbls.Length); foreach (var db in dbls) writer.Write(db); break; case ParameterTypes.ArrayFloat: var fls = (float[])parameter; writer.Write(fls.Length); foreach (var f in fls) writer.Write(f); break; case ParameterTypes.ArrayInt: var ints = (int[])parameter; writer.Write(ints.Length); foreach (var i in ints) writer.Write(i); break; case ParameterTypes.ArrayUInt: var uints = (uint[])parameter; writer.Write(uints.Length); foreach (var u in uints) writer.Write(u); break; case ParameterTypes.ArrayLong: var longs = (long[])parameter; writer.Write(longs.Length); foreach (var lg in longs) writer.Write(lg); break; case ParameterTypes.ArrayULong: var ulongs = (ulong[])parameter; writer.Write(ulongs.Length); foreach (var ul in ulongs) writer.Write(ul); break; case ParameterTypes.ArrayShort: var shorts = (short[])parameter; writer.Write(shorts.Length); foreach (var s in shorts) writer.Write(s); break; case ParameterTypes.ArrayUShort: var ushorts = (ushort[])parameter; writer.Write(ushorts.Length); foreach (var us in ushorts) writer.Write(us); break; case ParameterTypes.ArrayString: var strings = (string[])parameter; writer.Write(strings.Length); foreach (var st in strings) writer.Write(st ?? NULL_STRING); break; case ParameterTypes.ArrayType: var types = (Type[])parameter; writer.Write(types.Length); foreach (var t in types) writer.Write(t.ToConfigName()); break; case ParameterTypes.ArrayGuid: var guids = (Guid[])parameter; writer.Write(guids.Length); foreach (var g in guids) writer.Write(g.ToByteArray()); break; case ParameterTypes.ArrayDateTime: var dts = (DateTime[])parameter; writer.Write(dts.Length); foreach (var dt in dts) writer.Write(dt.ToString("o")); break; case ParameterTypes.ByteArray: case ParameterTypes.CompressedByteArray: case ParameterTypes.CompressedCharArray: case ParameterTypes.CompressedString: //write length of data writer.Write(dataBytes.Length); //write data writer.Write(dataBytes); break; case ParameterTypes.Unknown: case ParameterTypes.CompressedUnknown: //write type name as string writer.Write(type.ToConfigName()); //write length of data writer.Write(dataBytes.Length); //write data writer.Write(dataBytes); break; default: throw new Exception(string.Format("Unknown type byte '0x{0:X}'", typeByte)); } } } } public object[] ReceiveParameters(BinaryReader reader) { int parameterCount = reader.ReadInt32(); object[] parameters = new object[parameterCount]; for (int i = 0; i < parameterCount; i++) { //read type byte byte typeByte = reader.ReadByte(); if (typeByte == ParameterTypes.Null) { parameters[i] = null; } else { switch (typeByte) { case ParameterTypes.Bool: parameters[i] = reader.ReadBoolean(); break; case ParameterTypes.Byte: parameters[i] = reader.ReadByte(); break; case ParameterTypes.ByteArray: parameters[i] = reader.ReadBytes(reader.ReadInt32()); break; case ParameterTypes.CompressedByteArray: parameters[i] = _compressor.DeCompress(reader.ReadBytes(reader.ReadInt32())); break; case ParameterTypes.Char: parameters[i] = reader.ReadChar(); break; case ParameterTypes.CharArray: parameters[i] = reader.ReadChars(reader.ReadInt32()); break; case ParameterTypes.CompressedCharArray: var ccBytes = _compressor.DeCompress(reader.ReadBytes(reader.ReadInt32())); parameters[i] = Encoding.UTF8.GetChars(ccBytes); break; case ParameterTypes.Decimal: parameters[i] = reader.ReadDecimal(); break; case ParameterTypes.Double: parameters[i] = reader.ReadDouble(); break; case ParameterTypes.Float: parameters[i] = reader.ReadSingle(); break; case ParameterTypes.Int: parameters[i] = reader.ReadInt32(); break; case ParameterTypes.Long: parameters[i] = reader.ReadInt64(); break; case ParameterTypes.SByte: parameters[i] = reader.ReadSByte(); break; case ParameterTypes.Short: parameters[i] = reader.ReadInt16(); break; case ParameterTypes.String: parameters[i] = reader.ReadString(); break; case ParameterTypes.CompressedString: var csBytes = _compressor.DeCompress(reader.ReadBytes(reader.ReadInt32())); parameters[i] = Encoding.UTF8.GetString(csBytes); break; case ParameterTypes.UInt: parameters[i] = reader.ReadUInt32(); break; case ParameterTypes.ULong: parameters[i] = reader.ReadUInt64(); break; case ParameterTypes.UShort: parameters[i] = reader.ReadUInt16(); break; case ParameterTypes.Type: var typeName = reader.ReadString(); parameters[i] = Type.GetType(typeName); break; case ParameterTypes.Guid: parameters[i] = new Guid(reader.ReadBytes(16)); break; case ParameterTypes.DateTime: var dtstr = reader.ReadString(); parameters[i] = DateTime.Parse(dtstr, null, DateTimeStyles.RoundtripKind); break; case ParameterTypes.ArrayBool: var blen = reader.ReadInt32(); var bs = new bool[blen]; for (int x = 0; x < blen; x++) bs[x] = reader.ReadBoolean(); parameters[i] = bs; break; case ParameterTypes.ArraySByte: var sblen = reader.ReadInt32(); var sbs = new sbyte[sblen]; for (int x = 0; x < sblen; x++) sbs[x] = reader.ReadSByte(); parameters[i] = sbs; break; case ParameterTypes.ArrayDecimal: var dclen = reader.ReadInt32(); var dcs = new decimal[dclen]; for (int x = 0; x < dclen; x++) dcs[x] = reader.ReadDecimal(); parameters[i] = dcs; break; case ParameterTypes.ArrayDouble: var dblen = reader.ReadInt32(); var dbs = new double[dblen]; for (int x = 0; x < dblen; x++) dbs[x] = reader.ReadDouble(); parameters[i] = dbs; break; case ParameterTypes.ArrayFloat: var flen = reader.ReadInt32(); var fs = new float[flen]; for (int x = 0; x < flen; x++) fs[x] = reader.ReadSingle(); parameters[i] = fs; break; case ParameterTypes.ArrayInt: var ilen = reader.ReadInt32(); var iss = new int[ilen]; for (int x = 0; x < ilen; x++) iss[x] = reader.ReadInt32(); parameters[i] = iss; break; case ParameterTypes.ArrayUInt: var uilen = reader.ReadInt32(); var uis = new uint[uilen]; for (int x = 0; x < uilen; x++) uis[x] = reader.ReadUInt32(); parameters[i] = uis; break; case ParameterTypes.ArrayLong: var llen = reader.ReadInt32(); var ls = new long[llen]; for (int x = 0; x < llen; x++) ls[x] = reader.ReadInt64(); parameters[i] = ls; break; case ParameterTypes.ArrayULong: var ullen = reader.ReadInt32(); var uls = new ulong[ullen]; for (int x = 0; x < ullen; x++) uls[x] = reader.ReadUInt64(); parameters[i] = uls; break; case ParameterTypes.ArrayShort: var sslen = reader.ReadInt32(); var sss = new short[sslen]; for (int x = 0; x < sslen; x++) sss[x] = reader.ReadInt16(); parameters[i] = sss; break; case ParameterTypes.ArrayUShort: var ulen = reader.ReadInt32(); var us = new ushort[ulen]; for (int x = 0; x < ulen; x++) us[x] = reader.ReadUInt16(); parameters[i] = us; break; case ParameterTypes.ArrayString: var slen = reader.ReadInt32(); var ss = new string[slen]; for (int x = 0; x < slen; x++) { ss[x] = reader.ReadString(); if (ss[x] == NULL_STRING) ss[x] = null; } parameters[i] = ss; break; case ParameterTypes.ArrayType: var tlen = reader.ReadInt32(); var ts = new Type[tlen]; for (int x = 0; x < tlen; x++) ts[x] = Type.GetType(reader.ReadString()); parameters[i] = ts; break; case ParameterTypes.ArrayGuid: var glen = reader.ReadInt32(); var gs = new Guid[glen]; for (int x = 0; x < glen; x++) gs[x] = new Guid(reader.ReadBytes(16)); parameters[i] = gs; break; case ParameterTypes.ArrayDateTime: var dlen = reader.ReadInt32(); var dts = new DateTime[dlen]; for (int x = 0; x < dlen; x++) { var adtstr = reader.ReadString(); dts[x] = DateTime.Parse(adtstr, null, DateTimeStyles.RoundtripKind); } parameters[i] = dts; break; case ParameterTypes.Unknown: var typeConfigName = reader.ReadString(); var bytes = reader.ReadBytes(reader.ReadInt32()); parameters[i] = _serializer.Deserialize(bytes, typeConfigName); break; case ParameterTypes.CompressedUnknown: var cuTypeConfigName = reader.ReadString(); var cuBytes = _compressor.DeCompress(reader.ReadBytes(reader.ReadInt32())); parameters[i] = _serializer.Deserialize(cuBytes, cuTypeConfigName); break; default: throw new Exception(string.Format("Unknown type byte '0x{0:X}'", typeByte)); } } } return parameters; } private byte GetParameterType(Type type) { InitializeParamTypes(); if (_parameterTypes.ContainsKey(type)) return _parameterTypes[type]; return ParameterTypes.Unknown; } private void InitializeParamTypes() { if (_parameterTypes == null) { _parameterTypes = new Dictionary(); _parameterTypes.Add(typeof(bool), ParameterTypes.Bool); _parameterTypes.Add(typeof(byte), ParameterTypes.Byte); _parameterTypes.Add(typeof(sbyte), ParameterTypes.SByte); _parameterTypes.Add(typeof(char), ParameterTypes.Char); _parameterTypes.Add(typeof(decimal), ParameterTypes.Decimal); _parameterTypes.Add(typeof(double), ParameterTypes.Double); _parameterTypes.Add(typeof(float), ParameterTypes.Float); _parameterTypes.Add(typeof(int), ParameterTypes.Int); _parameterTypes.Add(typeof(uint), ParameterTypes.UInt); _parameterTypes.Add(typeof(long), ParameterTypes.Long); _parameterTypes.Add(typeof(ulong), ParameterTypes.ULong); _parameterTypes.Add(typeof(short), ParameterTypes.Short); _parameterTypes.Add(typeof(ushort), ParameterTypes.UShort); _parameterTypes.Add(typeof(string), ParameterTypes.String); _parameterTypes.Add(typeof(byte[]), ParameterTypes.ByteArray); _parameterTypes.Add(typeof(char[]), ParameterTypes.CharArray); _parameterTypes.Add(typeof(Type), ParameterTypes.Type); _parameterTypes.Add(typeof(Guid), ParameterTypes.Guid); _parameterTypes.Add(typeof(DateTime), ParameterTypes.DateTime); _parameterTypes.Add(typeof(bool[]), ParameterTypes.ArrayBool); _parameterTypes.Add(typeof(sbyte[]), ParameterTypes.ArraySByte); _parameterTypes.Add(typeof(decimal[]), ParameterTypes.ArrayDecimal); _parameterTypes.Add(typeof(double[]), ParameterTypes.ArrayDouble); _parameterTypes.Add(typeof(float[]), ParameterTypes.ArrayFloat); _parameterTypes.Add(typeof(int[]), ParameterTypes.ArrayInt); _parameterTypes.Add(typeof(uint[]), ParameterTypes.ArrayUInt); _parameterTypes.Add(typeof(long[]), ParameterTypes.ArrayLong); _parameterTypes.Add(typeof(ulong[]), ParameterTypes.ArrayULong); _parameterTypes.Add(typeof(short[]), ParameterTypes.ArrayShort); _parameterTypes.Add(typeof(ushort[]), ParameterTypes.ArrayUShort); _parameterTypes.Add(typeof(string[]), ParameterTypes.ArrayString); _parameterTypes.Add(typeof(Type[]), ParameterTypes.ArrayType); _parameterTypes.Add(typeof(Guid[]), ParameterTypes.ArrayGuid); _parameterTypes.Add(typeof(DateTime[]), ParameterTypes.ArrayDateTime); } } } } ================================================ FILE: src/ServiceWire/ParameterTypes.cs ================================================ namespace ServiceWire { internal sealed class ParameterTypes { internal const byte Unknown = 0x00; internal const byte Bool = 0x01; internal const byte Byte = 0x02; internal const byte SByte = 0x03; internal const byte Char = 0x04; internal const byte Decimal = 0x05; internal const byte Double = 0x06; internal const byte Float = 0x07; internal const byte Int = 0x08; internal const byte UInt = 0x09; internal const byte Long = 0x0A; internal const byte ULong = 0x0B; internal const byte Short = 0x0C; internal const byte UShort = 0x0D; internal const byte String = 0x0E; internal const byte ByteArray = 0x0F; internal const byte CharArray = 0x10; internal const byte Null = 0x11; internal const byte Type = 0x12; internal const byte Guid = 0x13; internal const byte DateTime = 0x14; internal const byte CompressedByteArray = 0x20; internal const byte CompressedCharArray = 0x21; internal const byte CompressedString = 0x22; internal const byte CompressedUnknown = 0x23; internal const byte ArrayBool = 0x41; internal const byte ArraySByte = 0x43; internal const byte ArrayDecimal = 0x45; internal const byte ArrayDouble = 0x46; internal const byte ArrayFloat = 0x47; internal const byte ArrayInt = 0x48; internal const byte ArrayUInt = 0x49; internal const byte ArrayLong = 0x4A; internal const byte ArrayULong = 0x4B; internal const byte ArrayShort = 0x4C; internal const byte ArrayUShort = 0x4D; internal const byte ArrayString = 0x4E; internal const byte ArrayType = 0x52; internal const byte ArrayGuid = 0x53; internal const byte ArrayDateTime = 0x54; } } ================================================ FILE: src/ServiceWire/PooledDictionary.cs ================================================ using System; using System.Collections.Concurrent; namespace ServiceWire { public sealed class PooledDictionary : IDisposable { private readonly ConcurrentDictionary> _dq; private readonly int _concurrencyLevel; private readonly int _size; public PooledDictionary() { _concurrencyLevel = Environment.ProcessorCount * 8; _size = _concurrencyLevel * _concurrencyLevel; _dq = new ConcurrentDictionary>(_concurrencyLevel, _size); } public void Add(TKey key, TValue value) { if (!_dq.ContainsKey(key)) _dq.TryAdd(key, new ConcurrentQueue()); ConcurrentQueue q; if (_dq.TryGetValue(key, out q)) { q.Enqueue(value); } else { throw new ArgumentException("Unable to add value"); } } public int Count(TKey key) { if (!_dq.ContainsKey(key)) _dq.TryAdd(key, new ConcurrentQueue()); ConcurrentQueue q; if (_dq.TryGetValue(key, out q)) { return q.Count; } return 0; } public TValue Request(TKey key, Func creator = null) { if (!_dq.ContainsKey(key)) _dq.TryAdd(key, new ConcurrentQueue()); ConcurrentQueue q; if (_dq.TryGetValue(key, out q)) { TValue v; if (q.TryDequeue(out v)) return v; if (null != creator) return creator(); } return default(TValue); } public void Release(TKey key, TValue value) { Add(key, value); //just adds it back to key's queue } #region IDisposable Members private bool _disposed = false; public void Dispose() { //MS recommended dispose pattern - prevents GC from disposing again if (!_disposed) { _disposed = true; foreach(var kvp in _dq) { while (!kvp.Value.IsEmpty) { TValue v; if (kvp.Value.TryDequeue(out v)) { var disp = v as IDisposable; if (null != disp) disp.Dispose(); } } } GC.SuppressFinalize(this); } } #endregion } } ================================================ FILE: src/ServiceWire/ProxyBuilder.cs ================================================ using System; using System.Reflection.Emit; namespace ServiceWire { internal sealed class ProxyBuilder { public string ProxyName { get; set; } public Type InterfaceType { get; set; } public Type CtorType { get; set; } public AssemblyBuilder AssemblyBuilder { get; set; } public ModuleBuilder ModuleBuilder { get; set; } public TypeBuilder TypeBuilder { get; set; } } } ================================================ FILE: src/ServiceWire/ProxyFactory.cs ================================================ using System; using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; using System.Threading; namespace ServiceWire { public static class ProxyFactory { private const string PROXY_ASSEMBLY = "ProxyAssembly"; private const string INVOKE_METHOD = "InvokeMethod"; private const string PROXY_MODULE = "ProxyModule"; private const string PROXY = "Proxy"; //pooled dictionary achieves same or better performance as ThreadStatic without creating as many builders under average load private static PooledDictionary _proxies = new PooledDictionary(); public static TInterface CreateProxy(Type channelType, Type ctorArgType, object channelCtorValue, ISerializer serializer, ICompressor compressor, ILog logger, IStats stats) where TInterface : class { if (!channelType.InheritsFrom(typeof(Channel))) throw new ArgumentException("channelType does not inherit from Channel"); Type interfaceType = typeof(TInterface); //derive unique key for this dynamic assembly by interface, channel and ctor type names var proxyName = interfaceType.ToConfigName() + channelType.ToConfigName() + ctorArgType.ToConfigName(); //get pooled proxy builder var localChannelType = channelType; var localCtorArgType = ctorArgType; ProxyBuilder proxyBuilder = null; TInterface proxy = null; try { proxyBuilder = _proxies.Request(proxyName, () => CreateProxyBuilder(proxyName, interfaceType, localChannelType, localCtorArgType)); proxy = CreateProxy(proxyBuilder, channelCtorValue, serializer, compressor, logger, stats); } finally { //return builder to the pool if (null != proxyBuilder) _proxies.Release(proxyName, proxyBuilder); } return proxy; } private static TInterface CreateProxy(ProxyBuilder proxyBuilder, object channelCtorValue, ISerializer serializer, ICompressor compressor, ILog logger, IStats stats) where TInterface : class { //create the type and construct an instance Type[] ctorArgTypes = new Type[] { typeof(Type), proxyBuilder.CtorType, typeof(ISerializer), typeof(ICompressor), typeof(ILog), typeof(IStats) }; Type t = proxyBuilder.TypeBuilder.CreateTypeInfo(); var constructorInfo = t.GetConstructor(ctorArgTypes); if (constructorInfo != null) { TInterface instance = (TInterface)constructorInfo.Invoke(new object[] { typeof(TInterface), channelCtorValue, serializer, compressor, logger, stats }); return instance; } return null; } private static ProxyBuilder CreateProxyBuilder(string proxyName, Type interfaceType, Type channelType, Type ctorArgType) { AppDomain domain = Thread.GetDomain(); // create a new assembly for the proxy AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(PROXY_ASSEMBLY), AssemblyBuilderAccess.Run); // create a new module for the proxy ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(PROXY_MODULE); // Set the class to be public and sealed TypeAttributes typeAttributes = TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed; // Construct the type builder TypeBuilder typeBuilder = moduleBuilder.DefineType(interfaceType.Name + PROXY, typeAttributes, channelType); List allInterfaces = new List(interfaceType.GetInterfaces()); allInterfaces.Add(interfaceType); //add the interface typeBuilder.AddInterfaceImplementation(interfaceType); //construct the constructor Type[] ctorArgTypes = new Type[] { typeof(Type), ctorArgType, typeof(ISerializer), typeof(ICompressor), typeof(ILog), typeof(IStats) }; CreateConstructor(channelType, typeBuilder, ctorArgTypes); //construct the type maps Dictionary ldindOpCodeTypeMap = new Dictionary(); ldindOpCodeTypeMap.Add(typeof(Boolean), OpCodes.Ldind_I1); ldindOpCodeTypeMap.Add(typeof(Byte), OpCodes.Ldind_U1); ldindOpCodeTypeMap.Add(typeof(SByte), OpCodes.Ldind_I1); ldindOpCodeTypeMap.Add(typeof(Int16), OpCodes.Ldind_I2); ldindOpCodeTypeMap.Add(typeof(UInt16), OpCodes.Ldind_U2); ldindOpCodeTypeMap.Add(typeof(Int32), OpCodes.Ldind_I4); ldindOpCodeTypeMap.Add(typeof(UInt32), OpCodes.Ldind_U4); ldindOpCodeTypeMap.Add(typeof(Int64), OpCodes.Ldind_I8); ldindOpCodeTypeMap.Add(typeof(UInt64), OpCodes.Ldind_I8); ldindOpCodeTypeMap.Add(typeof(Char), OpCodes.Ldind_U2); ldindOpCodeTypeMap.Add(typeof(Double), OpCodes.Ldind_R8); ldindOpCodeTypeMap.Add(typeof(Single), OpCodes.Ldind_R4); Dictionary stindOpCodeTypeMap = new Dictionary(); stindOpCodeTypeMap.Add(typeof(Boolean), OpCodes.Stind_I1); stindOpCodeTypeMap.Add(typeof(Byte), OpCodes.Stind_I1); stindOpCodeTypeMap.Add(typeof(SByte), OpCodes.Stind_I1); stindOpCodeTypeMap.Add(typeof(Int16), OpCodes.Stind_I2); stindOpCodeTypeMap.Add(typeof(UInt16), OpCodes.Stind_I2); stindOpCodeTypeMap.Add(typeof(Int32), OpCodes.Stind_I4); stindOpCodeTypeMap.Add(typeof(UInt32), OpCodes.Stind_I4); stindOpCodeTypeMap.Add(typeof(Int64), OpCodes.Stind_I8); stindOpCodeTypeMap.Add(typeof(UInt64), OpCodes.Stind_I8); stindOpCodeTypeMap.Add(typeof(Char), OpCodes.Stind_I2); stindOpCodeTypeMap.Add(typeof(Double), OpCodes.Stind_R8); stindOpCodeTypeMap.Add(typeof(Single), OpCodes.Stind_R4); //construct the method builders from the method infos defined in the interface List methods = GetAllMethods(allInterfaces); foreach (MethodInfo methodInfo in methods) { MethodBuilder methodBuilder = ConstructMethod(channelType, methodInfo, typeBuilder, ldindOpCodeTypeMap, stindOpCodeTypeMap); typeBuilder.DefineMethodOverride(methodBuilder, methodInfo); } //create proxy builder var result = new ProxyBuilder { ProxyName = proxyName, InterfaceType = interfaceType, CtorType = ctorArgType, AssemblyBuilder = assemblyBuilder, ModuleBuilder = moduleBuilder, TypeBuilder = typeBuilder }; return result; } private static List GetAllMethods(List allInterfaces) { List methods = new List(); foreach (Type interfaceType in allInterfaces) methods.AddRange(interfaceType.GetMethods()); return methods; } private static void CreateConstructor(Type channelType, TypeBuilder typeBuilder, Type[] ctorArgTypes) { ConstructorBuilder ctor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, ctorArgTypes); ConstructorInfo baseCtor = channelType.GetConstructor(ctorArgTypes); ILGenerator ctorIL = ctor.GetILGenerator(); ctorIL.Emit(OpCodes.Ldarg_0); //load "this" ctorIL.Emit(OpCodes.Ldarg_1); //load serviceType ctorIL.Emit(OpCodes.Ldarg_2); //load "endpoint" ctorIL.Emit(OpCodes.Ldarg_3); //load "serializer" ctorIL.Emit(OpCodes.Ldarg_S, 4); //load "compressor" ctorIL.Emit(OpCodes.Ldarg_S, 5); //load "logger" ctorIL.Emit(OpCodes.Ldarg_S, 6); //load "stats" ctorIL.Emit(OpCodes.Call, baseCtor); //call "base(...)" ctorIL.Emit(OpCodes.Ret); } private static MethodBuilder ConstructMethod(Type channelType, MethodInfo methodInfo, TypeBuilder typeBuilder, Dictionary ldindOpCodeTypeMap, Dictionary stindOpCodeTypeMap) { ParameterInfo[] paramInfos = methodInfo.GetParameters(); int nofParams = paramInfos.Length; Type[] parameterTypes = new Type[nofParams]; for (int i = 0; i < nofParams; i++) parameterTypes[i] = paramInfos[i].ParameterType; Type returnType = methodInfo.ReturnType; MethodBuilder methodBuilder = typeBuilder.DefineMethod(methodInfo.Name, MethodAttributes.Public | MethodAttributes.Virtual, returnType, parameterTypes); ILGenerator mIL = methodBuilder.GetILGenerator(); GenerateILCodeForMethod(channelType, methodInfo, mIL, parameterTypes, methodBuilder.ReturnType, ldindOpCodeTypeMap, stindOpCodeTypeMap); return methodBuilder; } private static void GenerateILCodeForMethod(Type channelType, MethodInfo methodInfo, ILGenerator mIL, Type[] inputArgTypes, Type returnType, Dictionary ldindOpCodeTypeMap, Dictionary stindOpCodeTypeMap) { mIL.Emit(OpCodes.Ldarg_0); //load "this" int nofArgs = inputArgTypes.Length; //get the MethodInfo for InvokeMethod MethodInfo invokeMethodMI = channelType.GetMethod(INVOKE_METHOD, BindingFlags.Instance | BindingFlags.NonPublic); //declare local variables LocalBuilder resultLB = mIL.DeclareLocal(typeof(object[])); // object[] result //set local value with method name and arg types to improve perfmance //metadata: methodInfo.Name | inputTypes[x].FullName .. | var metadata = methodInfo.Name; if (inputArgTypes.Length > 0) { var args = new string[inputArgTypes.Length]; for (int i = 0; i < inputArgTypes.Length; i++) args[i] = inputArgTypes[i].ToConfigName(); metadata += "|" + string.Join("|", args); } //declare and assign string literal LocalBuilder metaLB = mIL.DeclareLocal(typeof(string)); //mIL.Emit(OpCodes.Dup); //causes InvalidProgramException - Common Language Runtime detected an invalid program. mIL.Emit(OpCodes.Ldstr, metadata); mIL.Emit(OpCodes.Stloc_1); //load into metaData local variable //load metadata into first param for invokeMethodMI //mIL.Emit(OpCodes.Dup); //causes InvalidProgramException - Common Language Runtime detected an invalid program. mIL.Emit(OpCodes.Ldloc_1); mIL.Emit(OpCodes.Ldc_I4, nofArgs); //push the number of arguments mIL.Emit(OpCodes.Newarr, typeof(object)); //create an array of objects //store every input argument in the args array for (int i = 0; i < nofArgs; i++) { Type inputType = inputArgTypes[i].IsByRef ? inputArgTypes[i].GetElementType() : inputArgTypes[i]; mIL.Emit(OpCodes.Dup); mIL.Emit(OpCodes.Ldc_I4, i); //push the index onto the stack mIL.Emit(OpCodes.Ldarg, i + 1); //load the i'th argument. This might be an address if (inputArgTypes[i].IsByRef) { if (inputType.IsValueType) { if (inputType.IsPrimitive) { mIL.Emit(ldindOpCodeTypeMap[inputType]); mIL.Emit(OpCodes.Box, inputType); } else if (inputType.IsEnum && ldindOpCodeTypeMap.ContainsKey(Enum.GetUnderlyingType(inputType))) { //enum with a supported underlying type is converted to that underlying type Type underlyingTypeEnum = Enum.GetUnderlyingType(inputType); mIL.Emit(ldindOpCodeTypeMap[underlyingTypeEnum]); mIL.Emit(OpCodes.Box, inputType); } else { throw new NotSupportedException("Non-primitive native types (e.g. Decimal and Guid) ByRef are not supported."); } } else { mIL.Emit(OpCodes.Ldind_Ref); } } else { if (inputArgTypes[i].IsValueType) mIL.Emit(OpCodes.Box, inputArgTypes[i]); } mIL.Emit(OpCodes.Stelem_Ref); //store the reference in the args array } mIL.Emit(OpCodes.Call, invokeMethodMI); mIL.Emit(OpCodes.Stloc, resultLB.LocalIndex); //store the result //store the results in the arguments for (int i = 0; i < nofArgs; i++) { if (inputArgTypes[i].IsByRef) { Type inputType = inputArgTypes[i].GetElementType(); mIL.Emit(OpCodes.Ldarg, i + 1); //load the address of the argument mIL.Emit(OpCodes.Ldloc, resultLB.LocalIndex); //load the result array mIL.Emit(OpCodes.Ldc_I4, i + 1); //load the index into the result array mIL.Emit(OpCodes.Ldelem_Ref); //load the value in the index of the array if (inputType.IsEnum && ldindOpCodeTypeMap.ContainsKey(Enum.GetUnderlyingType(inputType))) { //enum with a supported underlying type is converted to that underlying type Type underlyingTypeEnum = Enum.GetUnderlyingType(inputType); mIL.Emit(OpCodes.Unbox, underlyingTypeEnum); mIL.Emit(ldindOpCodeTypeMap[underlyingTypeEnum]); mIL.Emit(stindOpCodeTypeMap[underlyingTypeEnum]); } else if (inputType.IsValueType) { mIL.Emit(OpCodes.Unbox, inputArgTypes[i].GetElementType()); mIL.Emit(ldindOpCodeTypeMap[inputArgTypes[i].GetElementType()]); mIL.Emit(stindOpCodeTypeMap[inputArgTypes[i].GetElementType()]); } else { mIL.Emit(OpCodes.Castclass, inputArgTypes[i].GetElementType()); mIL.Emit(OpCodes.Stind_Ref); //store the unboxed value at the argument address } } } if (returnType != typeof(void)) { mIL.Emit(OpCodes.Ldloc, resultLB.LocalIndex); //load the result array mIL.Emit(OpCodes.Ldc_I4, 0); //load the index of the return value. Alway 0 mIL.Emit(OpCodes.Ldelem_Ref); //load the value in the index of the array if (returnType.IsValueType) { mIL.Emit(OpCodes.Unbox, returnType); //unbox it if (returnType.IsPrimitive) //deal with primitive vs struct value types mIL.Emit(ldindOpCodeTypeMap[returnType]); else mIL.Emit(OpCodes.Ldobj, returnType); } else { mIL.Emit(OpCodes.Castclass, returnType); } } mIL.Emit(OpCodes.Ret); } } } ================================================ FILE: src/ServiceWire/SerializationValidator.cs ================================================ using System; using System.Collections.Generic; using System.Reflection; using System.Runtime.Serialization; namespace ServiceWire { internal static class SerializationValidator { public static List NonSerializableTypes(this Type type) { var nonSerializableTypes = new List(); AnalyzeType(type, nonSerializableTypes); return nonSerializableTypes; } public static void ValidateServiceInterface(this Type type) { var nonSerializableTypes = new List(); var methods = type.GetMethods(BindingFlags.Public); foreach (var method in methods) { if (method.ReturnType != typeof (void)) { AnalyzeType(method.ReturnType, nonSerializableTypes); } var parameters = method.GetParameters(); foreach (var parameter in parameters) { if (parameter.ParameterType.IsByRef) { Type inputType = parameter.ParameterType.GetElementType(); if (inputType.IsValueType && !inputType.IsPrimitive) throw new NotSupportedException("Non-primitive native types (e.g. Decimal and Guid) ByRef are not supported."); } AnalyzeType(parameter.ParameterType, nonSerializableTypes); } } if (nonSerializableTypes.Count > 0) { var errorMessage = string.Format("One or more types in {0} are not marked with the Serializable attribute: {1}", type.ToConfigName(), string.Join(",", nonSerializableTypes)); throw new SerializationException(errorMessage); } } private static void AnalyzeType(Type type, List nonSerializableTypes) { if (type.IsValueType || type == typeof(string)) return; if (type.IsEnum) { //enum with a supported underlying type is converted to that underlying type Type underlyingTypeEnum = Enum.GetUnderlyingType(type); if (underlyingTypeEnum.IsValueType || underlyingTypeEnum == typeof(string)) return; } if (!IsSerializable(type)) nonSerializableTypes.Add(type.Name); foreach (var propertyInfo in type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) { if (propertyInfo.PropertyType.IsGenericType) { foreach (var genericArgument in propertyInfo.PropertyType.GetGenericArguments()) { if (genericArgument == type) continue; // base case for circularly referenced properties AnalyzeType(genericArgument, nonSerializableTypes); } } else if (propertyInfo.GetType() != type) // base case for circularly referenced properties AnalyzeType(propertyInfo.PropertyType, nonSerializableTypes); } } private static bool IsSerializable(Type type) { return (Attribute.IsDefined(type, typeof(SerializableAttribute))); } } } ================================================ FILE: src/ServiceWire/ServiceInstance.cs ================================================ using System; using System.Collections.Concurrent; using System.Reflection; namespace ServiceWire { public class ServiceInstance { public int KeyIndex { get; set; } public Type InterfaceType { get; set; } public object SingletonInstance { get; set; } public ConcurrentDictionary InterfaceMethods { get; set; } public ConcurrentDictionary MethodParametersByRef { get; set; } public ServiceSyncInfo ServiceSyncInfo { get; set; } } } ================================================ FILE: src/ServiceWire/ServiceSyncInfo.cs ================================================ using System; using System.Runtime.Serialization; namespace ServiceWire { [Serializable, DataContract] public class ServiceSyncInfo { [DataMember(Order = 1)] public int ServiceKeyIndex { get; set; } [DataMember(Order = 2)] public MethodSyncInfo[] MethodInfos { get; set; } [DataMember(Order = 3)] public bool UseCompression { get; set; } [DataMember(Order = 4)] public int CompressionThreshold { get; set; } } } ================================================ FILE: src/ServiceWire/ServiceSyncInfoCacheKey.cs ================================================ using System; namespace ServiceWire { internal class ServiceSyncInfoCacheKey : IEquatable { public Type Type { get; } public IChannelIdentifier ChannelIdentifier { get; } public ServiceSyncInfoCacheKey(Type type, IChannelIdentifier channelIdentifier) { Type = type; ChannelIdentifier = channelIdentifier; } public bool Equals(ServiceSyncInfoCacheKey other) { return Type.Equals(other.Type) && ChannelIdentifier.Equals(other.ChannelIdentifier); } public override bool Equals(object obj) { if (obj is ServiceSyncInfoCacheKey other) { return Equals(other); } return false; } public override int GetHashCode() { unchecked { return Type.GetHashCode() + ChannelIdentifier.GetHashCode(); } } } } ================================================ FILE: src/ServiceWire/ServiceWire.csproj ================================================  netstandard2.0 true Tyler Jensen icon.png https://github.com/tylerjensen/ServiceWire License.txt Tyler Jensen 2013-2021 5.6.0 ServiceWire is a very fast and light weight service host and dynamic client library that simplifies the development and use of high performance remote procedure call (RPC) communication between .NET processes over Named Pipes or TCP/IP. And with the release of 2.0.0, ServiceWire now supports optional use of Zero Knowledge authentication with a fast variation of Secure Remote Password Protocol and strong Rijndael encryption of data across the wire via TCP without the need for public keys going across the wire. WCF Services Host Client NamedPipes TCP Messaging RPC Interception Zero Knowledge Secure Remote Password Protocol Serialization Contributions fix TCP client synchronous connection blocks and adds NamedPipeServerStreamFactory to allow greater level of permissions control in using named pipes. Multiple code improvements. AnyCPU ================================================ FILE: src/ServiceWire/Stats.cs ================================================ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Threading; using System.Threading.Tasks; namespace ServiceWire { public class Stats : LoggerBase, IStats { private int _statsBufferSize = 10000; private const string StatFilePrefixDefault = "stat-"; private const string StatFileExtensionDefault = ".txt"; public Stats(string statsDirectory = null, string statsFilePrefix = null, string statsFileExtension = null, int messageBufferSize = 32, int statsBufferSize = 10000, LogOptions options = LogOptions.LogOnlyToFile, LogRollOptions rollOptions = LogRollOptions.Daily, int rollMaxMegaBytes = 1024, bool useUtcTimeStamp = false) { _logDirectory = statsDirectory ?? Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "logs"); Directory.CreateDirectory(_logDirectory); //will throw if unable - does not throw if already exists _logFilePrefix = statsFilePrefix ?? StatFilePrefixDefault; _logFileExtension = statsFileExtension ?? StatFileExtensionDefault; _messageBufferSize = messageBufferSize; _statsBufferSize = statsBufferSize; _rollOptions = rollOptions; _rollMaxMegaBytes = rollMaxMegaBytes; _useUtcTimeStamp = useUtcTimeStamp; LogOptions = options; if (_messageBufferSize < 1) _messageBufferSize = 1; if (_messageBufferSize > 4096) _messageBufferSize = 4096; if (_statsBufferSize < 10) _statsBufferSize = 10; if (_statsBufferSize > 1000000) _statsBufferSize = 1000000; if (_rollOptions == LogRollOptions.Size) { if (_rollMaxMegaBytes < 1) _rollMaxMegaBytes = 1; if (_rollMaxMegaBytes < 4096) _rollMaxMegaBytes = 4096; } } private void WriteLines(string[] lines) { _logQueue.Enqueue(lines); if (_logQueue.Count >= _messageBufferSize) Task.Factory.StartNew(() => WriteBuffer(_messageBufferSize)); } public override void FlushLog() { var items = new List(); foreach (var kvp in _bag) { items.Add(kvp.Value.GetDump()); } foreach(var list in items) WriteLines(list); base.FlushLog(); } //period, cat, nm, count, total private int _period = 0; private ConcurrentDictionary _bag = new ConcurrentDictionary(); private void WriteBag(StatsBag bag) { int p = _period; Interlocked.Increment(ref _period); var lines = bag.GetDump(); StatsBag b; if (_bag.TryRemove(p, out b)) b.Clear(); WriteLines(lines); } public void Log(string name, float value) { int p = _period; var bag = _bag.GetOrAdd(p, new StatsBag(_useUtcTimeStamp)); bag.Add("unspecified", name, value); if (bag.Count >= _statsBufferSize) { WriteBag(bag); } } public void Log(string category, string name, float value) { int p = _period; var bag = _bag.GetOrAdd(p, new StatsBag(_useUtcTimeStamp)); bag.Add(category, name, value); if (bag.Count >= _statsBufferSize) { WriteBag(bag); } } } } ================================================ FILE: src/ServiceWire/StatsBag.cs ================================================ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; namespace ServiceWire { internal class StatsBag { private bool _useUtcTime = false; public StatsBag(bool useUtcTime) { _useUtcTime = useUtcTime; _started = _useUtcTime ? DateTime.UtcNow : DateTime.Now; } private DateTime _started = DateTime.Now; private int _count = 0; private ConcurrentDictionary> _bag = new ConcurrentDictionary>(); public int Count { get { return _count; } } public void Clear() { _bag.Clear(); } protected const string TimeStampPattern = "yyyy-MM-ddTHH:mm:ss.fff"; protected string GetTimeStamp() { return _useUtcTime ? DateTime.UtcNow.ToString(TimeStampPattern) : DateTime.Now.ToString(TimeStampPattern); } protected string GetTimeStamp(DateTime dt) { return _useUtcTime ? dt.ToUniversalTime().ToString(TimeStampPattern) : dt.ToString(TimeStampPattern); } public void Add(string category, string name, float value) { var val = value; var container = _bag.GetOrAdd(category, new ConcurrentDictionary()); container.AddOrUpdate(name, s => new StatInfo(val), (s, t) => { t.Increment(val); return t; }); Interlocked.Increment(ref _count); } public string[] GetDump() { var dumped = _useUtcTime ? DateTime.UtcNow : DateTime.Now; TimeSpan ts = dumped - _started; var tsTxt = ts.TotalSeconds.ToString("######.000000"); var lines = new List(); //add header lines.Add(string.Format("", GetTimeStamp(_started), GetTimeStamp(dumped), _count, tsTxt)); foreach (var cat in _bag) { var catCount = (from n in cat.Value select n.Value.Count).Sum(); lines.Add(string.Format(" ", cat.Key, catCount)); foreach (var stat in cat.Value) { if (null == stat.Value) continue; float total = stat.Value.Total; float avg = total == 0.0f ? 0.0f : total / stat.Value.Count; lines.Add(string.Format(" ", stat.Key, stat.Value.Count, total, avg)); } lines.Add(" "); } lines.Add(""); return lines.ToArray(); } } internal class StatInfo { private ConcurrentBag _bag = new ConcurrentBag(); public int Count { get { return _bag.Count; } } public float Total { get { return _bag.Sum(); } } public StatInfo(float value) { _bag.Add(value); } public void Increment(float value) { _bag.Add(value); } } } ================================================ FILE: src/ServiceWire/StreamingChannel.cs ================================================ using ServiceWire.ZeroKnowledge; using System; using System.Collections.Concurrent; using System.Diagnostics; using System.IO; using System.Reflection; using System.Security.Authentication; using System.Threading.Tasks; namespace ServiceWire { public class StreamingChannel : Channel, IDvChannel { private readonly object _syncRoot = new object(); protected BinaryReader _binReader; protected BinaryWriter _binWriter; protected Stream _stream; private readonly ParameterTransferHelper _parameterTransferHelper; private ServiceSyncInfo _syncInfo; private ZkCrypto _zkCrypto; // keep cached sync info to avoid redundant wire trips private static readonly ConcurrentDictionary SyncInfoCache = new ConcurrentDictionary(); public StreamingChannel(ISerializer serializer, ICompressor compressor, ILog logger = null, IStats stats = null) : base(serializer, compressor, logger, stats) { _parameterTransferHelper = new ParameterTransferHelper(_serializer, _compressor); } public static void ClearCachedSyncInfo() { SyncInfoCache.Clear(); } protected virtual IChannelIdentifier ChannelIdentifier { get; } /// /// Returns true if client is connected to the server. /// public virtual bool IsConnected => false; /// /// This method asks the server for a list of identifiers paired with method /// names and -parameter types. This is used when invoking methods server side. /// protected override void SyncInterface(Type serviceType, string username = null, string password = null) { if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password)) { var sw = Stopwatch.StartNew(); _logger.Debug("Zk authentiation started for: {0}, {1}", username, password); //do zk protocol authentication var sr = new ZkProtocol(); // Step 1. Client sends username and ephemeral hash of random number. var aRand = sr.CryptRand(); var aClientEphemeral = sr.GetClientEphemeralA(aRand); // send username and aClientEphemeral to server _binWriter.Write((int)MessageType.ZkInitiate); _binWriter.Write(username); _logger.Debug("username sent to server: {0}", username); _binWriter.Write(aClientEphemeral); //always 32 bytes _logger.Debug("ClientEphemeral (A) sent to server: {0}", Convert.ToBase64String(aClientEphemeral)); // get response from server var userFound = _binReader.ReadBoolean(); if (!userFound) { _logger.Debug("User not found. InvalidCredentialException thrown."); throw new InvalidCredentialException("authentication failed"); } var salt = _binReader.ReadBytes(32); _logger.Debug("Salt received from server: {0}", Convert.ToBase64String(salt)); var bServerEphemeral = _binReader.ReadBytes(32); _logger.Debug("ServerEphemeral (B) received from server: {0}", Convert.ToBase64String(bServerEphemeral)); // Step 3. Client and server calculate random scramble of ephemeral hash values exchanged. var clientScramble = sr.CalculateRandomScramble(aClientEphemeral, bServerEphemeral); // Step 4. Client computes session key var clientSessionKey = sr.ClientComputeSessionKey(salt, username, password, aClientEphemeral, bServerEphemeral, clientScramble); // Step 6. Client creates hash of session key and sends to server. Server creates same key and verifies. var clientSessionHash = sr.ClientCreateSessionHash(username, salt, aClientEphemeral, bServerEphemeral, clientSessionKey); // send to server and server verifies _binWriter.Write((int)MessageType.ZkProof); _binWriter.Write(clientSessionHash); //always 32 bytes _logger.Debug("ClientSessionKey Hash sent to server: {0}", Convert.ToBase64String(clientSessionHash)); // get response var serverVerified = _binReader.ReadBoolean(); if (!serverVerified) { _logger.Debug("Server verification failed. InvalidCredentialException thrown."); throw new InvalidCredentialException("authentication failed"); } var serverSessionHash = _binReader.ReadBytes(32); var clientServerSessionHash = sr.ServerCreateSessionHash(aClientEphemeral, clientSessionHash, clientSessionKey); if (!serverSessionHash.IsEqualTo(clientServerSessionHash)) { _logger.Debug("Server hash mismatch. InvalidCredentialException thrown. Has received: {0}", Convert.ToBase64String(serverSessionHash)); throw new InvalidCredentialException("authentication failed"); } _logger.Debug("Server Hash match. Received from server: {0}", Convert.ToBase64String(serverSessionHash)); _zkCrypto = new ZkCrypto(clientSessionKey, clientScramble); _logger.Debug("Zk authentiation completed successfully."); sw.Stop(); _stats.Log("ZkAuthentication", sw.ElapsedMilliseconds); } var serviceSyncInfoCacheKey = new ServiceSyncInfoCacheKey(serviceType, ChannelIdentifier); if (!SyncInfoCache.TryGetValue(serviceSyncInfoCacheKey, out _syncInfo)) { //write the message type _binWriter.Write((int)MessageType.SyncInterface); if (null != _zkCrypto) { //sync interface with encryption var assemName = serviceType.ToConfigName(); var assemblyNameEncrypted = _zkCrypto.Encrypt(assemName.ConvertToBytes()); _binWriter.Write(assemblyNameEncrypted.Length); _binWriter.Write(assemblyNameEncrypted); } else { _binWriter.Write(serviceType.ToConfigName()); } //read sync data var len = _binReader.ReadInt32(); //len is zero when AssemblyQualifiedName not same version or not found if (len == 0) throw new TypeAccessException("SyncInterface failed. Type or version of type unknown."); var bytes = _binReader.ReadBytes(len); if (null != _zkCrypto) { _logger.Debug("Encrypted data received from server: {0}", Convert.ToBase64String(bytes)); bytes = _zkCrypto.Decrypt(bytes); _logger.Debug("Decrypted data received from server: {0}", Convert.ToBase64String(bytes)); } _syncInfo = _serializer.Deserialize(bytes); SyncInfoCache.AddOrUpdate(serviceSyncInfoCacheKey, _syncInfo, (t, info) => _syncInfo); } } /// /// Invokes the method with the specified parameters. /// /// Method name and parameter type names. /// Parameters for the method call /// An array of objects containing the return value (index 0) and the parameters used to call /// the method, including any marked as "ref" or "out" protected override object[] InvokeMethod(string metaData, params object[] parameters) { //prevent call to invoke method on more than one thread at a time lock (_syncRoot) { var useCrypto = null != _zkCrypto; var mdata = metaData.Split('|'); //find the matching server side method ident var ident = -1; for (int index = 0; index < _syncInfo.MethodInfos.Length; index++) { var si = _syncInfo.MethodInfos[index]; //first of all the method names must match if (si.MethodName == mdata[0]) { //second of all the parameter types and -count must match if (mdata.Length - 1 == si.ParameterTypes.Length) { var matchingParameterTypes = true; for (int i = 0; i < si.ParameterTypes.Length; i++) if (!mdata[i + 1].Equals(si.ParameterTypes[i])) { matchingParameterTypes = false; break; } if (matchingParameterTypes) { ident = si.MethodIdent; break; } } } } if (ident < 0) throw new Exception(string.Format("Cannot match method '{0}' to its server side equivalent", mdata[0])); //write the message type _binWriter.Write((int)MessageType.MethodInvocation); //write service key index _binWriter.Write(_syncInfo.ServiceKeyIndex); //write the method ident to the server _binWriter.Write(ident); //if encrypted, wrap up key index and params and send len then enc bytes if (useCrypto) { byte[] callData; using (var ms = new MemoryStream()) using (var bw = new BinaryWriter(ms)) { //send the parameters _parameterTransferHelper.SendParameters(_syncInfo.UseCompression, _syncInfo.CompressionThreshold, bw, parameters); callData = ms.ToArray(); } _logger.Debug("Unencrypted data sent to server: {0}", Convert.ToBase64String(callData)); var encData = _zkCrypto.Encrypt(callData); _binWriter.Write(encData.Length); _binWriter.Write(encData); _logger.Debug("Encrypted data sent to server: {0}", Convert.ToBase64String(encData)); } else { //send the parameters _parameterTransferHelper.SendParameters(_syncInfo.UseCompression, _syncInfo.CompressionThreshold, _binWriter, parameters); } _binWriter.Flush(); _stream.Flush(); // Read the result of the invocation. MessageType messageType = (MessageType)_binReader.ReadInt32(); if (messageType == MessageType.UnknownMethod) throw new Exception("Unknown method."); object[] outParams; if (useCrypto) { var len = _binReader.ReadInt32(); var encData = _binReader.ReadBytes(len); _logger.Debug("Encrypted data received from server: {0}", Convert.ToBase64String(encData)); var data = _zkCrypto.Decrypt(encData); _logger.Debug("Decrypted data received from server: {0}", Convert.ToBase64String(data)); using (var ms = new MemoryStream(data)) using (var br = new BinaryReader(ms)) { outParams = _parameterTransferHelper.ReceiveParameters(br); } } else { outParams = _parameterTransferHelper.ReceiveParameters(_binReader); } if (messageType == MessageType.ThrowException) throw (Exception)outParams[0]; MethodSyncInfo methodSyncInfo = _syncInfo.MethodInfos[ident]; var returnType = methodSyncInfo.MethodReturnType.ToType(); if (IsTaskType(returnType) && outParams.Length > 0) { if (returnType.IsGenericType) { MethodInfo methodInfo = typeof(Task).GetMethod(nameof(Task.FromResult)) .MakeGenericMethod(new[] { returnType.GenericTypeArguments[0] }); outParams[0] = methodInfo.Invoke(null, new[] { outParams[0] }); } else { outParams[0] = Task.CompletedTask; } } return outParams; } } private static bool IsTaskType(Type type) { if (type == typeof(Task)) return true; if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Task<>)) return true; return false; } #region IDisposable Members protected override void Dispose(bool disposing) { if (!_disposed) { _disposed = true; //prevent second call to Dispose if (disposing) { try { _binWriter.Write((int)MessageType.TerminateConnection); } finally { if (null != _binWriter) _binWriter.Dispose(); if (null != _binReader) _binReader.Dispose(); } } } } #endregion } } ================================================ FILE: src/ServiceWire/TcpIp/TcpChannel.cs ================================================ using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Threading; namespace ServiceWire.TcpIp { public class TcpChannel : StreamingChannel { private readonly Socket _client; private readonly string _username; private readonly string _password; private readonly TcpChannelIdentifier _channelIdentifier; public TcpChannel(Type serviceType, IPEndPoint endpoint, ISerializer serializer, ICompressor compressor, ILog logger = null, IStats stats = null) : base(serializer, compressor, logger, stats) { _username = null; _password = null; _channelIdentifier = new TcpChannelIdentifier(endpoint); _client = CreateSocket(endpoint, 2500); Initialize(serviceType); } public TcpChannel(Type serviceType, TcpEndPoint endpoint, ISerializer serializer, ICompressor compressor, ILog logger = null, IStats stats = null) : base(serializer, compressor, logger, stats) { _username = null; _password = null; _channelIdentifier = new TcpChannelIdentifier(endpoint.EndPoint); _client = CreateSocket(endpoint.EndPoint, endpoint.ConnectTimeOutMs); Initialize(serviceType); } public TcpChannel(Type serviceType, TcpZkEndPoint endpoint, ISerializer serializer, ICompressor compressor, ILog logger = null, IStats stats = null) : base(serializer, compressor, logger, stats) { if (endpoint == null) throw new ArgumentNullException(nameof(endpoint)); if (endpoint.Username == null) throw new ArgumentNullException(nameof(endpoint.Username)); if (endpoint.Password == null) throw new ArgumentNullException(nameof(endpoint.Password)); _username = endpoint.Username; _password = endpoint.Password; _channelIdentifier = new TcpChannelIdentifier(endpoint.EndPoint); _client = CreateSocket(endpoint.EndPoint, endpoint.ConnectTimeOutMs); Initialize(serviceType); } private Socket CreateSocket(IPEndPoint endpoint, int connectTimeoutMs) { var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) { LingerState = { Enabled = false } }; var connected = false; var connectEventArgs = new SocketAsyncEventArgs { RemoteEndPoint = endpoint }; connectEventArgs.Completed += (sender, e) => { connected = true; }; if (client.ConnectAsync(connectEventArgs)) { while (!connected) { if (!SpinWait.SpinUntil(() => connected, connectTimeoutMs)) { client.Dispose(); throw new TimeoutException($"Unable to connect within {connectTimeoutMs}ms"); } } } if (connectEventArgs.SocketError != SocketError.Success) { client.Dispose(); throw new SocketException((int)connectEventArgs.SocketError); } if (!client.Connected) { client.Dispose(); throw new SocketException((int)SocketError.NotConnected); } return client; } private void Initialize(Type serviceType) { _stream = new BufferedStream(new NetworkStream(_client), 8192); _binReader = new BinaryReader(_stream); _binWriter = new BinaryWriter(_stream); try { SyncInterface(serviceType, _username, _password); } catch { Dispose(true); throw; } } protected override IChannelIdentifier ChannelIdentifier => _channelIdentifier; public override bool IsConnected => _client?.Connected ?? false; protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing) { _client?.Dispose(); } } } } ================================================ FILE: src/ServiceWire/TcpIp/TcpChannelIdentifier.cs ================================================ using System; using System.Net; namespace ServiceWire.TcpIp { internal class TcpChannelIdentifier : IChannelIdentifier, IEquatable { public string IpAddressAndPort { get; private set; } public TcpChannelIdentifier(IPEndPoint ipEndpoint) { IpAddressAndPort = ipEndpoint.ToString(); } public bool Equals(TcpChannelIdentifier other) { return IpAddressAndPort == other.IpAddressAndPort; } public override bool Equals(object obj) { if (obj is TcpChannelIdentifier other) { return Equals(other); } return false; } public override int GetHashCode() { return IpAddressAndPort.GetHashCode(); } } } ================================================ FILE: src/ServiceWire/TcpIp/TcpClient.cs ================================================ using System; using System.Net; namespace ServiceWire.TcpIp { public class TcpClient : IDisposable where TInterface : class { public TInterface Proxy { get; private set; } public TcpClient(TcpEndPoint endpoint, ISerializer serializer = null, ICompressor compressor = null, ILog logger = null, IStats stats = null) { if (null == serializer) serializer = new DefaultSerializer(); if (null == compressor) compressor = new DefaultCompressor(); if (null == logger) logger = new NullLogger(); if (null == stats) stats = new NullStats(); Proxy = TcpProxy.CreateProxy(endpoint, serializer, compressor, logger, stats); } public TcpClient(TcpZkEndPoint endpoint, ISerializer serializer = null, ICompressor compressor = null, ILog logger = null, IStats stats = null) { if (null == serializer) serializer = new DefaultSerializer(); if (null == compressor) compressor = new DefaultCompressor(); if (null == logger) logger = new NullLogger(); if (null == stats) stats = new NullStats(); Proxy = TcpProxy.CreateProxy(endpoint, serializer, compressor, logger, stats); } public TcpClient(IPEndPoint endpoint, ISerializer serializer = null, ICompressor compressor = null, ILog logger = null, IStats stats = null) { if (null == serializer) serializer = new DefaultSerializer(); if (null == compressor) compressor = new DefaultCompressor(); if (null == logger) logger = new NullLogger(); if (null == stats) stats = new NullStats(); Proxy = TcpProxy.CreateProxy(endpoint, serializer, compressor, logger, stats); } [Obsolete] public void InjectLoggerStats(ILog logger, IStats stats) { var channel = Proxy as Channel; channel?.InjectLoggerStats(logger, stats); } public bool IsConnected => (Proxy as TcpChannel)?.IsConnected == true; #region IDisposable Members private bool _disposed; public void Dispose() { //MS recommended dispose pattern - prevents GC from disposing again Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!_disposed) { _disposed = true; //prevent second call to Dispose if (disposing) { (Proxy as TcpChannel)?.Dispose(); } } } #endregion } } ================================================ FILE: src/ServiceWire/TcpIp/TcpEndPoint.cs ================================================ using System.Net; namespace ServiceWire.TcpIp { public class TcpEndPoint { public IPEndPoint EndPoint { get; private set; } public int ConnectTimeOutMs { get; private set; } public TcpEndPoint(IPEndPoint endPoint, int connectTimeOutMs = 2500) { this.EndPoint = endPoint; this.ConnectTimeOutMs = connectTimeOutMs; } } } ================================================ FILE: src/ServiceWire/TcpIp/TcpHost.cs ================================================ using ServiceWire.ZeroKnowledge; using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; namespace ServiceWire.TcpIp { public class TcpHost : Host { private readonly Socket _listener; private readonly IPEndPoint _endPoint; private readonly ManualResetEvent _listenResetEvent = new ManualResetEvent(false); /// /// Constructs an instance of the host and starts listening for incoming connections on any ip address. /// All listener threads are regular background threads. /// /// The port number for incoming requests /// /// /// Only required to support zero knowledge authentication and encryption. /// Inject your own serializer for complex objects and avoid using the Newtonsoft JSON DefaultSerializer. public TcpHost(int port, ILog log = null, IStats stats = null, IZkRepository zkRepository = null, ISerializer serializer = null, ICompressor compressor = null) : base(serializer, compressor) { _endPoint = new IPEndPoint(IPAddress.Any, port); _listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); base.Log = log; base.Stats = stats; base.ZkRepository = zkRepository ?? new ZkNullRepository(); } /// /// Constructs an instance of the host and starts listening for incoming connections on designated endpoint. /// All listener threads are regular background threads. /// /// NOTE: the instance created from the specified type is not automatically thread safe! /// /// /// /// /// Only required to support zero knowledge authentication and encryption. /// Inject your own serializer for complex objects and avoid using the Newtonsoft JSON DefaultSerializer. /// Inject your own compressor and avoid using the standard GZIP DefaultCompressor. public TcpHost(IPEndPoint endpoint, ILog log = null, IStats stats = null, IZkRepository zkRepository = null, ISerializer serializer = null, ICompressor compressor = null) : base(serializer, compressor) { _endPoint = endpoint; _listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); base.Log = log; base.Stats = stats; base.ZkRepository = zkRepository ?? new ZkNullRepository(); } /// /// Gets the end point this host is listening on /// public IPEndPoint EndPoint { get { return _endPoint; } } protected override void StartListener() { _listener.Bind(_endPoint); _listener.Listen(8192); _acceptEventArg = new SocketAsyncEventArgs(); _acceptEventArg.Completed += new EventHandler(acceptEventArg_Completed); Task.Factory.StartNew(Listen, TaskCreationOptions.LongRunning); } private SocketAsyncEventArgs _acceptEventArg; /// /// Listens for incoming tcp requests. /// private void Listen() { try { while (!_disposed) { // Set the event to nonsignaled state. _listenResetEvent.Reset(); _acceptEventArg.AcceptSocket = null; try { // If AcceptAsync returns false - it must be handled synchronously. // https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.socket.acceptasync#system-net-sockets-socket-acceptasync(system-net-sockets-socketasynceventargs) if (!_listener.AcceptAsync(_acceptEventArg)) { AcceptNewClient(_acceptEventArg, true); } } catch (Exception ex) { _log.Error("Listen error: {0}", ex.ToString().Flatten()); break; //break loop on unhandled } // Wait until a connection is made before continuing. _listenResetEvent.WaitOne(); } } catch (Exception e) { _log.Fatal("Listen fatal error: {0}", e.ToString().Flatten()); } } private void acceptEventArg_Completed(object sender, SocketAsyncEventArgs e) { AcceptNewClient(e, false); } private void AcceptNewClient(SocketAsyncEventArgs e, bool processRequestsOnTask) { try { if (e.SocketError != SocketError.Success) { if (!_disposed) _listenResetEvent.Set(); return; } Socket activeSocket = e.AcceptSocket; // Signal the listening thread to continue. _listenResetEvent.Set(); if (processRequestsOnTask) { Task.Factory.StartNew(() => StartProcessingRequestsOnSocket(activeSocket), TaskCreationOptions.LongRunning); } else { StartProcessingRequestsOnSocket(activeSocket); } } catch (Exception fatalException) { _log.Fatal("AcceptNewClient fatal error: {0}", fatalException.ToString().Flatten()); } } private void StartProcessingRequestsOnSocket(Socket activeSocket) { BufferedStream stream = null; try { stream = new BufferedStream(new NetworkStream(activeSocket), 8192); base.ProcessRequest(stream); } catch (Exception ex) { _log.Error("AcceptNewClient_ProcessRequest error: {0}", ex.ToString().Flatten()); } finally { if (null != stream) { stream.Close(); } if (null != activeSocket && activeSocket.Connected) { try { activeSocket.Shutdown(SocketShutdown.Both); } catch (Exception shutdownException) { _log.Error("AcceptNewClient_ActiveSocketShutdown error: {0}", shutdownException.ToString().Flatten()); } try { activeSocket.Close(); } catch (Exception closeException) { _log.Error("AcceptNewClient_ActiveSocketClose error: {0}", closeException.ToString().Flatten()); } } } } #region IDisposable Members private bool _disposed = false; protected override void Dispose(bool disposing) { if (!_disposed) { _disposed = true; //prevent second call to Dispose if (disposing) { _listenResetEvent.Set(); _acceptEventArg?.Dispose(); _listener.Close(); _listenResetEvent.Close(); } } base.Dispose(disposing); } #endregion } } ================================================ FILE: src/ServiceWire/TcpIp/TcpProxy.cs ================================================ using System.Net; namespace ServiceWire.TcpIp { public static class TcpProxy { public static TInterface CreateProxy(TcpZkEndPoint endpoint, ISerializer serializer, ICompressor compressor, ILog logger, IStats stats) where TInterface : class { return ProxyFactory.CreateProxy(typeof(TcpChannel), typeof(TcpZkEndPoint), endpoint, serializer, compressor, logger, stats); } public static TInterface CreateProxy(TcpEndPoint endpoint, ISerializer serializer, ICompressor compressor, ILog logger, IStats stats) where TInterface : class { return ProxyFactory.CreateProxy(typeof(TcpChannel), typeof(TcpEndPoint), endpoint, serializer, compressor, logger, stats); } public static TInterface CreateProxy(IPEndPoint endpoint, ISerializer serializer, ICompressor compressor, ILog logger, IStats stats) where TInterface : class { return ProxyFactory.CreateProxy(typeof(TcpChannel), typeof(IPEndPoint), endpoint, serializer, compressor, logger, stats); } } } ================================================ FILE: src/ServiceWire/TcpIp/TcpZkEndPoint.cs ================================================ using System.Net; namespace ServiceWire.TcpIp { public class TcpZkEndPoint { public string Username { get; set; } public string Password { get; set; } public IPEndPoint EndPoint { get; set; } public int ConnectTimeOutMs { get; set; } public TcpZkEndPoint(string username, string password, IPEndPoint endPoint, int connectTimeOutMs = 2500) { this.Username = username; this.Password = password; this.EndPoint = endPoint; this.ConnectTimeOutMs = connectTimeOutMs; } } } ================================================ FILE: src/ServiceWire/ZeroKnowledge/IZkRepository.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ServiceWire.ZeroKnowledge { public interface IZkRepository { ZkPasswordHash GetPasswordHashSet(string username); } public class ZkNullRepository : IZkRepository { public ZkPasswordHash GetPasswordHashSet(string username) { return null; } } } ================================================ FILE: src/ServiceWire/ZeroKnowledge/ZkBigInt.cs ================================================ /* * The MIT License (MIT) * Copyright (c) 2007 Scott Garland * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated * documentation files (the "Software"), to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ using System; using System.Collections.Generic; namespace ServiceWire.ZeroKnowledge { using DType = System.UInt32; // This could be UInt32, UInt16 or Byte; not UInt64. #region DigitsArray internal class DigitsArray { internal DigitsArray(int size) { Allocate(size, 0); } internal DigitsArray(int size, int used) { Allocate(size, used); } internal DigitsArray(DType[] copyFrom) { Allocate(copyFrom.Length); CopyFrom(copyFrom, 0, 0, copyFrom.Length); ResetDataUsed(); } internal DigitsArray(DigitsArray copyFrom) { Allocate(copyFrom.Count, copyFrom.DataUsed); Array.Copy(copyFrom.m_data, 0, m_data, 0, copyFrom.Count); } private DType[] m_data; internal static readonly DType AllBits; // = ~((DType)0); internal static readonly DType HiBitSet; // = 0x80000000; internal static int DataSizeOf { get { return sizeof(DType); } } internal static int DataSizeBits { get { return sizeof(DType) * 8; } } static DigitsArray() { unchecked { AllBits = (DType)~((DType)0); HiBitSet = (DType)(((DType)1) << (DataSizeBits) - 1); } } public void Allocate(int size) { Allocate(size, 0); } public void Allocate(int size, int used) { m_data = new DType[size + 1]; m_dataUsed = used; } internal void CopyFrom(DType[] source, int sourceOffset, int offset, int length) { Array.Copy(source, sourceOffset, m_data, 0, length); } internal void CopyTo(DType[] array, int offset, int length) { Array.Copy(m_data, 0, array, offset, length); } internal DType this[int index] { get { if (index < m_dataUsed) return m_data[index]; return (IsNegative ? (DType)AllBits : (DType)0); } set { m_data[index] = value; } } private int m_dataUsed; internal int DataUsed { get { return m_dataUsed; } set { m_dataUsed = value; } } internal int Count { get { return m_data.Length; } } internal bool IsZero { get { return m_dataUsed == 0 || (m_dataUsed == 1 && m_data[0] == 0); } } internal bool IsNegative { get { return (m_data[m_data.Length - 1] & HiBitSet) == HiBitSet; } } internal void ResetDataUsed() { m_dataUsed = m_data.Length; if (IsNegative) { while (m_dataUsed > 1 && m_data[m_dataUsed - 1] == AllBits) { --m_dataUsed; } m_dataUsed++; } else { while (m_dataUsed > 1 && m_data[m_dataUsed - 1] == 0) { --m_dataUsed; } if (m_dataUsed == 0) { m_dataUsed = 1; } } } internal int ShiftRight(int shiftCount) { return ShiftRight(m_data, shiftCount); } internal static int ShiftRight(DType[] buffer, int shiftCount) { int shiftAmount = DigitsArray.DataSizeBits; int invShift = 0; int bufLen = buffer.Length; while (bufLen > 1 && buffer[bufLen - 1] == 0) { bufLen--; } for (int count = shiftCount; count > 0; count -= shiftAmount) { if (count < shiftAmount) { shiftAmount = count; invShift = DigitsArray.DataSizeBits - shiftAmount; } ulong carry = 0; for (int i = bufLen - 1; i >= 0; i--) { ulong val = ((ulong)buffer[i]) >> shiftAmount; val |= carry; carry = ((ulong)buffer[i]) << invShift; buffer[i] = (DType)(val); } } while (bufLen > 1 && buffer[bufLen - 1] == 0) { bufLen--; } return bufLen; } internal int ShiftLeft(int shiftCount) { return ShiftLeft(m_data, shiftCount); } internal static int ShiftLeft(DType[] buffer, int shiftCount) { int shiftAmount = DigitsArray.DataSizeBits; int bufLen = buffer.Length; while (bufLen > 1 && buffer[bufLen - 1] == 0) { bufLen--; } for (int count = shiftCount; count > 0; count -= shiftAmount) { if (count < shiftAmount) { shiftAmount = count; } ulong carry = 0; for (int i = 0; i < bufLen; i++) { ulong val = ((ulong)buffer[i]) << shiftAmount; val |= carry; buffer[i] = (DType)(val & DigitsArray.AllBits); carry = (val >> DigitsArray.DataSizeBits); } if (carry != 0) { if (bufLen + 1 <= buffer.Length) { buffer[bufLen] = (DType)carry; bufLen++; carry = 0; } else { throw new OverflowException(); } } } return bufLen; } internal int ShiftLeftWithoutOverflow(int shiftCount) { List temporary = new List(m_data); int shiftAmount = DigitsArray.DataSizeBits; for (int count = shiftCount; count > 0; count -= shiftAmount) { if (count < shiftAmount) { shiftAmount = count; } ulong carry = 0; for (int i = 0; i < temporary.Count; i++) { ulong val = ((ulong)temporary[i]) << shiftAmount; val |= carry; temporary[i] = (DType)(val & DigitsArray.AllBits); carry = (val >> DigitsArray.DataSizeBits); } if (carry != 0) { temporary.Add(0); temporary[temporary.Count - 1] = (DType)carry; } } m_data = new DType[temporary.Count]; temporary.CopyTo(m_data); return m_data.Length; } } #endregion /// /// Represents a integer of abitrary length. /// /// /// /// A BigInteger object is immutable like System.String. The object can not be modifed, and new BigInteger objects are /// created by using the operations of existing BigInteger objects. /// /// /// Internally a BigInteger object is an array of ? that is represents the digits of the n-place integer. Negative BigIntegers /// are stored internally as 1's complements, thus every BigInteger object contains 1 or more padding elements to hold the sign. /// /// /// /// /// public class MainProgram /// { /// [STAThread] /// public static void Main(string[] args) /// { /// BigInteger a = new BigInteger(25); /// a = a + 100; /// /// BigInteger b = new BigInteger("139435810094598308945890230913"); /// /// BigInteger c = b / a; /// BigInteger d = b % a; /// /// BigInteger e = (c * a) + d; /// if (e != b) /// { /// Console.WriteLine("Can never be true."); /// } /// } /// /// public class BigInteger { private DigitsArray m_digits; #region Constructors /// /// Create a BigInteger with an integer value of 0. /// public BigInteger() { m_digits = new DigitsArray(1, 1); } /// /// Creates a BigInteger with the value of the operand. /// /// A long. public BigInteger(long number) { m_digits = new DigitsArray((8 / DigitsArray.DataSizeOf) + 1, 0); while (number != 0 && m_digits.DataUsed < m_digits.Count) { m_digits[m_digits.DataUsed] = (DType)(number & DigitsArray.AllBits); number >>= DigitsArray.DataSizeBits; m_digits.DataUsed++; } m_digits.ResetDataUsed(); } /// /// Creates a BigInteger with the value of the operand. Can never be negative. /// /// A unsigned long. public BigInteger(ulong number) { m_digits = new DigitsArray((8 / DigitsArray.DataSizeOf) + 1, 0); while (number != 0 && m_digits.DataUsed < m_digits.Count) { m_digits[m_digits.DataUsed] = (DType)(number & DigitsArray.AllBits); number >>= DigitsArray.DataSizeBits; m_digits.DataUsed++; } m_digits.ResetDataUsed(); } /// /// Creates a BigInteger initialized from the byte array. /// /// public BigInteger(byte[] array) { ConstructFrom(array, 0, array.Length); } /// /// Creates a BigInteger initialized from the byte array ending at . /// /// A byte array. /// Int number of bytes to use. public BigInteger(byte[] array, int length) { ConstructFrom(array, 0, length); } /// /// Creates a BigInteger initialized from bytes starting at . /// /// A byte array. /// Int offset into the . /// Int number of bytes. public BigInteger(byte[] array, int offset, int length) { ConstructFrom(array, offset, length); } private void ConstructFrom(byte[] array, int offset, int length) { if (array == null) { throw new ArgumentNullException("array"); } if (offset > array.Length || length > array.Length) { throw new ArgumentOutOfRangeException("offset"); } if (length > array.Length || (offset + length) > array.Length) { throw new ArgumentOutOfRangeException("length"); } int estSize = length / 4; int leftOver = length & 3; if (leftOver != 0) { ++estSize; } m_digits = new DigitsArray(estSize + 1, 0); // alloc one extra since we can't init -'s from here. for (int i = offset + length - 1, j = 0; (i - offset) >= 3; i -= 4, j++) { m_digits[j] = (DType)((array[i - 3] << 24) + (array[i - 2] << 16) + (array[i - 1] << 8) + array[i]); m_digits.DataUsed++; } DType accumulator = 0; for (int i = leftOver; i > 0; i--) { DType digit = array[offset + leftOver - i]; digit = (digit << ((i - 1) * 8)); accumulator |= digit; } m_digits[m_digits.DataUsed] = accumulator; m_digits.ResetDataUsed(); } /// /// Creates a BigInteger in base-10 from the parameter. /// /// /// The new BigInteger is negative if the has a leading - (minus). /// /// A string public BigInteger(string digits) { Construct(digits, 10); } /// /// Creates a BigInteger in base and value from the parameters. /// /// /// The new BigInteger is negative if the has a leading - (minus). /// /// A string /// A int between 2 and 36. public BigInteger(string digits, int radix) { Construct(digits, radix); } private void Construct(string digits, int radix) { if (digits == null) { throw new ArgumentNullException("digits"); } BigInteger multiplier = new BigInteger(1); BigInteger result = new BigInteger(); digits = digits.ToUpper(System.Globalization.CultureInfo.CurrentCulture).Trim(); int nDigits = (digits[0] == '-' ? 1 : 0); for (int idx = digits.Length - 1; idx >= nDigits ; idx--) { int d = (int)digits[idx]; if (d >= '0' && d <= '9') { d -= '0'; } else if (d >= 'A' && d <= 'Z') { d = (d - 'A') + 10; } else { throw new ArgumentOutOfRangeException("digits"); } if (d >= radix) { throw new ArgumentOutOfRangeException("digits"); } result += (multiplier * d); multiplier *= radix; } if (digits[0] == '-') { result = -result; } this.m_digits = result.m_digits; } /// /// Copy constructor, doesn't copy the digits parameter, assumes this owns the DigitsArray. /// /// The parameter is saved and reset. /// private BigInteger(DigitsArray digits) { digits.ResetDataUsed(); this.m_digits = digits; } #endregion #region Public Properties /// /// A bool value that is true when the BigInteger is negative (less than zero). /// /// /// A bool value that is true when the BigInteger is negative (less than zero). /// public bool IsNegative { get { return m_digits.IsNegative; } } /// /// A bool value that is true when the BigInteger is exactly zero. /// /// /// A bool value that is true when the BigInteger is exactly zero. /// public bool IsZero { get { return m_digits.IsZero; } } #endregion #region Implicit Type Operators Overloads /// /// Creates a BigInteger from a long. /// /// A long. /// A BigInteger initialzed by . public static implicit operator BigInteger(long value) { return (new BigInteger(value)); } /// /// Creates a BigInteger from a ulong. /// /// A ulong. /// A BigInteger initialzed by . public static implicit operator BigInteger(ulong value) { return (new BigInteger(value)); } /// /// Creates a BigInteger from a int. /// /// A int. /// A BigInteger initialzed by . public static implicit operator BigInteger(int value) { return (new BigInteger((long)value)); } /// /// Creates a BigInteger from a uint. /// /// A uint. /// A BigInteger initialzed by . public static implicit operator BigInteger(uint value) { return (new BigInteger((ulong)value)); } #endregion #region Addition and Subtraction Operator Overloads /// /// Adds two BigIntegers and returns a new BigInteger that represents the sum. /// /// A BigInteger /// A BigInteger /// The BigInteger result of adding and . public static BigInteger operator + (BigInteger leftSide, BigInteger rightSide) { int size = System.Math.Max(leftSide.m_digits.DataUsed, rightSide.m_digits.DataUsed); DigitsArray da = new DigitsArray(size + 1); long carry = 0; for (int i = 0; i < da.Count; i++) { long sum = (long)leftSide.m_digits[i] + (long)rightSide.m_digits[i] + carry; carry = (long)(sum >> DigitsArray.DataSizeBits); da[i] = (DType)(sum & DigitsArray.AllBits); } return new BigInteger(da); } /// /// Adds two BigIntegers and returns a new BigInteger that represents the sum. /// /// A BigInteger /// A BigInteger /// The BigInteger result of adding and . public static BigInteger Add(BigInteger leftSide, BigInteger rightSide) { return leftSide - rightSide; } /// /// Increments the BigInteger operand by 1. /// /// The BigInteger operand. /// The value of incremented by 1. public static BigInteger operator ++ (BigInteger leftSide) { return (leftSide + 1); } /// /// Increments the BigInteger operand by 1. /// /// The BigInteger operand. /// The value of incremented by 1. public static BigInteger Increment(BigInteger leftSide) { return (leftSide + 1); } /// /// Substracts two BigIntegers and returns a new BigInteger that represents the sum. /// /// A BigInteger /// A BigInteger /// The BigInteger result of substracting and . public static BigInteger operator - (BigInteger leftSide, BigInteger rightSide) { int size = System.Math.Max(leftSide.m_digits.DataUsed, rightSide.m_digits.DataUsed) + 1; DigitsArray da = new DigitsArray(size); long carry = 0; for (int i = 0; i < da.Count; i++) { long diff = (long)leftSide.m_digits[i] - (long)rightSide.m_digits[i] - carry; da[i] = (DType)(diff & DigitsArray.AllBits); da.DataUsed++; carry = ((diff < 0) ? 1 : 0); } return new BigInteger(da); } /// /// Substracts two BigIntegers and returns a new BigInteger that represents the sum. /// /// A BigInteger /// A BigInteger /// The BigInteger result of substracting and . public static BigInteger Subtract(BigInteger leftSide, BigInteger rightSide) { return leftSide - rightSide; } /// /// Decrements the BigInteger operand by 1. /// /// The BigInteger operand. /// The value of the decremented by 1. public static BigInteger operator -- (BigInteger leftSide) { return (leftSide - 1); } /// /// Decrements the BigInteger operand by 1. /// /// The BigInteger operand. /// The value of the decremented by 1. public static BigInteger Decrement(BigInteger leftSide) { return (leftSide - 1); } #endregion #region Negate Operator Overload /// /// Negates the BigInteger, that is, if the BigInteger is negative return a positive BigInteger, and if the /// BigInteger is negative return the postive. /// /// A BigInteger operand. /// The value of the negated. public static BigInteger operator - (BigInteger leftSide) { if (object.ReferenceEquals(leftSide, null)) { throw new ArgumentNullException("leftSide"); } if (leftSide.IsZero) { return new BigInteger(0); } DigitsArray da = new DigitsArray(leftSide.m_digits.DataUsed + 1, leftSide.m_digits.DataUsed + 1); for (int i = 0; i < da.Count; i++) { da[i] = (DType)(~(leftSide.m_digits[i])); } // add one to result (1's complement + 1) bool carry = true; int index = 0; while (carry && index < da.Count) { long val = (long)da[index] + 1; da[index] = (DType)(val & DigitsArray.AllBits); carry = ((val >> DigitsArray.DataSizeBits) > 0); index++; } return new BigInteger(da); } /// /// Negates the BigInteger, that is, if the BigInteger is negative return a positive BigInteger, and if the /// BigInteger is negative return the postive. /// /// The value of the negated. public BigInteger Negate() { return -this; } /// /// Creates a BigInteger absolute value of the operand. /// /// A BigInteger. /// A BigInteger that represents the absolute value of . public static BigInteger Abs(BigInteger leftSide) { if (object.ReferenceEquals(leftSide, null)) { throw new ArgumentNullException("leftSide"); } if (leftSide.IsNegative) { return -leftSide; } return leftSide; } #endregion #region Multiplication, Division and Modulus Operators /// /// Multiply two BigIntegers returning the result. /// /// /// See Knuth. /// /// A BigInteger. /// A BigInteger /// public static BigInteger operator * (BigInteger leftSide, BigInteger rightSide) { if (object.ReferenceEquals(leftSide, null)) { throw new ArgumentNullException("leftSide"); } if (object.ReferenceEquals(rightSide, null)) { throw new ArgumentNullException("rightSide"); } bool leftSideNeg = leftSide.IsNegative; bool rightSideNeg = rightSide.IsNegative; leftSide = Abs(leftSide); rightSide = Abs(rightSide); DigitsArray da = new DigitsArray(leftSide.m_digits.DataUsed + rightSide.m_digits.DataUsed); da.DataUsed = da.Count; for (int i = 0; i < leftSide.m_digits.DataUsed; i++) { ulong carry = 0; for (int j = 0, k = i; j < rightSide.m_digits.DataUsed; j++, k++) { ulong val = ((ulong)leftSide.m_digits[i] * (ulong)rightSide.m_digits[j]) + (ulong)da[k] + carry; da[k] = (DType)(val & DigitsArray.AllBits); carry = (val >> DigitsArray.DataSizeBits); } if (carry != 0) { da[i + rightSide.m_digits.DataUsed] = (DType)carry; } } //da.ResetDataUsed(); BigInteger result = new BigInteger(da); return (leftSideNeg != rightSideNeg ? -result : result); } /// /// Multiply two BigIntegers returning the result. /// /// A BigInteger. /// A BigInteger /// public static BigInteger Multiply(BigInteger leftSide, BigInteger rightSide) { return leftSide * rightSide; } /// /// Divide a BigInteger by another BigInteger and returning the result. /// /// A BigInteger divisor. /// A BigInteger dividend. /// The BigInteger result. public static BigInteger operator / (BigInteger leftSide, BigInteger rightSide) { if (leftSide == null) { throw new ArgumentNullException("leftSide"); } if (rightSide == null) { throw new ArgumentNullException("rightSide"); } if (rightSide.IsZero) { throw new DivideByZeroException(); } bool divisorNeg = rightSide.IsNegative; bool dividendNeg = leftSide.IsNegative; leftSide = Abs(leftSide); rightSide = Abs(rightSide); if (leftSide < rightSide) { return new BigInteger(0); } BigInteger quotient; BigInteger remainder; Divide(leftSide, rightSide, out quotient, out remainder); return (dividendNeg != divisorNeg ? -quotient : quotient); } /// /// Divide a BigInteger by another BigInteger and returning the result. /// /// A BigInteger divisor. /// A BigInteger dividend. /// The BigInteger result. public static BigInteger Divide(BigInteger leftSide, BigInteger rightSide) { return leftSide / rightSide; } private static void Divide(BigInteger leftSide, BigInteger rightSide, out BigInteger quotient, out BigInteger remainder) { if (leftSide.IsZero) { quotient = new BigInteger(); remainder = new BigInteger(); return; } if (rightSide.m_digits.DataUsed == 1) { SingleDivide(leftSide, rightSide, out quotient, out remainder); } else { MultiDivide(leftSide, rightSide, out quotient, out remainder); } } private static void MultiDivide(BigInteger leftSide, BigInteger rightSide, out BigInteger quotient, out BigInteger remainder) { if (rightSide.IsZero) { throw new DivideByZeroException(); } DType val = rightSide.m_digits[rightSide.m_digits.DataUsed - 1]; int d = 0; for (uint mask = DigitsArray.HiBitSet; mask != 0 && (val & mask) == 0; mask >>= 1) { d++; } int remainderLen = leftSide.m_digits.DataUsed + 1; DType[] remainderDat = new DType[remainderLen]; leftSide.m_digits.CopyTo(remainderDat, 0, leftSide.m_digits.DataUsed); DigitsArray.ShiftLeft(remainderDat, d); rightSide = rightSide << d; ulong firstDivisor = rightSide.m_digits[rightSide.m_digits.DataUsed - 1]; ulong secondDivisor = (rightSide.m_digits.DataUsed < 2 ? (DType)0 : rightSide.m_digits[rightSide.m_digits.DataUsed - 2]); int divisorLen = rightSide.m_digits.DataUsed + 1; DigitsArray dividendPart = new DigitsArray(divisorLen, divisorLen); DType[] result = new DType[leftSide.m_digits.Count + 1]; int resultPos = 0; ulong carryBit = (ulong)0x1 << DigitsArray.DataSizeBits; // 0x100000000 for (int j = remainderLen - rightSide.m_digits.DataUsed, pos = remainderLen - 1; j > 0; j--, pos--) { ulong dividend = ((ulong)remainderDat[pos] << DigitsArray.DataSizeBits) + (ulong)remainderDat[pos - 1]; ulong qHat = (dividend / firstDivisor); ulong rHat = (dividend % firstDivisor); while (pos >= 2) { if (qHat == carryBit || (qHat * secondDivisor) > ((rHat << DigitsArray.DataSizeBits) + remainderDat[pos - 2])) { qHat--; rHat += firstDivisor; if (rHat < carryBit) { continue; } } break; } for (int h = 0; h < divisorLen; h++) { dividendPart[divisorLen - h - 1] = remainderDat[pos - h]; } BigInteger dTemp = new BigInteger(dividendPart); BigInteger rTemp = rightSide * (long)qHat; while (rTemp > dTemp) { qHat--; rTemp -= rightSide; } rTemp = dTemp - rTemp; for (int h = 0; h < divisorLen; h++) { remainderDat[pos - h] = rTemp.m_digits[rightSide.m_digits.DataUsed - h]; } result[resultPos++] = (DType)qHat; } Array.Reverse(result, 0, resultPos); quotient = new BigInteger(new DigitsArray(result)); int n = DigitsArray.ShiftRight(remainderDat, d); DigitsArray rDA = new DigitsArray(n, n); rDA.CopyFrom(remainderDat, 0, 0, rDA.DataUsed); remainder = new BigInteger(rDA); } private static void SingleDivide(BigInteger leftSide, BigInteger rightSide, out BigInteger quotient, out BigInteger remainder) { if (rightSide.IsZero) { throw new DivideByZeroException(); } DigitsArray remainderDigits = new DigitsArray(leftSide.m_digits); remainderDigits.ResetDataUsed(); int pos = remainderDigits.DataUsed - 1; ulong divisor = (ulong)rightSide.m_digits[0]; ulong dividend = (ulong)remainderDigits[pos]; DType[] result = new DType[leftSide.m_digits.Count]; leftSide.m_digits.CopyTo(result, 0, result.Length); int resultPos = 0; if (dividend >= divisor) { result[resultPos++] = (DType)(dividend / divisor); remainderDigits[pos] = (DType)(dividend % divisor); } pos--; while (pos >= 0) { dividend = ((ulong)(remainderDigits[pos + 1]) << DigitsArray.DataSizeBits) + (ulong)remainderDigits[pos]; result[resultPos++] = (DType)(dividend / divisor); remainderDigits[pos + 1] = 0; remainderDigits[pos--] = (DType)(dividend % divisor); } remainder = new BigInteger(remainderDigits); DigitsArray quotientDigits = new DigitsArray(resultPos + 1, resultPos); int j = 0; for (int i = quotientDigits.DataUsed - 1; i >= 0; i--, j++) { quotientDigits[j] = result[i]; } quotient = new BigInteger(quotientDigits); } /// /// Perform the modulus of a BigInteger with another BigInteger and return the result. /// /// A BigInteger divisor. /// A BigInteger dividend. /// The BigInteger result. public static BigInteger operator % (BigInteger leftSide, BigInteger rightSide) { if (leftSide == null) { throw new ArgumentNullException("leftSide"); } if (rightSide == null) { throw new ArgumentNullException("rightSide"); } if (rightSide.IsZero) { throw new DivideByZeroException(); } BigInteger quotient; BigInteger remainder; bool dividendNeg = leftSide.IsNegative; leftSide = Abs(leftSide); rightSide = Abs(rightSide); if (leftSide < rightSide) { return leftSide; } Divide(leftSide, rightSide, out quotient, out remainder); return (dividendNeg ? -remainder : remainder); } /// /// Perform the modulus of a BigInteger with another BigInteger and return the result. /// /// A BigInteger divisor. /// A BigInteger dividend. /// The BigInteger result. public static BigInteger Modulus(BigInteger leftSide, BigInteger rightSide) { return leftSide % rightSide; } #endregion #region Bitwise Operator Overloads public static BigInteger operator & (BigInteger leftSide, BigInteger rightSide) { int len = System.Math.Max(leftSide.m_digits.DataUsed, rightSide.m_digits.DataUsed); DigitsArray da = new DigitsArray(len, len); for (int idx = 0; idx < len; idx++) { da[idx] = (DType)(leftSide.m_digits[idx] & rightSide.m_digits[idx]); } return new BigInteger(da); } public static BigInteger BitwiseAnd(BigInteger leftSide, BigInteger rightSide) { return leftSide & rightSide; } public static BigInteger operator | (BigInteger leftSide, BigInteger rightSide) { int len = System.Math.Max(leftSide.m_digits.DataUsed, rightSide.m_digits.DataUsed); DigitsArray da = new DigitsArray(len, len); for (int idx = 0; idx < len; idx++) { da[idx] = (DType)(leftSide.m_digits[idx] | rightSide.m_digits[idx]); } return new BigInteger(da); } public static BigInteger BitwiseOr(BigInteger leftSide, BigInteger rightSide) { return leftSide | rightSide; } public static BigInteger operator ^ (BigInteger leftSide, BigInteger rightSide) { int len = System.Math.Max(leftSide.m_digits.DataUsed, rightSide.m_digits.DataUsed); DigitsArray da = new DigitsArray(len, len); for (int idx = 0; idx < len; idx++) { da[idx] = (DType)(leftSide.m_digits[idx] ^ rightSide.m_digits[idx]); } return new BigInteger(da); } public static BigInteger Xor(BigInteger leftSide, BigInteger rightSide) { return leftSide ^ rightSide; } public static BigInteger operator ~ (BigInteger leftSide) { DigitsArray da = new DigitsArray(leftSide.m_digits.Count); for(int idx = 0; idx < da.Count; idx++) { da[idx] = (DType)(~(leftSide.m_digits[idx])); } return new BigInteger(da); } public static BigInteger OnesComplement(BigInteger leftSide) { return ~leftSide; } #endregion #region Left and Right Shift Operator Overloads public static BigInteger operator << (BigInteger leftSide, int shiftCount) { if (leftSide == null) { throw new ArgumentNullException("leftSide"); } DigitsArray da = new DigitsArray(leftSide.m_digits); da.DataUsed = da.ShiftLeftWithoutOverflow(shiftCount); return new BigInteger(da); } public static BigInteger LeftShift(BigInteger leftSide, int shiftCount) { return leftSide << shiftCount; } public static BigInteger operator >> (BigInteger leftSide, int shiftCount) { if (leftSide == null) { throw new ArgumentNullException("leftSide"); } DigitsArray da = new DigitsArray(leftSide.m_digits); da.DataUsed = da.ShiftRight(shiftCount); if (leftSide.IsNegative) { for (int i = da.Count - 1; i >= da.DataUsed; i--) { da[i] = DigitsArray.AllBits; } DType mask = DigitsArray.HiBitSet; for (int i = 0; i < DigitsArray.DataSizeBits; i++) { if ((da[da.DataUsed - 1] & mask) == DigitsArray.HiBitSet) { break; } da[da.DataUsed - 1] |= mask; mask >>= 1; } da.DataUsed = da.Count; } return new BigInteger(da); } public static BigInteger RightShift(BigInteger leftSide, int shiftCount) { if (leftSide == null) { throw new ArgumentNullException("leftSide"); } return leftSide >> shiftCount; } #endregion #region Relational Operator Overloads /// /// Compare this instance to a specified object and returns indication of their relative value. /// /// An object to compare, or a null reference (Nothing in Visual Basic). /// A signed number indicating the relative value of this instance and value. /// /// /// Return Value /// Description /// /// /// Less than zero /// This instance is less than value. /// /// /// Zero /// This instance is equal to value. /// /// /// Greater than zero /// /// This instance is greater than value. /// -or- /// value is a null reference (Nothing in Visual Basic). /// /// /// /// public int CompareTo(BigInteger value) { return Compare(this, value); } /// /// Compare two objects and return an indication of their relative value. /// /// An object to compare, or a null reference (Nothing in Visual Basic). /// An object to compare, or a null reference (Nothing in Visual Basic). /// A signed number indicating the relative value of this instance and value. /// /// /// Return Value /// Description /// /// /// Less than zero /// This instance is less than value. /// /// /// Zero /// This instance is equal to value. /// /// /// Greater than zero /// /// This instance is greater than value. /// -or- /// value is a null reference (Nothing in Visual Basic). /// /// /// /// public static int Compare(BigInteger leftSide, BigInteger rightSide) { if (object.ReferenceEquals(leftSide, rightSide)) { return 0; } if (object.ReferenceEquals(leftSide, null)) { throw new ArgumentNullException("leftSide"); } if (object.ReferenceEquals(rightSide, null)) { throw new ArgumentNullException("rightSide"); } if (leftSide > rightSide) return 1; if (leftSide == rightSide) return 0; return -1; } public static bool operator == (BigInteger leftSide, BigInteger rightSide) { if (object.ReferenceEquals(leftSide, rightSide)) { return true; } if (object.ReferenceEquals(leftSide, null ) || object.ReferenceEquals(rightSide, null )) { return false; } if (leftSide.IsNegative != rightSide.IsNegative) { return false; } return leftSide.Equals(rightSide); } public static bool operator != (BigInteger leftSide, BigInteger rightSide) { return !(leftSide == rightSide); } public static bool operator > (BigInteger leftSide, BigInteger rightSide) { if (object.ReferenceEquals(leftSide, null)) { throw new ArgumentNullException("leftSide"); } if (object.ReferenceEquals(rightSide, null)) { throw new ArgumentNullException("rightSide"); } if (leftSide.IsNegative != rightSide.IsNegative) { return rightSide.IsNegative; } if (leftSide.m_digits.DataUsed != rightSide.m_digits.DataUsed) { return leftSide.m_digits.DataUsed > rightSide.m_digits.DataUsed; } for (int idx = leftSide.m_digits.DataUsed - 1; idx >= 0; idx--) { if (leftSide.m_digits[idx] != rightSide.m_digits[idx]) { return (leftSide.m_digits[idx] > rightSide.m_digits[idx]); } } return false; } public static bool operator < (BigInteger leftSide, BigInteger rightSide) { if (object.ReferenceEquals(leftSide, null)) { throw new ArgumentNullException("leftSide"); } if (object.ReferenceEquals(rightSide, null)) { throw new ArgumentNullException("rightSide"); } if (leftSide.IsNegative != rightSide.IsNegative) { return leftSide.IsNegative; } if (leftSide.m_digits.DataUsed != rightSide.m_digits.DataUsed) { return leftSide.m_digits.DataUsed < rightSide.m_digits.DataUsed; } for (int idx = leftSide.m_digits.DataUsed - 1; idx >= 0; idx--) { if (leftSide.m_digits[idx] != rightSide.m_digits[idx]) { return (leftSide.m_digits[idx] < rightSide.m_digits[idx]); } } return false; } public static bool operator >= (BigInteger leftSide, BigInteger rightSide) { return Compare(leftSide, rightSide) >= 0; } public static bool operator <= (BigInteger leftSide, BigInteger rightSide) { return Compare(leftSide, rightSide) <= 0; } #endregion #region Object Overrides /// /// Determines whether two Object instances are equal. /// /// An Object to compare with this instance. /// /// System.Object public override bool Equals(object obj) { if (object.ReferenceEquals(obj, null)) { return false; } if (object.ReferenceEquals(this, obj)) { return true; } BigInteger c = (BigInteger)obj; if (this.m_digits.DataUsed != c.m_digits.DataUsed) { return false; } for (int idx = 0; idx < this.m_digits.DataUsed; idx++) { if (this.m_digits[idx] != c.m_digits[idx]) { return false; } } return true; } /// /// Returns the hash code for this instance. /// /// A 32-bit signed integer has code. /// System.Object public override int GetHashCode() { return this.m_digits.GetHashCode(); } /// /// Converts the numeric value of this instance to its equivalent base 10 string representation. /// /// A String in base 10. /// System.Object public override string ToString() { return ToString(10); } #endregion #region Type Conversion Methods /// /// Converts the numeric value of this instance to its equivalent string representation in specified base. /// /// Int radix between 2 and 36 /// A string. public string ToString(int radix) { if (radix < 2 || radix > 36) { throw new ArgumentOutOfRangeException("radix"); } if (IsZero) { return "0"; } BigInteger a = this; bool negative = a.IsNegative; a = Abs(this); BigInteger quotient; BigInteger remainder; BigInteger biRadix = new BigInteger(radix); const string charSet = "0123456789abcdefghijklmnopqrstuvwxyz"; System.Collections.ArrayList al = new System.Collections.ArrayList(); while (a.m_digits.DataUsed > 1 || (a.m_digits.DataUsed == 1 && a.m_digits[0] != 0)) { Divide(a, biRadix, out quotient, out remainder); al.Insert(0, charSet[(int)remainder.m_digits[0]]); a = quotient; } string result = new String((char[])al.ToArray(typeof(char))); if (radix == 10 && negative) { return "-" + result; } return result; } /// /// Returns string in hexidecimal of the internal digit representation. /// /// /// This is not the same as ToString(16). This method does not return the sign, but instead /// dumps the digits array into a string representation in base 16. /// /// A string in base 16. public string ToHexString() { System.Text.StringBuilder sb = new System.Text.StringBuilder(); sb.AppendFormat("{0:X}", m_digits[m_digits.DataUsed - 1]); string f = "{0:X" + (2 * DigitsArray.DataSizeOf) + "}"; for (int i = m_digits.DataUsed - 2; i >= 0; i--) { sb.AppendFormat(f, m_digits[i]); } return sb.ToString(); } /// /// Returns BigInteger as System.Int16 if possible. /// /// /// Int value of BigInteger /// When BigInteger is too large to fit into System.Int16 public static int ToInt16(BigInteger value) { if (object.ReferenceEquals(value, null)) { throw new ArgumentNullException("value"); } return System.Int16.Parse(value.ToString(), System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.CurrentCulture); } /// /// Returns BigInteger as System.UInt16 if possible. /// /// /// /// When BigInteger is too large to fit into System.UInt16 public static uint ToUInt16(BigInteger value) { if (object.ReferenceEquals(value, null)) { throw new ArgumentNullException("value"); } return System.UInt16.Parse(value.ToString(), System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.CurrentCulture); } /// /// Returns BigInteger as System.Int32 if possible. /// /// /// /// When BigInteger is too large to fit into System.Int32 public static int ToInt32(BigInteger value) { if (object.ReferenceEquals(value, null)) { throw new ArgumentNullException("value"); } return System.Int32.Parse(value.ToString(), System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.CurrentCulture); } /// /// Returns BigInteger as System.UInt32 if possible. /// /// /// /// When BigInteger is too large to fit into System.UInt32 public static uint ToUInt32(BigInteger value) { if (object.ReferenceEquals(value, null)) { throw new ArgumentNullException("value"); } return System.UInt32.Parse(value.ToString(), System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.CurrentCulture); } /// /// Returns BigInteger as System.Int64 if possible. /// /// /// /// When BigInteger is too large to fit into System.Int64 public static long ToInt64(BigInteger value) { if (object.ReferenceEquals(value, null)) { throw new ArgumentNullException("value"); } return System.Int64.Parse(value.ToString(), System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.CurrentCulture); } /// /// Returns BigInteger as System.UInt64 if possible. /// /// /// /// When BigInteger is too large to fit into System.UInt64 public static ulong ToUInt64(BigInteger value) { if (object.ReferenceEquals(value, null)) { throw new ArgumentNullException("value"); } return System.UInt64.Parse(value.ToString(), System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.CurrentCulture); } public static byte[] ToByteArray(BigInteger value) { if (object.ReferenceEquals(value, null)) { throw new ArgumentNullException("value"); } var arr = new uint[value.m_digits.DataUsed]; value.m_digits.CopyTo(arr, 0, value.m_digits.DataUsed); var len = Buffer.ByteLength(arr); var buf = new byte[len]; Buffer.BlockCopy(arr, 0, buf, 0, len); Array.Reverse(buf); return buf; } #endregion } } ================================================ FILE: src/ServiceWire/ZeroKnowledge/ZkCrypto.cs ================================================ using System; using System.Security.Cryptography; namespace ServiceWire.ZeroKnowledge { /// /// Easy to use encapsulation of Rijndael encryption. /// public class ZkCrypto { private readonly byte[] _key; private readonly byte[] _iv; private readonly MD5CryptoServiceProvider _md5; public ZkCrypto(byte[] key, byte[] iv) { if (key.Length != 32) throw new ArgumentException("key must be 256 bits", "key"); if (iv.Length != 32) throw new ArgumentException("iv must be 256 bits", "iv"); _md5 = new MD5CryptoServiceProvider(); _key = key; _iv = _md5.ComputeHash(iv); } public byte[] Encrypt(byte[] data) { using (var crypto = RijndaelManaged.Create()) { crypto.Mode = CipherMode.CBC; crypto.BlockSize = 128; crypto.KeySize = 256; crypto.Padding = PaddingMode.ISO10126; using (var encryptor = crypto.CreateEncryptor(_key, _iv)) { return encryptor.TransformFinalBlock(data, 0, data.Length); } } } public byte[] Decrypt(byte[] encrypted) { using (var crypto = RijndaelManaged.Create()) { crypto.Mode = CipherMode.CBC; crypto.BlockSize = 128; crypto.KeySize = 256; crypto.Padding = PaddingMode.ISO10126; using (var dencryptor = crypto.CreateDecryptor(_key, _iv)) { return dencryptor.TransformFinalBlock(encrypted, 0, encrypted.Length); } } } } } ================================================ FILE: src/ServiceWire/ZeroKnowledge/ZkExt.cs ================================================ using System.Text; namespace ServiceWire.ZeroKnowledge { public static class ZkExt { public static byte[] ConvertToBytes(this string val) { return Encoding.Unicode.GetBytes(val); } public static string ConverToString(this byte[] bytes) { return Encoding.Unicode.GetString(bytes); } public static bool IsEqualTo(this byte[] a1, byte[] a2) { if (a1.Length != a2.Length) return false; for (int i = 0; i < a1.Length; i++) { if (a1[i] != a2[i]) return false; } return true; } } } ================================================ FILE: src/ServiceWire/ZeroKnowledge/ZkPasswordHash.cs ================================================ namespace ServiceWire.ZeroKnowledge { public class ZkPasswordHash { public byte[] Salt { get; set; } public byte[] Key { get; set; } public byte[] Verifier { get; set; } } } ================================================ FILE: src/ServiceWire/ZeroKnowledge/ZkProtocol.cs ================================================ using System; using System.Linq; using System.Security.Cryptography; namespace ServiceWire.ZeroKnowledge { /// /// Zero knowledge protocol loosely based on secure remote password protocol v6. /// public class ZkProtocol { private readonly SHA256CryptoServiceProvider _sha; private readonly Random _random; private readonly BigInteger _n; public ZkProtocol() { _sha = new SHA256CryptoServiceProvider(); _random = new Random(DateTime.Now.Millisecond); _n = new BigInteger(ZkSafePrimes.N4); } /// /// Server must generate password hash and store only username and hash values. /// /// /// /// public ZkPasswordHash HashCredentials(string username, string password) { var salt = ComputeHash(CryptRand()); var key = ComputeHash(salt, username.ConvertToBytes(), password.ConvertToBytes()); var ver = CryptRand(); return new ZkPasswordHash { Salt = salt, Key = key, Verifier = ver }; } /// /// Step 1. Client sends username and ephemeral hash of random number. /// /// Crypto randum generated by CryptRand() method. /// public byte[] GetClientEphemeralA(byte[] aRand) { var aval = ComputeHash(aRand); return aval; } /// /// Step 2. Server looks up username, gets pwd hash, and sends client salt and ephemeral hash of params. /// /// /// /// Crypto randum generated by CryptRand() method. /// public byte[] GetServerEphemeralB(byte[] salt, byte[] verifier, byte[] bRand) { var bval = ComputeHash(salt, verifier, bRand); return bval; } /// /// Step 3. Client and server calculate random scramble of ephemeral hash values exchanged. /// /// /// /// public byte[] CalculateRandomScramble(byte[] ephemeralA, byte[] ephemeralB) { return ComputeHash(ephemeralA, ephemeralB); } /// /// Step 4. Client computes session key /// /// /// /// /// /// /// /// public byte[] ClientComputeSessionKey(byte[] salt, string username, string password, byte[] aEphemeral, byte[] bEphemeral, byte[] uScramble) { var key = ComputeHash(salt, username.ConvertToBytes(), password.ConvertToBytes()); var kc = ComputeHash(salt, key, aEphemeral, bEphemeral, uScramble); return kc; } /// /// Step 5. Server computes session key /// /// /// /// /// /// /// public byte[] ServerComputeSessionKey(byte[] salt, byte[] key, byte[] aEphemeral, byte[] bEphemeral, byte[] uScramble) { var ks = ComputeHash(salt, key, aEphemeral, bEphemeral, uScramble); return ks; } /// /// Step 6. Client creates hash of session key and sends to server. Server creates same key and verifies. /// /// /// /// /// /// /// public byte[] ClientCreateSessionHash(string username, byte[] salt, byte[] aEphemeral, byte[] bEphermeral, byte[] sessionKey) { var mc = ComputeHash(username.ConvertToBytes(), salt, aEphemeral, bEphermeral, sessionKey); return mc; } /// /// Step 7. Server creates hash of session key and sends to client. Client creates same key and verifies. /// /// /// /// /// public byte[] ServerCreateSessionHash(byte[] aEphemeral, byte[] clientHash, byte[] sessionKey) { var ms = ComputeHash(aEphemeral, clientHash, sessionKey); return ms; } /// /// Generate crypto safe, pseudo random number. /// /// max value supported is 4096 /// public byte[] CryptRand(int bits = 4096) { var rb = new byte[256]; _random.NextBytes(rb); var bigrand = new BigInteger(rb); var crand = (bigrand % ZkSafePrimes.GetSafePrime(_random.Next(0, 2047))) ^ _n; var bytes = BigInteger.ToByteArray(crand); if (bits >= 4096) return bytes; var count = bits / 8; var skip = _random.Next(0, bytes.Length - count); return bytes.Skip(skip).Take(count).ToArray(); } public byte[] ComputeHash(params byte[][] items) { var buf = Combine(items); return _sha.ComputeHash(buf); } public byte[] Combine(params byte[][] arrays) { byte[] ret = new byte[arrays.Sum(x => x.Length)]; int offset = 0; foreach (byte[] data in arrays) { Buffer.BlockCopy(data, 0, ret, offset, data.Length); offset += data.Length; } return ret; } } } ================================================ FILE: src/ServiceWire/ZeroKnowledge/ZkSafePrimes.cs ================================================ namespace ServiceWire.ZeroKnowledge { public static class ZkSafePrimes { public static byte[] N4 { get { return Safe4096BitPrime; } } private static byte[] Safe4096BitPrime = new byte[] { 0xaf,0x0c,0x85,0x8d,0xf6,0xf0,0x8f,0xdf,0x9c,0x65,0xc4,0x6b,0x86,0x2b,0x8a,0x49,0xa8,0x96,0x43,0xbc,0x8b,0xeb,0x69,0xd4,0xfe,0xca,0xd6,0xde,0xa6,0xaf,0x8d,0xc3, 0x94,0x38,0x12,0xbc,0x93,0x78,0x9d,0xba,0x86,0x11,0x3a,0xd7,0x9a,0xbf,0x48,0xbf,0x46,0x09,0x89,0x47,0xe7,0x1e,0x41,0xa1,0x36,0x53,0x3f,0x60,0x43,0x6f,0x90,0xb8, 0x9d,0x53,0x5d,0xc3,0x54,0xeb,0xd9,0xcb,0x6c,0xf5,0x7f,0x55,0x06,0xd5,0xb1,0x8b,0xbc,0xaa,0x86,0x19,0x98,0xf4,0x05,0x5b,0x9e,0xc3,0x58,0x2f,0xa6,0xc2,0x16,0x1f, 0x75,0xd0,0x55,0x42,0xba,0x4b,0x2d,0x54,0x96,0xb4,0x11,0x38,0x54,0xc6,0xd1,0x4f,0xb8,0xbb,0x93,0x37,0x05,0x79,0xba,0xe0,0xe1,0xe6,0x07,0x7f,0xd6,0xef,0xe6,0x2e, 0xd7,0x44,0xf6,0x5a,0x19,0x12,0xf7,0x30,0x59,0x2e,0x62,0x1e,0xc7,0xd4,0x59,0x3c,0x1f,0xf4,0x16,0x94,0xac,0xf6,0x1b,0xe2,0x7b,0xa5,0xd2,0x5e,0xdf,0x5e,0xbf,0xe3, 0x33,0x34,0x61,0x7a,0x31,0xb0,0x89,0xaa,0x8a,0xc8,0xb8,0xf9,0x17,0xf0,0xd8,0x18,0x53,0x62,0x57,0x4c,0x7c,0xdf,0xaf,0x64,0x62,0x41,0x49,0xae,0xc5,0xc1,0xa3,0x97, 0x28,0x96,0x14,0xa9,0xef,0xdc,0x52,0x9f,0x40,0x84,0xb0,0x97,0x83,0x60,0xec,0xe1,0x0d,0xd6,0xeb,0x9a,0xd8,0xbe,0x04,0x6f,0xe7,0xc9,0x35,0x76,0x9a,0x06,0x8a,0xf8, 0xbf,0x72,0x56,0x9e,0x6e,0x5b,0x45,0x4b,0x58,0x75,0x4f,0x35,0x89,0x2b,0x91,0x8f,0xa7,0x30,0xfe,0xfa,0x1e,0xf4,0x06,0x7d,0x64,0xf3,0x65,0xdb,0x72,0x53,0xd4,0x9e, 0xd9,0xf0,0x91,0x93,0xc8,0x89,0xb4,0xa0,0x78,0xe6,0x2b,0xf5,0x6b,0x91,0x04,0xf9,0xac,0xac,0x2e,0x2a,0x38,0x03,0xdb,0xbd,0xf4,0xd7,0x69,0x6e,0x1a,0x26,0xf8,0xb4, 0x71,0x59,0xaf,0x14,0xf8,0x55,0x0c,0x70,0xd0,0xdc,0x9b,0x1c,0x36,0x35,0x32,0x86,0xa2,0x01,0xe0,0x42,0x47,0x4f,0x2d,0x46,0x67,0x37,0xbb,0x35,0xc3,0xb6,0xa7,0x54, 0x41,0x3e,0xaa,0xd1,0x90,0xdd,0x3d,0x09,0x08,0x17,0xbc,0x32,0xf6,0xe7,0xba,0x41,0x3f,0xf9,0xf9,0x4b,0xf6,0x5b,0xa8,0x76,0x54,0x5a,0x4a,0x1e,0x0c,0xe5,0x44,0x30, 0x84,0xf7,0x04,0x94,0x22,0x24,0xee,0x79,0xf0,0x67,0xd8,0x51,0x86,0x52,0xb6,0x1d,0x59,0x1a,0xa1,0xbe,0x7a,0x35,0xac,0xd3,0xd9,0x6b,0x31,0x44,0x3d,0x30,0x46,0xb6, 0x1c,0x73,0xd2,0xa4,0x13,0x05,0x84,0x8a,0xcc,0xe6,0x4d,0x83,0x04,0x54,0xf1,0xa9,0x47,0x60,0x4b,0x57,0x62,0x59,0x90,0x05,0x9b,0x23,0xa2,0xc3,0xf3,0x3c,0xca,0xcf, 0x0a,0x91,0x85,0xbf,0x9e,0x6b,0xbf,0x9c,0xcc,0x7e,0xd0,0x5e,0xcc,0xbe,0xe8,0xed,0xa9,0x37,0x5a,0x96,0x4f,0xd1,0xf0,0xc4,0xe0,0xfb,0x25,0xe6,0x4a,0xf1,0x90,0x89, 0xfd,0xb9,0xa3,0x5a,0xbc,0xb1,0x7e,0x8c,0x4b,0xfd,0xfd,0x60,0x29,0x48,0x31,0x94,0x28,0x51,0xcd,0x22,0x7a,0x0c,0x0f,0x40,0x5a,0x84,0xdb,0xb4,0x37,0x90,0x91,0x90, 0x18,0x1b,0x3e,0xf7,0x79,0x1b,0x47,0x37,0x02,0x0f,0xf1,0x2a,0x0d,0xef,0x48,0x0b,0x78,0x08,0x40,0xce,0xde,0x5f,0x62,0x4c,0xf8,0xf3,0xbe,0xf5,0xfd,0x44,0xf5,0x73 }; public static int GetSafePrime(int index) { if (index < 1) index = 1; else if (index > 2047) index = 2047; else if (index % 2 == 0) index++; return _safePrimes[index]; } //2048 values, even numbered values are safe primes, odds are their q in N = 2q+1 private static int[] _safePrimes = new[] { 5903,11807,8741,17483,11909,23819,6053,12107,8951,17903,11939,23879,6101,12203,8969,17939,12011, 24023,6113,12227,9029,18059,12041,24083,6131,12263,9059,18119,12101,24203,6173,12347,9221,18443, 12119,24239,6263,12527,9293,18587,12203,24407,6269,12539,9371,18743,12263,24527,6323,12647,9419, 18839,12329,24659,6329,12659,9473,18947,12653,25307,6449,12899,9479,18959,12671,25343,6491,12983, 9539,19079,12791,25583,6521,13043,9629,19259,12821,25643,6551,13103,9689,19379,12899,25799,6563, 13127,9791,19583,12923,25847,6581,13163,10061,20123,12959,25919,6761,13523,10091,20183,13001,26003, 6899,13799,10163,20327,13049,26099,6983,13967,10253,20507,13229,26459,7043,14087,10271,20543,13313, 26627,7079,14159,10313,20627,13451,26903,7103,14207,10331,20663,13463,26927,7121,14243,10529,21059, 13553,27107,7151,14303,10589,21179,13619,27239,7193,14387,10613,21227,13649,27299,7211,14423,10691, 21383,13763,27527,7349,14699,10709,21419,13883,27767,7433,14867,10733,21467,13901,27803,7541,15083, 10781,21563,13913,27827,7643,15287,10799,21599,14009,28019,7649,15299,10883,21767,14081,28163,7691, 15383,11171,22343,14153,28307,7823,15647,11321,22643,14159,28319,7841,15683,11369,22739,14249,28499, 7883,15767,11393,22787,14303,28607,7901,15803,11471,22943,14321,28643,14489,28979,18041,36083,21149, 42299,14561,29123,18131,36263,21179,42359,14621,29243,18149,36299,21221,42443,14669,29339,18191,36383, 21341,42683,14699,29399,18233,36467,21383,42767,14741,29483,18341,36683,21419,42839,14783,29567,18443, 36887,21611,43223,14831,29663,18461,36923,21701,43403,14879,29759,18731,37463,21713,43427,14939,29879, 18773,37547,21803,43607,15101,30203,18803,37607,21893,43787,15161,30323,18899,37799,22013,44027,15173, 30347,19163,38327,22079,44159,15233,30467,19301,38603,22133,44267,15269,30539,19319,38639,22259,44519, 15401,30803,19373,38747,22271,44543,15569,31139,19391,38783,22343,44687,15629,31259,19433,38867,22349, 44699,15773,31547,19553,39107,22409,44819,15791,31583,19559,39119,22433,44867,15803,31607,19661,39323, 22469,44939,15923,31847,19709,39419,22481,44963,16001,32003,19751,39503,22541,45083,16091,32183,19889, 39779,22751,45503,16253,32507,19913,39827,22853,45707,16301,32603,19919,39839,22943,45887,16421,32843, 19991,39983,23099,46199,16493,32987,20063,40127,23279,46559,16553,33107,20249,40499,23321,46643,16673, 33347,20369,40739,23339,46679,16811,33623,20393,40787,23459,46919,16823,33647,20411,40823,23561,47123, 16883,33767,20441,40883,23603,47207,16931,33863,20693,41387,23669,47339,17159,34319,20753,41507,23753, 47507,17183,34367,20759,41519,23819,47639,17291,34583,20771,41543,23909,47819,17333,34667,20789,41579, 23981,47963,17351,34703,20879,41759,24203,48407,17579,35159,20921,41843,24239,48479,17669,35339,20939, 41879,24281,48563,17681,35363,20963,41927,24473,48947,17939,35879,21011,42023,24509,49019,17981,35963, 21089,42179,24551,49103,24611,49223,28793,57587,32789,65579,24683,49367,28859,57719,32843,65687,24749, 49499,28901,57803,32933,65867,24971,49943,28949,57899,33023,66047,25073,50147,28961,57923,33053,66107, 25229,50459,29021,58043,33119,66239,25523,51047,29033,58067,33179,66359,25601,51203,29201,58403,33191, 66383,25643,51287,29339,58679,33461,66923,25673,51347,29363,58727,33479,66959,25703,51407,29453,58907, 33521,67043,25799,51599,29483,58967,33569,67139,25841,51683,29531,59063,33623,67247,25913,51827,29723, 59447,33713,67427,25919,51839,29873,59747,33749,67499,26111,52223,30269,60539,33773,67547,26189,52379, 30323,60647,33809,67619,26459,52919,30389,60779,33941,67883,26501,53003,30449,60899,34253,68507,26573, 53147,30671,61343,34283,68567,26633,53267,30689,61379,34319,68639,26849,53699,30773,61547,34439,68879, 26879,53759,30851,61703,34631,69263,26891,53783,30983,61967,34883,69767,26993,53987,31019,62039,34913, 69827,27143,54287,31151,62303,34949,69899,27281,54563,31253,62507,35069,70139,27479,54959,31319,62639, 35081,70163,27539,55079,31469,62939,35099,70199,27551,55103,31649,63299,35111,70223,27581,55163,31721, 63443,35291,70583,27743,55487,31793,63587,35573,71147,27773,55547,31799,63599,35831,71663,27809,55619, 31859,63719,35933,71867,27893,55787,32003,64007,35993,71987,27983,55967,32009,64019,35999,71999,28001, 56003,32141,64283,36083,72167,28019,56039,32159,64319,36191,72383,28403,56807,32381,64763,36251,72503, 28499,56999,32531,65063,36353,72707,28559,57119,32561,65123,36383,72767,28571,57143,32573,65147,36479, 72959,28643,57287,32633,65267,36563,73127,28751,57503,32771,65543,36629,73259,36761,73523,39983,79967, 44129,88259,36791,73583,39989,79979,44189,88379,36821,73643,40193,80387,44249,88499,36923,73847,40283, 80567,44273,88547,36929,73859,40343,80687,44501,89003,37013,74027,40559,81119,44543,89087,37049,74099, 40763,81527,44651,89303,37139,74279,40823,81647,44699,89399,37181,74363,40853,81707,44729,89459,37253, 74507,40949,81899,44879,89759,37379,74759,41081,82163,44909,89819,37619,75239,41231,82463,45053,90107, 37853,75707,41243,82487,45119,90239,37871,75743,41381,82763,45131,90263,37991,75983,41399,82799,45179, 90359,38039,76079,41603,83207,45263,90527,38183,76367,41609,83219,45329,90659,38189,76379,41621,83243, 45569,91139,38201,76403,41669,83339,45599,91199,38231,76463,41729,83459,45641,91283,38303,76607,41969, 83939,45971,91943,38333,76667,42023,84047,46181,92363,38453,76907,42071,84143,46199,92399,38459,76919, 42089,84179,46229,92459,38501,77003,42131,84263,46349,92699,38639,77279,42221,84443,46523,93047,38669, 77339,42359,84719,46589,93179,38723,77447,42473,84947,46619,93239,38861,77723,42611,85223,46643,93287, 38873,77747,42719,85439,46691,93383,38891,77783,42743,85487,46703,93407,38933,77867,42821,85643,46751, 93503,39089,78179,42923,85847,47189,94379,39233,78467,43013,86027,47279,94559,39239,78479,43313,86627, 47363,94727,39419,78839,43391,86783,47501,95003,39443,78887,43541,87083,47513,95027,39521,79043,43649, 87299,47543,95087,39551,79103,43661,87323,47609,95219,39569,79139,43691,87383,47639,95279,39659,79319, 43721,87443,47741,95483,39779,79559,43793,87587,48029,96059,39953,79907,43943,87887,48131,96263,39971, 79943,44111,88223,48221,96443,48239,96479,52553,105107,56519,113039,48413,96827,52571,105143,56531,113063, 48479,96959,52583,105167,56663,113327,48563,97127,52631,105263,56681,113363,48593,97187,52733,105467,56783, 113567,48731,97463,52883,105767,56891,113783,48761,97523,53051,106103,56909,113819,49103,98207,53093,106187, 56921,113843,49193,98387,53309,106619,56951,113903,49253,98507,53411,106823,57041,114083,49331,98663,53453, 106907,57149,114299,49433,98867,53549,107099,57203,114407,49463,98927,53591,107183,57329,114659,49481,98963, 53639,107279,57413,114827,49499,98999,53849,107699,57773,115547,49559,99119,53951,107903,57839,115679,49811, 99623,54011,108023,57881,115763,49853,99707,54101,108203,58013,116027,49919,99839,54251,108503,58049,116099, 50021,100043,54293,108587,58193,116387,50051,100103,54401,108803,58211,116423,50261,100523,54413,108827,58451, 116903,50273,100547,54443,108887,58511,117023,50411,100823,54773,109547,58601,117203,50423,100847,54941,109883, 58889,117779,50513,101027,54959,109919,58979,117959,50591,101183,55229,110459,59021,118043,50741,101483,55439, 110879,59063,118127,50873,101747,55469,110939,59123,118247,50969,101939,55631,111263,59369,118739,50993,101987, 55661,111323,59393,118787,51203,102407,55673,111347,59399,118799,51503,103007,55721,111443,59453,118907,51521, 103043,55733,111467,59513,119027,51539,103079,55799,111599,59621,119243,51659,103319,55829,111659,59723,119447, 51893,103787,55889,111779,59879,119759,52103,104207,55931,111863,59981,119963,52121,104243,56009,112019,60083, 120167,52163,104327,56081,112163,60149,120299,52289,104579,56099,112199,60251,120503,52361,104723,56123,112247, 60293,120587,52379,104759,56393,112787,60383,120767,52511,105023,56489,112979,60449,120899,60509,121019,65099, 130199,69203,138407,60689,121379,65111,130223,69341,138683,60719,121439,65129,130259,69431,138863,60761,121523, 65171,130343,69539,139079,60773,121547,65183,130367,69593,139187,60779,121559,65309,130619,69809,139619,61331, 122663,65393,130787,69941,139883,61409,122819,65633,131267,70061,140123,61469,122939,65651,131303,70079,140159, 61703,123407,65843,131687,70181,140363,61751,123503,65963,131927,70313,140627,61961,123923,66029,132059,70379, 140759,61991,123983,66173,132347,70589,141179,62099,124199,66191,132383,70769,141539,62171,124343,66431,132863, 70793,141587,62213,124427,66593,133187,70853,141707,62351,124703,66701,133403,70901,141803,62423,124847,66749, 133499,70979,141959,62459,124919,66791,133583,71399,142799,62501,125003,66959,133919,71453,142907,62591,125183, 67043,134087,71693,143387,62603,125207,67103,134207,71741,143483,62753,125507,67121,134243,71843,143687,62819, 125639,67169,134339,71849,143699,62981,125963,67181,134363,71999,143999,63113,126227,67349,134699,72101,144203, 63179,126359,67433,134867,72161,144323,63419,126839,67499,134999,72269,144539,63671,127343,67559,135119,72503, 145007,63743,127487,67733,135467,72911,145823,63803,127607,67943,135887,73259,146519,63839,127679,68111,136223, 73421,146843,63863,127727,68171,136343,73523,147047,63929,127859,68261,136523,73553,147107,64301,128603,68279, 136559,73589,147179,64373,128747,68489,136979,73613,147227,64439,128879,68543,137087,73673,147347,64451,128903, 68639,137279,73709,147419,64709,129419,68669,137339,73751,147503,64763,129527,68699,137399,73823,147647,64793, 129587,68819,137639,74099,148199,64853,129707,68963,137927,74201,148403,64901,129803,69029,138059,74219,148439, 65063,130127,69119,138239,74363,148727,74381,148763,78623,157247,82721,165443,74699,149399,78653,157307,82763, 165527,74729,149459,78713,157427,82793,165587,74759,149519,78779,157559,82889,165779,74771,149543,78839,157679, 83243,166487,74933,149867,79151,158303,83339,166679,75041,150083,79181,158363,83399,166799,75149,150299,79259, 158519,83423,166847,75161,150323,79283,158567,83459,166919,75329,150659,79349,158699,83579,167159,75353,150707, 79379,158759,83813,167627,75389,150779,79433,158867,83873,167747,75479,150959,79559,159119,83939,167879,75503, 151007,79589,159179,84011,168023,75689,151379,79613,159227,84131,168263,75821,151643,79769,159539,84263,168527, 75833,151667,79811,159623,84299,168599,75941,151883,79841,159683,84401,168803,75983,151967,79889,159779,84431, 168863,76001,152003,80039,160079,84443,168887,76031,152063,80309,160619,84449,168899,76091,152183,80369,160739, 84503,169007,76259,152519,80651,161303,84509,169019,76283,152567,80669,161339,84533,169067,76421,152843,80681, 161363,84629,169259,76679,153359,80819,161639,84653,169307,76781,153563,81071,162143,84659,169319,76871,153743, 81131,162263,84713,169427,76943,153887,81281,162563,84761,169523,77243,154487,81509,163019,85049,170099,77261, 154523,81563,163127,85061,170123,77471,154943,81611,163223,85103,170207,77513,155027,81629,163259,85121,170243, 77543,155087,81701,163403,85133,170267,77699,155399,81839,163679,85223,170447,77711,155423,81929,163859,85313, 170627,77813,155627,82073,164147,85523,171047,77849,155699,82139,164279,85601,171203,78059,156119,82193,164387, 85691,171383,78173,156347,82223,164447,85733,171467,78233,156467,82493,164987,85829,171659,78311,156623,82499, 164999,85853,171707,78341,156683,82529,165059,85931,171863,78509,157019,82601,165203,86111,172223,86171,172343, 91631,183263,96731,193463,86291,172583,91691,183383,96779,193559,86441,172883,91841,183683,96851,193703,86771, 173543,92003,184007,96989,193979,86843,173687,92243,184487,97001,194003,87071,174143,92363,184727,97241,194483, 87149,174299,92681,185363,97511,195023,87221,174443,92849,185699,97523,195047,87299,174599,92861,185723,97829, 195659,87383,174767,92951,185903,97871,195743,87539,175079,92993,185987,97931,195863,87701,175403,93053,186107, 97943,195887,87959,175919,93059,186119,98123,196247,88079,176159,93113,186227,98321,196643,88463,176927,93239, 186479,98369,196739,88661,177323,93323,186647,98453,196907,88811,177623,93371,186743,98459,196919,88919,177839, 93479,186959,98561,197123,89051,178103,93563,187127,98573,197147,89123,178247,93581,187163,98621,197243,89153, 178307,93683,187367,98639,197279,89399,178799,93893,187787,98669,197339,89759,179519,93911,187823,98711,197423, 89909,179819,93941,187883,98849,197699,90011,180023,94079,188159,98963,197927,90089,180179,94151,188303,98981, 197963,90173,180347,94229,188459,99023,198047,90281,180563,94343,188687,99041,198083,90599,181199,94421,188843, 99089,198179,90641,181283,94463,188927,99173,198347,90749,181499,95261,190523,99251,198503,90803,181607,95393, 190787,99551,199103,90833,181667,95549,191099,99623,199247,90971,181943,95561,191123,99689,199379,91079,182159, 95723,191447,99761,199523,91121,182243,95789,191579,99839,199679,91139,182279,95813,191627,91193,182387,95873, 191747,91373,182747,95891,191783,91433,182867,96269,192539,91463,182927,96293,192587,91499,182999,96443,192887, 91529,183059,96461,192923,91583,183167,96581,193163 }; } } ================================================ FILE: src/ServiceWire/ZeroKnowledge/ZkSession.cs ================================================ using System; using System.Diagnostics; using System.IO; namespace ServiceWire.ZeroKnowledge { public class ZkSession { private readonly IZkRepository _repository; private readonly ZkProtocol _zkProtocol = new ZkProtocol(); private ZkPasswordHash _zkPasswordHash = null; private ZkCrypto _zkCrypto = null; private readonly ILog _logger; private readonly IStats _stats; private string _username = null; private byte[] _aEphemeral = null; private byte[] _bEphemeral = null; private byte[] _bRand = null; private byte[] _scramble = null; private byte[] _serverSessionKey = null; private byte[] _clientSessionHash = null; private byte[] _serverSessionHash = null; public ZkSession(IZkRepository respository, ILog logger, IStats stats) { _repository = respository; _logger = logger ?? new NullLogger(); _stats = stats ?? new NullStats(); } public ZkCrypto Crypto { get { return _zkCrypto; } } public bool ProcessZkProof(BinaryReader binReader, BinaryWriter binWriter, Stopwatch sw) { _clientSessionHash = binReader.ReadBytes(32); _logger.Debug("ZkProof client session hash received: {0}", Convert.ToBase64String(_clientSessionHash)); var serverClientSessionHash = _zkProtocol.ClientCreateSessionHash(_username, _zkPasswordHash.Salt, _aEphemeral, _bEphemeral, _serverSessionKey); _serverSessionHash = _zkProtocol.ServerCreateSessionHash(_aEphemeral, _clientSessionHash, _serverSessionKey); if (!_clientSessionHash.IsEqualTo(serverClientSessionHash)) { _logger.Debug("ZkProof session hash does not match. Authentication failed. Server session hash: {0}", Convert.ToBase64String(_serverSessionHash)); binWriter.Write(false); return false; } _zkCrypto = new ZkCrypto(_serverSessionKey, _scramble); binWriter.Write(true); binWriter.Write(_serverSessionHash); _logger.Debug("ZkProof session hash sent to client: {0}", Convert.ToBase64String(_serverSessionHash)); return true; } public bool ProcessZkInitiation(BinaryReader binReader, BinaryWriter binWriter, Stopwatch sw) { _username = binReader.ReadString(); _aEphemeral = binReader.ReadBytes(32); _logger.Debug("ZkInitiation client username received: {0}", _username); _logger.Debug("ZkInitiation client Ephemeral received: {0}", Convert.ToBase64String(_aEphemeral)); _zkPasswordHash = _repository.GetPasswordHashSet(_username); if (null == _zkPasswordHash) { _logger.Debug("ZkInitiation client username not found. Authentication failed."); binWriter.Write(false); return false; } _bRand = _zkProtocol.CryptRand(); _bEphemeral = _zkProtocol.GetServerEphemeralB(_zkPasswordHash.Salt, _zkPasswordHash.Verifier, _bRand); _scramble = _zkProtocol.CalculateRandomScramble(_aEphemeral, _bEphemeral); _serverSessionKey = _zkProtocol.ServerComputeSessionKey(_zkPasswordHash.Salt, _zkPasswordHash.Key, _aEphemeral, _bEphemeral, _scramble); binWriter.Write(true); binWriter.Write(_zkPasswordHash.Salt); _logger.Debug("ZkInitiation hash salt sent to client: {0}", Convert.ToBase64String(_zkPasswordHash.Salt)); binWriter.Write(_bEphemeral); _logger.Debug("ZkInitiation server Ephemeral sent to client: {0}", Convert.ToBase64String(_bEphemeral)); return true; } } } ================================================ FILE: src/ServiceWire/_license.txt ================================================ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * COPYRIGHT NOTICES AND OPEN SOURCE LICENSES * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Title: ServiceWire Description: Very fast and light weight services host and dynamic client library that simplifies the development and use of high performance remote procedure call (RPC) communication between .NET processes over Named Pipes or TCP/IP. Copyright (C) 2013-2014 DuoVia Inc. Author: Tyler Jensen Source: https://github.com/duovia/ServiceWire Licensed 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. This notice, including the DERIVITIVE WORK NOTCE below, must be included in compiled and source form of this work whenever distributed. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * NAME CHANGE NOTICE The code in this work was originally published under the name DuoVia.Net. This work is a continuation of that project under the same copyright holder and author. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * DERIVATIVE WORK NOTICE Some of the source code in this work is derived from RemotingLite version 1.2.5 available at http://remotinglite.codeplex.com. The author of RemotingLite provides no endorsement, explicitly or implied, of this work. The original source is published under the following license terms: New BSD License (BSD) Copyright (c) 2008, Frank Thomsen All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Sector 0 nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. In addition, the following license information was part of the original source files but has been moved here for code brevity: RemotingLite ------ A light framework for making remote method invocations using TCP/IP. It is based loosely on Windows Communication Foundation, and is meant to provide programmers with the same API regardless of whether they write software for the Microsoft .NET platform or the Mono .NET platform. Consult the documentation and example applications for information about how to use this API. Author : Frank Thomsen http : http://sector0.dk Concact : http://sector0.dk/?q=contact Information : http://sector0.dk/?q=node/27 Licence : Free. If you use this, please let me know. Please feel free to contact me with ideas, bugs or improvements. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * DERIVATIVE WORK NOTICE This work includes a private branch of Scott Garland's BigInteger renamed as ZkBigInt. The MIT License (MIT) Copyright (c) 2007 Scott Garland Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: src/ServiceWire.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.2.32210.308 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{B4BD92DB-D725-4D20-8134-B610CA83F842}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Unit", "Unit", "{FC6B546C-85D6-4C37-ABB4-E02903AFB564}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Integration", "Integration", "{F2EA68D1-E9C5-40C2-8EFA-4D282ACD32D4}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solutionItems", ".solutionItems", "{C6A9C54E-EB6C-403A-AC34-A2E0F4674B61}" ProjectSection(SolutionItems) = preProject icon.png = icon.png License.txt = License.txt ..\README.md = ..\README.md EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Demo", "Demo", "{917620BA-F957-4891-82CC-B7AEDC2121CD}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceWire", "ServiceWire\ServiceWire.csproj", "{4F36B450-A225-4D13-9786-8B84AC25A304}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceWireTestHost", "Tests\Integration\ServiceWireTestHost\ServiceWireTestHost.csproj", "{3265DA8B-AD7E-4F2A-BB5B-2D81C5BA500A}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceWireTestClient2", "Tests\Integration\ServiceWireTestClient2\ServiceWireTestClient2.csproj", "{2AF18F87-5258-4AF4-8441-A67344DD5C13}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceWireTestClient1", "Tests\Integration\ServiceWireTestClient1\ServiceWireTestClient1.csproj", "{B9FAD2F3-17D4-435D-906B-45175457B294}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceWireTests", "Tests\Unit\ServiceWireTests\ServiceWireTests.csproj", "{0DF0ECE8-6ACA-442B-BBA6-6367F9B8D296}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceWireTestCommon", "Tests\ServiceWireTestCommon\ServiceWireTestCommon.csproj", "{4E07F3FB-F17C-4FF8-AA06-59ECE4956F6F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DemoHost", "Demo\DemoHost\DemoHost.csproj", "{67A4599D-104F-40DF-AD8E-0F1F9EA56F7B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DemoCommon", "Demo\DemoCommon\DemoCommon.csproj", "{D871AB84-61BC-474B-80DD-2B75E149CC90}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DemoClient", "Demo\DemoClient\DemoClient.csproj", "{B8D38CA4-25C1-455F-865C-93BB1F73C4D6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceWireTestHostPlusClient", "ServiceWireTestHostPlusClient\ServiceWireTestHostPlusClient.csproj", "{339E1750-55D3-4FA3-B428-C5BAA6639741}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Benchmarks", "Benchmarks", "{21449EC0-BD2D-4850-AD38-40AF0689C5CD}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceWire.Benchmarks", "Benchmarks\ServiceWire.Benchmarks\ServiceWire.Benchmarks.csproj", "{CA9235AA-03F2-4F0A-BB53-AE1607D08FCB}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Serializers", "Serializers", "{04D8746E-D49E-4704-9906-163ED424D677}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceWire.Serializers", "Serializers\ServiceWire.Serializers\ServiceWire.Serializers.csproj", "{FD65CE8A-64B0-424E-A311-867416C1741A}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{AA5FE88F-8DE5-43B1-8894-C32C3F64DA63}" ProjectSection(SolutionItems) = preProject ..\.github\workflows\build-and-test.yml = ..\.github\workflows\build-and-test.yml EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{E02F769F-E737-4193-970F-C6D21BA1ECB4}" ProjectSection(SolutionItems) = preProject ..\.github\workflows\build-and-test.yml = ..\.github\workflows\build-and-test.yml EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {4F36B450-A225-4D13-9786-8B84AC25A304}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4F36B450-A225-4D13-9786-8B84AC25A304}.Debug|Any CPU.Build.0 = Debug|Any CPU {4F36B450-A225-4D13-9786-8B84AC25A304}.Release|Any CPU.ActiveCfg = Release|Any CPU {4F36B450-A225-4D13-9786-8B84AC25A304}.Release|Any CPU.Build.0 = Release|Any CPU {3265DA8B-AD7E-4F2A-BB5B-2D81C5BA500A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3265DA8B-AD7E-4F2A-BB5B-2D81C5BA500A}.Debug|Any CPU.Build.0 = Debug|Any CPU {3265DA8B-AD7E-4F2A-BB5B-2D81C5BA500A}.Release|Any CPU.ActiveCfg = Release|Any CPU {3265DA8B-AD7E-4F2A-BB5B-2D81C5BA500A}.Release|Any CPU.Build.0 = Release|Any CPU {2AF18F87-5258-4AF4-8441-A67344DD5C13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2AF18F87-5258-4AF4-8441-A67344DD5C13}.Debug|Any CPU.Build.0 = Debug|Any CPU {2AF18F87-5258-4AF4-8441-A67344DD5C13}.Release|Any CPU.ActiveCfg = Release|Any CPU {2AF18F87-5258-4AF4-8441-A67344DD5C13}.Release|Any CPU.Build.0 = Release|Any CPU {B9FAD2F3-17D4-435D-906B-45175457B294}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B9FAD2F3-17D4-435D-906B-45175457B294}.Debug|Any CPU.Build.0 = Debug|Any CPU {B9FAD2F3-17D4-435D-906B-45175457B294}.Release|Any CPU.ActiveCfg = Release|Any CPU {B9FAD2F3-17D4-435D-906B-45175457B294}.Release|Any CPU.Build.0 = Release|Any CPU {0DF0ECE8-6ACA-442B-BBA6-6367F9B8D296}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0DF0ECE8-6ACA-442B-BBA6-6367F9B8D296}.Debug|Any CPU.Build.0 = Debug|Any CPU {0DF0ECE8-6ACA-442B-BBA6-6367F9B8D296}.Release|Any CPU.ActiveCfg = Release|Any CPU {0DF0ECE8-6ACA-442B-BBA6-6367F9B8D296}.Release|Any CPU.Build.0 = Release|Any CPU {4E07F3FB-F17C-4FF8-AA06-59ECE4956F6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4E07F3FB-F17C-4FF8-AA06-59ECE4956F6F}.Debug|Any CPU.Build.0 = Debug|Any CPU {4E07F3FB-F17C-4FF8-AA06-59ECE4956F6F}.Release|Any CPU.ActiveCfg = Release|Any CPU {4E07F3FB-F17C-4FF8-AA06-59ECE4956F6F}.Release|Any CPU.Build.0 = Release|Any CPU {67A4599D-104F-40DF-AD8E-0F1F9EA56F7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {67A4599D-104F-40DF-AD8E-0F1F9EA56F7B}.Debug|Any CPU.Build.0 = Debug|Any CPU {67A4599D-104F-40DF-AD8E-0F1F9EA56F7B}.Release|Any CPU.ActiveCfg = Release|Any CPU {67A4599D-104F-40DF-AD8E-0F1F9EA56F7B}.Release|Any CPU.Build.0 = Release|Any CPU {D871AB84-61BC-474B-80DD-2B75E149CC90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D871AB84-61BC-474B-80DD-2B75E149CC90}.Debug|Any CPU.Build.0 = Debug|Any CPU {D871AB84-61BC-474B-80DD-2B75E149CC90}.Release|Any CPU.ActiveCfg = Release|Any CPU {D871AB84-61BC-474B-80DD-2B75E149CC90}.Release|Any CPU.Build.0 = Release|Any CPU {B8D38CA4-25C1-455F-865C-93BB1F73C4D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B8D38CA4-25C1-455F-865C-93BB1F73C4D6}.Debug|Any CPU.Build.0 = Debug|Any CPU {B8D38CA4-25C1-455F-865C-93BB1F73C4D6}.Release|Any CPU.ActiveCfg = Release|Any CPU {B8D38CA4-25C1-455F-865C-93BB1F73C4D6}.Release|Any CPU.Build.0 = Release|Any CPU {339E1750-55D3-4FA3-B428-C5BAA6639741}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {339E1750-55D3-4FA3-B428-C5BAA6639741}.Debug|Any CPU.Build.0 = Debug|Any CPU {339E1750-55D3-4FA3-B428-C5BAA6639741}.Release|Any CPU.ActiveCfg = Release|Any CPU {339E1750-55D3-4FA3-B428-C5BAA6639741}.Release|Any CPU.Build.0 = Release|Any CPU {CA9235AA-03F2-4F0A-BB53-AE1607D08FCB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CA9235AA-03F2-4F0A-BB53-AE1607D08FCB}.Debug|Any CPU.Build.0 = Debug|Any CPU {CA9235AA-03F2-4F0A-BB53-AE1607D08FCB}.Release|Any CPU.ActiveCfg = Release|Any CPU {CA9235AA-03F2-4F0A-BB53-AE1607D08FCB}.Release|Any CPU.Build.0 = Release|Any CPU {FD65CE8A-64B0-424E-A311-867416C1741A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FD65CE8A-64B0-424E-A311-867416C1741A}.Debug|Any CPU.Build.0 = Debug|Any CPU {FD65CE8A-64B0-424E-A311-867416C1741A}.Release|Any CPU.ActiveCfg = Release|Any CPU {FD65CE8A-64B0-424E-A311-867416C1741A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {FC6B546C-85D6-4C37-ABB4-E02903AFB564} = {B4BD92DB-D725-4D20-8134-B610CA83F842} {F2EA68D1-E9C5-40C2-8EFA-4D282ACD32D4} = {B4BD92DB-D725-4D20-8134-B610CA83F842} {3265DA8B-AD7E-4F2A-BB5B-2D81C5BA500A} = {F2EA68D1-E9C5-40C2-8EFA-4D282ACD32D4} {2AF18F87-5258-4AF4-8441-A67344DD5C13} = {F2EA68D1-E9C5-40C2-8EFA-4D282ACD32D4} {B9FAD2F3-17D4-435D-906B-45175457B294} = {F2EA68D1-E9C5-40C2-8EFA-4D282ACD32D4} {0DF0ECE8-6ACA-442B-BBA6-6367F9B8D296} = {FC6B546C-85D6-4C37-ABB4-E02903AFB564} {4E07F3FB-F17C-4FF8-AA06-59ECE4956F6F} = {B4BD92DB-D725-4D20-8134-B610CA83F842} {67A4599D-104F-40DF-AD8E-0F1F9EA56F7B} = {917620BA-F957-4891-82CC-B7AEDC2121CD} {D871AB84-61BC-474B-80DD-2B75E149CC90} = {917620BA-F957-4891-82CC-B7AEDC2121CD} {B8D38CA4-25C1-455F-865C-93BB1F73C4D6} = {917620BA-F957-4891-82CC-B7AEDC2121CD} {339E1750-55D3-4FA3-B428-C5BAA6639741} = {F2EA68D1-E9C5-40C2-8EFA-4D282ACD32D4} {CA9235AA-03F2-4F0A-BB53-AE1607D08FCB} = {21449EC0-BD2D-4850-AD38-40AF0689C5CD} {FD65CE8A-64B0-424E-A311-867416C1741A} = {04D8746E-D49E-4704-9906-163ED424D677} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E466F06C-6792-41A4-9212-8FBC628E7F22} EndGlobalSection EndGlobal ================================================ FILE: src/ServiceWireTestHostPlusClient/Program.cs ================================================ // Small Test Program To Debug issues with throwing errors over a channel using System; using ServiceWire.NamedPipes; namespace ServiceWireTestHostPlustClient { public class Program { public static void Main() { // Create Host var nphost = new NpHost("TEST", null, null); nphost.AddService(new SimpleError()); nphost.Open(); // Create Client var npClient = new NpClient(new NpEndPoint("TEST")); // Test everything works if (npClient.Proxy.TestReturn3() != 3) throw new Exception("Interface not working"); // See that we get the INNER exception with the Fixed code try { npClient.Proxy.RaiseAnError(); } catch (Exception ex) { // Should be the exception we raised Console.WriteLine(ex.Message); } Console.ReadLine(); } } public interface ISimpleError { int TestReturn3(); void RaiseAnError(); } public class SimpleError : ISimpleError { public int TestReturn3() => 3; public void RaiseAnError() { throw new Exception("Deliberate Exception for Test"); } } } ================================================ FILE: src/ServiceWireTestHostPlusClient/ServiceWireTestHostPlusClient.csproj ================================================  net6.0;net8.0 net6.0;net8.0;net48 Exe true ================================================ FILE: src/Tests/Integration/ServiceWireTestClient1/App.config ================================================ ================================================ FILE: src/Tests/Integration/ServiceWireTestClient1/Program.cs ================================================ using System.Configuration; using System.Net.Sockets; using ServiceWire.NamedPipes; using ServiceWire.TcpIp; using ServiceWireTestCommon; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ServiceWireTestClient1 { class Program { private static void Main(string[] args) { Thread.Sleep(1000); var ip = ConfigurationManager.AppSettings["ip"]; var port = Convert.ToInt32(ConfigurationManager.AppSettings["port"]); var ipEndpoint = new IPEndPoint(IPAddress.Parse(ip), port); for (int i = 0; i < 1; i++) RunTest(ipEndpoint, ip); Console.ReadLine(); } private static void RunTest(IPEndPoint ipEndpoint, string ip) { using (var client = new TcpClient(ipEndpoint)) { decimal abc = client.Proxy.GetDecimal(4.5m); bool result = client.Proxy.OutDecimal(abc); } var tcpEndpoint = new TcpEndPoint(ipEndpoint, 80); //using (var client = new NetTcpTesterProxy(ipEndpoint)) using (var client = new NetTcpTesterProxy(tcpEndpoint)) { var id = client.GetId("test1", 3.314, 42, DateTime.Now); long q = 3; var response = client.Get(id, "mirror", 4.123, out q); var list = client.GetItems(id); var listFromAsync = client.GetItemsAsync(id).GetAwaiter().GetResult(); } using (var client = new NetTcpMyTesterProxy(ipEndpoint)) { var id = client.GetId("test1", 3.314, 42); int q2 = 4; var response = client.Get(id, "mirror", 4.123, out q2); var list = client.GetItems(id, new int[] { 3, 6, 9 }); } var sw = Stopwatch.StartNew(); var from = 0; var to = 200; Parallel.For(from, to, index => { using (var client = new NetTcpTesterProxy(ipEndpoint)) { for (int i = 0; i < 10; i++) { var id = client.GetId("test1", 3.314, 42, DateTime.Now); long q = 2; var response = client.Get(id, "mirror", 4.123, out q); var list = client.GetItems(id); var listFromAsync = client.GetItemsAsync(id).GetAwaiter().GetResult(); } } using (var client = new NetTcpMyTesterProxy(ipEndpoint)) { for (int i = 0; i < 10; i++) { var id = client.GetId("test1", 3.314, 42); int q2 = 6; var response = client.Get(id, "mirror", 4.123, out q2); var list = client.GetItems(id, new int[] { 3, 6, 9 }); } } }); sw.Stop(); var msperop = sw.ElapsedMilliseconds / 12000.0; Console.WriteLine("tcp: {0}, {1}", sw.ElapsedMilliseconds, msperop); if ("127.0.0.1" == ip) //only run np tests when testing locally { var pipeName = "ServiceWireTestHost"; using (var client = new NetNpTesterProxy(new NpEndPoint(pipeName))) { var id = client.GetId("test1", 3.314, 42, DateTime.Now); long q = 2; var response = client.Get(id, "mirror", 4.123, out q); var list = client.GetItems(id); var listFromAsync = client.GetItemsAsync(id).GetAwaiter().GetResult(); } sw = Stopwatch.StartNew(); Parallel.For(from, to, index => { using (var client = new NetNpTesterProxy(new NpEndPoint(pipeName))) { for (int i = 0; i < 10; i++) { var id = client.GetId("test1", 3.314, 42, DateTime.Now); long q = 4; var response = client.Get(id, "mirror", 4.123, out q); var list = client.GetItems(id); var listFromAsync = client.GetItemsAsync(id).GetAwaiter().GetResult(); long id1; long id2; long id3 = client.TestLong(out id1, out id2); eResult e1 = eResult.FAILED; eResult e2 = eResult.FAILED; eResult eRet = client.TestEnum(out e1, ref e2); } } using (var client = new NetNpMyTesterProxy(new NpEndPoint(pipeName))) { for (int i = 0; i < 10; i++) { var id = client.GetId("test1", 3.314, 42); int q2 = 4; var response = client.Get(id, "mirror", 4.123, out q2); var list = client.GetItems(id, new int[] { 3, 6, 9 }); } } }); sw.Stop(); msperop = sw.ElapsedMilliseconds / 12000.0; Console.WriteLine("pip: {0}, {1}", sw.ElapsedMilliseconds, msperop); Thread.Sleep(2000); } } } } ================================================ FILE: src/Tests/Integration/ServiceWireTestClient1/ServiceWireTestClient1.csproj ================================================  net6.0;net8.0 net6.0;net8.0;net48 Exe true ================================================ FILE: src/Tests/Integration/ServiceWireTestClient2/App.config ================================================ ================================================ FILE: src/Tests/Integration/ServiceWireTestClient2/Program.cs ================================================ using System.Configuration; using ServiceWire.NamedPipes; using ServiceWire.TcpIp; using ServiceWireTestCommon; using System; using System.Diagnostics; using System.Net; using System.Threading; using System.Threading.Tasks; using System.Collections.Generic; namespace ServiceWireTestClient2 { class Program { private static void Main(string[] args) { Thread.Sleep(1200); var ip = ConfigurationManager.AppSettings["ip"]; var port = Convert.ToInt32(ConfigurationManager.AppSettings["port"]); var ipEndpoint = new IPEndPoint(IPAddress.Parse(ip), port); var tasks = new List(); for (int i = 0; i < 1; i++) tasks.Add(RunTest(ipEndpoint, ip)); Task.WaitAll(tasks.ToArray()); Console.ReadLine(); } private static async Task RunTest(IPEndPoint ipEndpoint, string ip) { using (var client = new TcpClient(ipEndpoint)) { decimal abc = client.Proxy.GetDecimal(4.5m); bool result = client.Proxy.OutDecimal(abc); } using (var client = new TcpClient(ipEndpoint)) { decimal abc = await client.Proxy.GetDecimalAsync(4.5m); bool result = await client.Proxy.OutDecimalAsync(abc); } using (var client = new NetTcpTesterProxy(ipEndpoint)) { var id = client.GetId("test1", 3.314, 42, DateTime.Now); long q = 3; var response = client.Get(id, "mirror", 4.123, out q); var list = client.GetItems(id); var listFromAsync = await client.GetItemsAsync(id); } using (var client = new NetTcpMyTesterProxy(ipEndpoint)) { var id = client.GetId("test1", 3.314, 42); int q2 = 4; var response = client.Get(id, "mirror", 4.123, out q2); var list = client.GetItems(id, new int[] { 3, 6, 9 }); } var sw = Stopwatch.StartNew(); var from = 0; var to = 400; Parallel.For(from, to, index => { using (var client = new NetTcpTesterProxy(ipEndpoint)) { for (int i = 0; i < 10; i++) { var id = client.GetId("test1", 3.314, 42, DateTime.Now); long q = 2; var response = client.Get(id, "mirror", 4.123, out q); var list = client.GetItems(id); } } using (var client = new NetTcpMyTesterProxy(ipEndpoint)) { for (int i = 0; i < 10; i++) { var id = client.GetId("test1", 3.314, 42); int q2 = 6; var response = client.Get(id, "mirror", 4.123, out q2); var list = client.GetItems(id, new int[] { 3, 6, 9 }); } } }); sw.Stop(); var msperop = sw.ElapsedMilliseconds / 24000.0; Console.WriteLine("tcp: {0}, {1}", sw.ElapsedMilliseconds, msperop); if ("127.0.0.1" == ip) //only run np tests when testing locally { var pipeName = "ServiceWireTestHost"; using (var client = new NetNpTesterProxy(new NpEndPoint(pipeName))) { var id = client.GetId("test1", 3.314, 42, DateTime.Now); long q = 2; var response = client.Get(id, "mirror", 4.123, out q); var list = client.GetItems(id); } sw = Stopwatch.StartNew(); Parallel.For(from, to, index => { using (var client = new NetNpTesterProxy(new NpEndPoint(pipeName))) { for (int i = 0; i < 10; i++) { var id = client.GetId("test1", 3.314, 42, DateTime.Now); long q = 4; var response = client.Get(id, "mirror", 4.123, out q); var list = client.GetItems(id); long id1; long id2; long id3 = client.TestLong(out id1, out id2); eResult e1 = eResult.FAILED; eResult e2 = eResult.FAILED; eResult eRet = client.TestEnum(out e1, ref e2); } } using (var client = new NetNpMyTesterProxy(new NpEndPoint(pipeName))) { for (int i = 0; i < 10; i++) { var id = client.GetId("test1", 3.314, 42); int q2 = 4; var response = client.Get(id, "mirror", 4.123, out q2); var list = client.GetItems(id, new int[] { 3, 6, 9 }); } } }); sw.Stop(); msperop = sw.ElapsedMilliseconds / 24000.0; Console.WriteLine("pip: {0}, {1}", sw.ElapsedMilliseconds, msperop); Thread.Sleep(2000); } } } } ================================================ FILE: src/Tests/Integration/ServiceWireTestClient2/ServiceWireTestClient2.csproj ================================================  net6.0;net8.0 net6.0;net8.0;net48 Exe true ================================================ FILE: src/Tests/Integration/ServiceWireTestHost/App.config ================================================ ================================================ FILE: src/Tests/Integration/ServiceWireTestHost/Program.cs ================================================ using System; using System.Collections.Generic; using System.Configuration; using System.Net; using System.Text; using System.Threading.Tasks; using ServiceWire; using ServiceWire.NamedPipes; using ServiceWire.TcpIp; using ServiceWireTestCommon; namespace ServiceWireTestHost { class Program { static void Main(string[] args) { var logger = new Logger(logLevel: LogLevel.Debug); var stats = new Stats(); var ip = ConfigurationManager.AppSettings["ip"]; var port = Convert.ToInt32(ConfigurationManager.AppSettings["port"]); var ipEndpoint = new IPEndPoint(IPAddress.Any, port); var useCompression = false; var compressionThreshold = 131072; //128KB var pipeName = "ServiceWireTestHost"; var nphost = new NpHost(pipeName, logger, stats); var tester = new NetTester(); nphost.AddService(tester); var mytester = new MyTester(); nphost.UseCompression = useCompression; nphost.CompressionThreshold = compressionThreshold; nphost.AddService(mytester); nphost.Open(); var tcphost = new TcpHost(ipEndpoint, logger, stats); tcphost.UseCompression = useCompression; tcphost.CompressionThreshold = compressionThreshold; tcphost.AddService(tester); tcphost.AddService(mytester); var valTypes = new ValTypes(); tcphost.AddService(valTypes); tcphost.Open(); Console.WriteLine("Press Enter to stop the dual host test."); Console.ReadLine(); nphost.Close(); tcphost.Close(); Console.WriteLine("Press Enter to quit."); Console.ReadLine(); } } public class ValTypes : IValTypes { public decimal GetDecimal(decimal input) { return input += 456.44m; } public Task GetDecimalAsync(decimal input) { return Task.FromResult(GetDecimal(input)); } public bool OutDecimal(decimal val) { val = 45.66m; return true; } public Task OutDecimalAsync(decimal val) { return Task.FromResult(OutDecimal(val)); } } public class NetTester : INetTester { public Guid GetId(string source, double weight, int quantity, DateTime dt) { return Guid.NewGuid(); } public TestResponse Get(Guid id, string label, double weight, out long quantity) { quantity = 42; return new TestResponse { Id = id, Label = "Hello, world.", Quantity = quantity }; } public List GetItems(Guid id) { var list = new List(); list.Add("42"); list.Add(id.ToString()); list.Add("Test"); return list; } public Task> GetItemsAsync(Guid id) { return Task.FromResult(GetItems(id)); } public long TestLong(out long id1, out long id2) { id1 = 23; id2 = 24; return 25; } public eResult TestEnum(out eResult e1, ref eResult e2) { e1 = eResult.OK; e2 = eResult.OK; return eResult.OK; } } public class MyTester : IMyTester { private string longLabel = string.Empty; private const int totalKilobytes = 140; private Random rand = new Random(DateTime.Now.Millisecond); public MyTester() { var sb = new StringBuilder(); for (int i = 0; i < totalKilobytes; i++) { for (int k = 0; k < 1024; k++) sb.Append(((char)rand.Next(32, 126))); } longLabel = sb.ToString(); } public Guid GetId(string source, double weight, int quantity) { return Guid.NewGuid(); } public TestResponse Get(Guid id, string label, double weight, out int quantity) { quantity = 44; return new TestResponse { Id = id, Label = longLabel, Quantity = quantity }; } public List GetItems(Guid id, int[] vals) { var list = new List(); list.Add("42"); list.Add(id.ToString()); list.Add("MyTest"); list.Add(longLabel); return list; } } } ================================================ FILE: src/Tests/Integration/ServiceWireTestHost/ServiceWireTestHost.csproj ================================================  net6.0;net8.0 net6.0;net8.0;net48 Exe true ================================================ FILE: src/Tests/ServiceWireTestCommon/ServiceWireTestCommon.csproj ================================================  net6.0;net8.0 net6.0;net8.0;net48 true ================================================ FILE: src/Tests/ServiceWireTestCommon/TestContracts.cs ================================================ using ServiceWire.NamedPipes; using ServiceWire.TcpIp; using System; using System.Collections.Generic; using System.Net; using System.Threading.Tasks; namespace ServiceWireTestCommon { public enum eResult : UInt16 { FAILED = 0, OK = 1 } public interface IValTypes { decimal GetDecimal(decimal input); Task GetDecimalAsync(decimal input); bool OutDecimal(decimal val); Task OutDecimalAsync(decimal val); } public interface INetTester { Guid GetId(string source, double weight, int quantity, DateTime dt); TestResponse Get(Guid id, string label, double weight, out long quantity); long TestLong(out long id1, out long id2); eResult TestEnum(out eResult e1, ref eResult e2); List GetItems(Guid id); Task> GetItemsAsync(Guid id); } public interface IMyTester { Guid GetId(string source, double weight, int quantity); TestResponse Get(Guid id, string label, double weight, out int quantity); List GetItems(Guid id, int[] vals); } [Serializable] public struct TestResponse { public Guid Id { get; set; } public string Label { get; set; } public long Quantity { get; set; } public IList Values { get; set; } } public class NetTcpTesterProxy : TcpClient, INetTester { public NetTcpTesterProxy(TcpEndPoint endpoint) : base(endpoint) { } public NetTcpTesterProxy(IPEndPoint endpoint) : base(endpoint) { } public Guid GetId(string source, double weight, int quantity, DateTime dt) { return Proxy.GetId(source, weight, quantity, dt); } public TestResponse Get(Guid id, string label, double weight, out long quantity) { return Proxy.Get(id, label, weight, out quantity); } public List GetItems(Guid id) { return Proxy.GetItems(id); } public Task> GetItemsAsync(Guid id) { return Task.FromResult(Proxy.GetItems(id)); } public long TestLong(out long id1, out long id2) { id1 = 23; id2 = 24; return 25; } public eResult TestEnum(out eResult e1, ref eResult e2) { e1 = eResult.OK; e2 = eResult.OK; return eResult.OK; } } public class NetNpTesterProxy : NpClient, INetTester { public NetNpTesterProxy(NpEndPoint npAddress) : base(npAddress) { } public Guid GetId(string source, double weight, int quantity, DateTime dt) { return Proxy.GetId(source, weight, quantity, dt); } public TestResponse Get(Guid id, string label, double weight, out long quantity) { return Proxy.Get(id, label, weight, out quantity); } public List GetItems(Guid id) { return Proxy.GetItems(id); } public Task> GetItemsAsync(Guid id) { return Task.FromResult(Proxy.GetItems(id)); } public long TestLong(out long id1, out long id2) { return Proxy.TestLong(out id1, out id2); } public eResult TestEnum(out eResult e1, ref eResult e2) { return Proxy.TestEnum(out e1, ref e2); } } public class NetTcpMyTesterProxy : TcpClient, IMyTester { public NetTcpMyTesterProxy(IPEndPoint endpoint) : base(endpoint) { } public Guid GetId(string source, double weight, int quantity) { return Proxy.GetId(source, weight, quantity); } public TestResponse Get(Guid id, string label, double weight, out int quantity) { return Proxy.Get(id, label, weight, out quantity); } public List GetItems(Guid id, int[] vals) { return Proxy.GetItems(id, vals); } } public class NetNpMyTesterProxy : NpClient, IMyTester { public NetNpMyTesterProxy(NpEndPoint npAddress) : base(npAddress) { } public Guid GetId(string source, double weight, int quantity) { return Proxy.GetId(source, weight, quantity); } public TestResponse Get(Guid id, string label, double weight, out int quantity) { return Proxy.Get(id, label, weight, out quantity); } public List GetItems(Guid id, int[] vals) { return Proxy.GetItems(id, vals); } } } ================================================ FILE: src/Tests/Unit/ServiceWireTests/AsyncTests.cs ================================================ using System; using System.IO; using System.Linq; using System.Text.Json.Serialization; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using ServiceWire; using ServiceWire.NamedPipes; using Xunit; namespace ServiceWireTests { public class AsyncTests : IDisposable { private NpHost _nphost; [Fact] public async Task Divide_ShouldThrow_Exception() { await Task.Factory.StartNew(StartPipeListener, TaskCreationOptions.LongRunning); await Task.Delay(2000); var npClient = new NpClient(new NpEndPoint("8642D0EA-13B4-408F-8DBB-BB8CDEAAA0EC"), new CustomPipeSerializer()); var exception = await Assert.ThrowsAsync(async () => { await npClient.Proxy.Divide(4.0, 0); }); Assert.IsType(exception); } void StartPipeListener() { _nphost = new NpHost("8642D0EA-13B4-408F-8DBB-BB8CDEAAA0EC", null, null, new CustomPipeSerializer()); _nphost.UseCompression = true; _nphost.AddService(new Sum()); _nphost.Open(); } public void Dispose() { _nphost.Close(); } } public interface ISum { Task Divide(double a, int b); } public class Sum : ISum { public Task Divide(double a, int b) { if (b == 0) throw new PipeException("Trying to divide by zero."); return Task.FromResult(a / b); } } internal class PipeException : ApplicationException { public PipeException(string message = null) : base(message) { } } internal class CustomPipeSerializer : ISerializer { private readonly JsonSerializerOptions _customOptions; public CustomPipeSerializer() { _customOptions = new JsonSerializerOptions() { Converters = { new PipeExceptionJsonConverter(), new JsonStringEnumConverter() }, }; } public byte[] Serialize(T obj) => obj == null ? null : JsonSerializer.SerializeToUtf8Bytes(obj); public byte[] Serialize(object obj, string typeConfigName) => obj == null ? null : JsonSerializer.SerializeToUtf8Bytes(obj, typeConfigName.ToType(), _customOptions); public T Deserialize(byte[] bytes) => bytes == null || bytes.Length == 0 ? default(T) : JsonSerializer.Deserialize((ReadOnlySpan)bytes, _customOptions); public object Deserialize(byte[] bytes, string typeConfigName) { Type t = typeConfigName != null ? typeConfigName.ToType() : throw new ArgumentNullException(nameof(typeConfigName)); return bytes == null || bytes.Length == 0 ? t.GetDefault() : JsonSerializer.Deserialize((ReadOnlySpan)bytes, t, _customOptions); } } internal class PipeExceptionJsonConverter : JsonConverter { public override PipeException Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType != JsonTokenType.StartObject) { throw new JsonException(); } string message = "Message Not Available."; while (reader.Read()) { if (reader.TokenType == JsonTokenType.EndObject) { break; } if (reader.TokenType == JsonTokenType.PropertyName) { var propertyName = reader.GetString(); reader.Read(); switch (propertyName) { case "Message": var messageRead = reader.GetString(); if (messageRead != null) message = messageRead; break; } } } return new PipeException(message); } public override void Write(Utf8JsonWriter writer, PipeException value, JsonSerializerOptions options) { writer.WriteStartObject(); writer.WriteString("Message", value.Message); writer.WriteEndObject(); } } } ================================================ FILE: src/Tests/Unit/ServiceWireTests/INetTester.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace ServiceWireTests { public interface INetTester { int Min(int a, int b); Dictionary Range(int start, int count); TestResponse Get(Guid id, string label, double weight, out int quantity); Task CalculateAsync(int a, int b); string[] GetStrings(); } [Serializable] public struct TestResponse { public Guid Id { get; set; } public string Label { get; set; } public long Quantity { get; set; } public IList Values { get; set; } } public class NetTester : INetTester { public int Min(int a, int b) { return Math.Min(a, b); } public Dictionary Range(int start, int count) { return Enumerable.Range(start, count).ToDictionary(key => key, el => el); } public Task CalculateAsync(int a, int b) { return Task.FromResult(a + b); } public TestResponse Get(Guid id, string label, double weight, out int quantity) { quantity = 44; return new TestResponse { Id = id, Label = "MyLabel", Quantity = quantity, Values = new List { "one", "two", "three", "four" } }; } public string[] GetStrings() { return new string[] { "one", "two", null, "four" }; } } } ================================================ FILE: src/Tests/Unit/ServiceWireTests/InterceptionTests.cs ================================================ using ServiceWire.Aspects; using System; using System.Diagnostics; using Xunit; namespace ServiceWireTests { public class InterceptionTests { [Fact] public void SimpleTest() { var preInvokeInfo = string.Empty; var postInvokeInfo = string.Empty; var exceptionHandlerInfo = string.Empty; var cc = new CrossCuttingConcerns(); cc.PreInvoke = (instanceId, methodName, parameters) => { preInvokeInfo = methodName + "_preInvokeInfo"; }; cc.PostInvoke = (instanceId, methodName, parameters) => { postInvokeInfo = methodName + "_postInvokeInfo"; }; cc.ExceptionHandler = (instanceId, methodName, parameters, exception) => { exceptionHandlerInfo = methodName + "_exceptionHandlerInfo"; return false; //do not throw }; var t = Interceptor.Intercept(new SimpleMath(), cc); var a = t.Add(1, 2); Assert.False(string.IsNullOrEmpty(preInvokeInfo)); Assert.False(string.IsNullOrEmpty(postInvokeInfo)); Assert.True(string.IsNullOrEmpty(exceptionHandlerInfo)); var b = t.Divide(4, 0); Assert.False(string.IsNullOrEmpty(preInvokeInfo)); Assert.False(string.IsNullOrEmpty(postInvokeInfo)); Assert.False(string.IsNullOrEmpty(exceptionHandlerInfo)); } [Fact] public void SimpleTimingTest() { var preInvokeInfo = string.Empty; var postInvokeInfo = string.Empty; var exceptionHandlerInfo = string.Empty; var cc = new CrossCuttingConcerns(); cc.PreInvoke = (instanceId, methodName, parameters) => { preInvokeInfo = methodName + "_preInvokeInfo"; }; cc.PostInvoke = (instanceId, methodName, parameters) => { postInvokeInfo = methodName + "_postInvokeInfo"; }; cc.ExceptionHandler = (instanceId, methodName, parameters, exception) => { exceptionHandlerInfo = methodName + "_exceptionHandlerInfo"; return false; //do not throw }; var t = Interceptor.Intercept(new SimpleMath(), cc); var a = t.Add(1, 2); Assert.False(string.IsNullOrEmpty(preInvokeInfo)); Assert.False(string.IsNullOrEmpty(postInvokeInfo)); Assert.True(string.IsNullOrEmpty(exceptionHandlerInfo)); var sw = Stopwatch.StartNew(); var t2 = Interceptor.Intercept(new SimpleMath2(), cc); sw.Stop(); var interceptCtorTicks = sw.ElapsedTicks; var interceptCtorTs = sw.Elapsed; sw.Reset(); var c = t2.Add(2, 3); sw.Stop(); var interceptAddTicks = sw.ElapsedTicks; var interceptAddTs = sw.Elapsed; sw.Reset(); var t3 = Interceptor.Intercept(new SimpleMath2(), cc); sw.Stop(); var interceptCtorTicks2 = sw.ElapsedTicks; var interceptCtorTs2 = sw.Elapsed; sw.Reset(); var c2 = t3.Add(2, 3); sw.Stop(); var interceptAddTicks2 = sw.ElapsedTicks; var interceptAddTs2 = sw.Elapsed; sw.Reset(); var t4 = new SimpleMath2(); sw.Stop(); var plainCtorTicks = sw.ElapsedTicks; var plainCtorTs = sw.Elapsed; sw.Reset(); var c3 = t4.Add(2, 3); sw.Stop(); var plainAddTicks = sw.ElapsedTicks; var plainAddTs = sw.Elapsed; Assert.True(plainCtorTicks <= interceptCtorTicks); Assert.True(plainAddTicks <= interceptAddTicks); Assert.True(plainCtorTicks <= interceptCtorTicks2); Assert.True(plainAddTicks <= interceptAddTicks2); Assert.True(plainCtorTs <= interceptCtorTs); Assert.True(plainAddTs <= interceptAddTs); Assert.True(plainCtorTs <= interceptCtorTs2); Assert.True(plainAddTs <= interceptAddTs2); } [Fact] public void SimpleErrorTestNoThrow() { var preInvokeInfo = string.Empty; var postInvokeInfo = string.Empty; var exceptionHandlerInfo = string.Empty; var cc = new CrossCuttingConcerns(); cc.PreInvoke = (instanceId, methodName, parameters) => { preInvokeInfo = methodName + "_preInvokeInfo"; }; cc.PostInvoke = (instanceId, methodName, parameters) => { postInvokeInfo = methodName + "_postInvokeInfo"; }; cc.ExceptionHandler = (instanceId, methodName, parameters, exception) => { exceptionHandlerInfo = methodName + "_exceptionHandlerInfo"; return false; //do not throw }; var t = Interceptor.Intercept(new SimpleError(), cc); t.RaiseAnError(); Assert.False(string.IsNullOrEmpty(preInvokeInfo)); Assert.False(string.IsNullOrEmpty(postInvokeInfo)); Assert.False(string.IsNullOrEmpty(exceptionHandlerInfo)); } [Fact] public void SimpleErrorTestThrowOriginalError() { var preInvokeInfo = string.Empty; var postInvokeInfo = string.Empty; var exceptionHandlerInfo = string.Empty; var cc = new CrossCuttingConcerns(); cc.PreInvoke = (instanceId, methodName, parameters) => { preInvokeInfo = methodName + "_preInvokeInfo"; }; cc.PostInvoke = (instanceId, methodName, parameters) => { postInvokeInfo = methodName + "_postInvokeInfo"; }; cc.ExceptionHandler = (instanceId, methodName, parameters, exception) => { exceptionHandlerInfo = methodName + "_exceptionHandlerInfo"; return true; //do throw }; var t = Interceptor.Intercept(new SimpleError(), cc); try { t.RaiseAnError(); } catch (Exception ex) { var isCorrectError = ex.Message.Contains("eliberate"); Assert.True(isCorrectError); } Assert.False(string.IsNullOrEmpty(preInvokeInfo)); Assert.False(string.IsNullOrEmpty(postInvokeInfo)); Assert.False(string.IsNullOrEmpty(exceptionHandlerInfo)); } } public interface ISimpleMath { int Add(int a, int b); int Divide(int a, int b); } public class SimpleMath : ISimpleMath { public int Add(int a, int b) { return a + b; } public int Divide(int a, int b) { return a / b; } } public interface ISimpleMath2 { int Add(int a, int b); int Divide(int a, int b); } public class SimpleMath2 : ISimpleMath2 { public int Add(int a, int b) { return a + b; } public int Divide(int a, int b) { return a / b; } } public interface ISimpleError { void RaiseAnError(); } public class SimpleError : ISimpleError { public void RaiseAnError() { throw new Exception("Deliberate Exception for Test"); } } } ================================================ FILE: src/Tests/Unit/ServiceWireTests/NewtonsoftSerializer.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Reflection; using ServiceWire; using Newtonsoft.Json; namespace ServiceWireTests { public class NewtonsoftSerializer : ISerializer { private JsonSerializerSettings settings = new JsonSerializerSettings { ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore }; public T Deserialize(byte[] bytes) { if (null == bytes || bytes.Length == 0) return default(T); var json = Encoding.UTF8.GetString(bytes); return JsonConvert.DeserializeObject(json, settings); } public object Deserialize(byte[] bytes, string typeConfigName) { if (null == typeConfigName) throw new ArgumentNullException(nameof(typeConfigName)); var type = typeConfigName.ToType(); if (null == typeConfigName || null == bytes || bytes.Length == 0) return type.GetDefault(); var json = Encoding.UTF8.GetString(bytes); return JsonConvert.DeserializeObject(json, type, settings); } public byte[] Serialize(T obj) { if (null == obj) return null; var json = JsonConvert.SerializeObject(obj, settings); return Encoding.UTF8.GetBytes(json); } public byte[] Serialize(object obj, string typeConfigName) { if (null == obj) return null; var type = typeConfigName.ToType(); var json = JsonConvert.SerializeObject(obj, type, settings); return Encoding.UTF8.GetBytes(json); } } } ================================================ FILE: src/Tests/Unit/ServiceWireTests/NpTests.cs ================================================ using System; using System.Linq; using System.Threading; using System.Threading.Tasks; using ServiceWire.NamedPipes; using Xunit; namespace ServiceWireTests { public class NpTests : IDisposable { private INetTester _tester; private NpHost _nphost; private NpClient _clientProxy; private readonly string _pipeName = "ServiceWireTestHost"; private NpEndPoint CreateEndPoint() { return new NpEndPoint(_pipeName); } public NpTests() { var rnd = new Random(DateTime.UtcNow.Millisecond); var val = rnd.Next(999, 99999); _pipeName += val.ToString(); _tester = new NetTester(); _nphost = new NpHost(_pipeName); _nphost.AddService(_tester); _nphost.Open(); _clientProxy = new NpClient(CreateEndPoint()); } [Fact] public void SimpleTest() { var rnd = new Random(); var a = rnd.Next(0, 100); var b = rnd.Next(0, 100); var result = _clientProxy.Proxy.Min(a, b); Assert.Equal(Math.Min(a, b), result); } [Fact] public void SimpleNewtonsoftSerializerTest() { using (var nphost = new NpHost(_pipeName + "Json", serializer: new NewtonsoftSerializer())) { nphost.AddService(_tester); nphost.Open(); var rnd = new Random(); var a = rnd.Next(0, 100); var b = rnd.Next(0, 100); var result = _clientProxy.Proxy.Min(a, b); Assert.Equal(Math.Min(a, b), result); } } [Fact] public void SimpleProtobufSerializerTest() { using (var nphost = new NpHost(_pipeName + "Proto", serializer: new ProtobufSerializer())) { nphost.AddService(_tester); nphost.Open(); var rnd = new Random(); var a = rnd.Next(0, 100); var b = rnd.Next(0, 100); var result = _clientProxy.Proxy.Min(a, b); Assert.Equal(Math.Min(a, b), result); } } [Fact] public async Task CalculateAsyncTest() { var rnd = new Random(); var a = rnd.Next(0, 100); var b = rnd.Next(0, 100); var result = await _clientProxy.Proxy.CalculateAsync(a, b); Assert.Equal(a + b, result); } [Fact(Skip = "Avoid Parallel.For")] public void SimpleParallelTest() { var rnd = new Random(); Parallel.For(0, 4, (index, state) => { var a = rnd.Next(0, 100); var b = rnd.Next(0, 100); using (var clientProxy = new NpClient(CreateEndPoint())) { var result = clientProxy.Proxy.Min(a, b); if (Math.Min(a, b) != result) state.Break(); Assert.Equal(Math.Min(a, b), result); } Task.Delay(100); }); } [Fact] public void ResponseTest() { const int count = 50; const int start = 0; var result = _clientProxy.Proxy.Range(start, count); for (var i = start; i < count; i++) { int temp; Assert.True(result.TryGetValue(i, out temp)); Assert.Equal(i, temp); } } [Fact(Skip = "Avoid Parallel.For")] public void ResponseParallelTest() { Parallel.For(0, 4, (index, state) => { Thread.Sleep(100); const int count = 5; const int start = 0; using (var clientProxy = new NpClient(CreateEndPoint())) { var result = clientProxy.Proxy.Range(start, count); for (var i = start; i < count; i++) { int temp; if (result.TryGetValue(i, out temp)) { if (i != temp) state.Break(); Assert.Equal(i, temp); } else { state.Break(); Assert.True(false); } } } Task.Delay(100); }); } [Fact] public void ResponseWithOutParameterTest() { int quantity = 0; var result = _clientProxy.Proxy.Get(Guid.NewGuid(), "SomeLabel", 45.65, out quantity); Assert.Equal(44, quantity); Assert.NotEqual(default(TestResponse), result); Assert.Equal("MyLabel", result.Label); } [Fact] public void ResponseWithOutParameterNewtonsoftSerializerTest() { using (var nphost = new NpHost(_pipeName + "JsonResponseOut", serializer: new NewtonsoftSerializer())) { nphost.AddService(_tester); nphost.Open(); using (var clientProxy = new NpClient(new NpEndPoint(_pipeName + "JsonResponseOut"), new NewtonsoftSerializer())) { int quantity = 0; var result = clientProxy.Proxy.Get(Guid.NewGuid(), "SomeLabel", 45.65, out quantity); Assert.Equal(44, quantity); Assert.NotEqual(default(TestResponse), result); Assert.Equal("MyLabel", result.Label); } } } [Fact] public void GetStringsTest() { var result = _clientProxy.Proxy.GetStrings(); Assert.Equal(4, result.Length); Assert.Null(result[2]); } public void Dispose() { _clientProxy.Dispose(); _nphost.Close(); _nphost.Dispose(); } } } ================================================ FILE: src/Tests/Unit/ServiceWireTests/ParameterTransferHelperTests.cs ================================================ using ServiceWire; using System; using System.IO; using Xunit; namespace ServiceWireTests { public class ParameterTransferHelperTests { [Fact] public void SimpleTypesTest() { bool b = true; TestSingle(b); // bool), P byte y = 0x32; TestSingle(y); // byte), P sbyte sb = -33; TestSingle(sb); // sbyte), char c = 'c'; TestSingle(c); // char), P decimal d = 1.34m; TestSingle(d); // decimal) double db = 3.45; TestSingle(db); // double), float f = 32.3f; TestSingle(f); // float), int i = 34588; TestSingle(i); // int), Pa uint ui = 34444; TestSingle(ui); // uint), P long l = 3499887766; TestSingle(l); // long), P ulong ul = 9876758765654; TestSingle(ul); // ulong), short st = 234; TestSingle(st); // short), ushort ut = 888; TestSingle(ut); // ushort), string str = "hello"; TestSingle(str); // string), Guid g = Guid.NewGuid(); TestSingle(g); // Guid), P DateTime dt = DateTime.Now; TestSingle(dt); // DateTime } [Fact] public void SimpleTypesMultipleTest() { bool b = true; byte y = 0x32; sbyte sb = -33; char c = 'c'; decimal d = 1.34m; TestMultiple(b, y, sb, c, d); double db = 3.45; float f = 32.3f; int i = 34588; uint ui = 34444; long l = 3499887766; ulong ul = 9876758765654; short st = 234; ushort ut = 888; TestMultiple(db, f, i, ui, l, ul, st, ut); string str = "hello"; Guid g = Guid.NewGuid(); DateTime dt = DateTime.Now; TestMultiple(str, g, dt); } [Fact] public void ArrayOfSimpleTypesTest() { var bools = new bool[] { true, true, false, true, false }; TestArraySimpleType(bools); var ys = new byte[] { 0x32, 0x33, 0x42, 0x52 }; TestArraySimpleType(ys); var sbs = new sbyte[] { -33, 14, -12, -33, 14, -12 }; TestArraySimpleType(sbs); var cs = new char[] { 'c', 'd', 'e', 'r' }; TestArraySimpleType(cs); var ds = new decimal[] { 1.344m, 1.374m, 1.394m, 1.314m }; TestArraySimpleType(ds); var dbs = new double[] { 3.435, 3.425, 3.465, 3.845, 3.145 }; TestArraySimpleType(dbs); var fs = new float[] { 32.37f, 362.3f, 342.3f, 32.23f, 32.33f }; TestArraySimpleType(fs); var iss = new int[] { 345288, 541215, 542315, 542215, 564215, 544215 }; TestArraySimpleType(iss); var uis = new uint[] { 34578444, 45676456, 452456456, 452456456, 424556456 }; TestArraySimpleType(uis); var ls = new long[] { 34997766, 99887766, 34998866, 34987766, 34998877 }; TestArraySimpleType(ls); var uls = new ulong[] { 987675765654, 987758765654, 987758765654, 987675876654, 987675875654 }; TestArraySimpleType(uls); var sts = new short[] { 2343, 2343, 2334, 2345, 2434 }; TestArraySimpleType(sts); var uts = new ushort[] { 8828, 4244, 4244, 2444, 4442, 4244 }; TestArraySimpleType(uts); var strs = new string[] { "hello", "heallo", "heldlo", "hellao" }; TestArraySimpleType(strs); var gs = new Guid[] { Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid() }; TestArraySimpleType(gs); var dts = new DateTime[] { DateTime.Now, DateTime.Now.AddDays(1), DateTime.Now.AddDays(2) }; TestArraySimpleType(dts); } private void TestArraySimpleType(object obj) { var result = RunInAndOut(obj); var arr = obj as Array; var rarr = result[0] as Array; Assert.NotNull(result); Assert.Equal(arr.Length, rarr.Length); } private void TestMultiple(params object[] obj) { var result = RunInAndOut(obj); Assert.NotNull(result); Assert.Equal(obj.Length, result.Length); for (int i = 0; i < obj.Length; i++) { Assert.Equal(obj[i], result[i]); } } private void TestSingle(object obj) { var result = RunInAndOut(obj); Assert.NotNull(result); Assert.Single(result); Assert.Equal(obj, result[0]); } private object[] RunInAndOut(params object[] obj) { var pth = new ParameterTransferHelper(new DefaultSerializer(), new DefaultCompressor()); object[] result = null; using (var ms = new MemoryStream()) using (var writer = new BinaryWriter(ms)) using (var reader = new BinaryReader(ms)) { pth.SendParameters(false, 0, writer, obj); ms.Position = 0; result = pth.ReceiveParameters(reader); } return result; } } } /* _parameterTypes.Add(typeof(bool), ParameterTypes.Bool); _parameterTypes.Add(typeof(byte), ParameterTypes.Byte); _parameterTypes.Add(typeof(sbyte), ParameterTypes.SByte); _parameterTypes.Add(typeof(char), ParameterTypes.Char); _parameterTypes.Add(typeof(decimal), ParameterTypes.Decimal); _parameterTypes.Add(typeof(double), ParameterTypes.Double); _parameterTypes.Add(typeof(float), ParameterTypes.Float); _parameterTypes.Add(typeof(int), ParameterTypes.Int); _parameterTypes.Add(typeof(uint), ParameterTypes.UInt); _parameterTypes.Add(typeof(long), ParameterTypes.Long); _parameterTypes.Add(typeof(ulong), ParameterTypes.ULong); _parameterTypes.Add(typeof(short), ParameterTypes.Short); _parameterTypes.Add(typeof(ushort), ParameterTypes.UShort); _parameterTypes.Add(typeof(string), ParameterTypes.String); _parameterTypes.Add(typeof(byte[]), ParameterTypes.ByteArray); _parameterTypes.Add(typeof(char[]), ParameterTypes.CharArray); _parameterTypes.Add(typeof(Type), ParameterTypes.Type); _parameterTypes.Add(typeof(Guid), ParameterTypes.Guid); _parameterTypes.Add(typeof(DateTime), ParameterTypes.DateTime); _parameterTypes.Add(typeof(bool[]), ParameterTypes.ArrayBool); _parameterTypes.Add(typeof(sbyte[]), ParameterTypes.ArraySByte); _parameterTypes.Add(typeof(decimal[]), ParameterTypes.ArrayDecimal); _parameterTypes.Add(typeof(double[]), ParameterTypes.ArrayDouble); _parameterTypes.Add(typeof(float[]), ParameterTypes.ArrayFloat); _parameterTypes.Add(typeof(int[]), ParameterTypes.ArrayInt); _parameterTypes.Add(typeof(uint[]), ParameterTypes.ArrayUInt); _parameterTypes.Add(typeof(long[]), ParameterTypes.ArrayLong); _parameterTypes.Add(typeof(ulong[]), ParameterTypes.ArrayULong); _parameterTypes.Add(typeof(short[]), ParameterTypes.ArrayShort); _parameterTypes.Add(typeof(ushort[]), ParameterTypes.ArrayUShort); _parameterTypes.Add(typeof(string[]), ParameterTypes.ArrayString); _parameterTypes.Add(typeof(Type[]), ParameterTypes.ArrayType); _parameterTypes.Add(typeof(Guid[]), ParameterTypes.ArrayGuid); _parameterTypes.Add(typeof(DateTime[]), ParameterTypes.ArrayDateTime); */ ================================================ FILE: src/Tests/Unit/ServiceWireTests/ProtobufSerializer.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Reflection; using ServiceWire; using ProtoBuf; using ProtoBuf.Meta; namespace ServiceWireTests { public class ProtobufSerializer : ISerializer { public T Deserialize(byte[] bytes) { if (bytes.Length == 0) return default(T); using (var ms = new MemoryStream(bytes)) { return Serializer.Deserialize(ms); } } public object Deserialize(byte[] bytes, string typeConfigName) { if (null == typeConfigName) throw new ArgumentNullException(nameof(typeConfigName)); var type = typeConfigName.ToType(); if (null == typeConfigName || null == bytes || bytes.Length == 0) return type.GetDefault(); using (var ms = new MemoryStream(bytes)) { return Serializer.Deserialize(type, ms); } } public byte[] Serialize(T obj) { if (null == obj) return null; using (var ms = new MemoryStream()) { try { Serializer.Serialize(ms, obj); var bytes = ms.ToArray(); return bytes; } catch (Exception e) { Console.Write(e); } return null; } } public byte[] Serialize(object obj, string typeConfigName) { if (null == typeConfigName) throw new ArgumentNullException(nameof(typeConfigName)); if (null == obj) return null; using (var ms = new MemoryStream()) { try { Serializer.Serialize(ms, obj); var bytes = ms.ToArray(); return bytes; } catch (Exception e) { Console.Write(e); } return null; } } } } ================================================ FILE: src/Tests/Unit/ServiceWireTests/SequentialCollection.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Xunit; namespace ServiceWireTests { [CollectionDefinition("Sequential Collection Tcp", DisableParallelization = true)] public class SequentialCollectionTcp { } [CollectionDefinition("Sequential Collection TcpZk", DisableParallelization = true)] public class SequentialCollectionTcpZk { } } ================================================ FILE: src/Tests/Unit/ServiceWireTests/ServiceWireTests.csproj ================================================  net6.0;net8.0 net6.0;net8.0;net48 false true all runtime; build; native; contentfiles; analyzers ================================================ FILE: src/Tests/Unit/ServiceWireTests/TcpTests.cs ================================================ using System; using System.Net; using System.Threading.Tasks; using ServiceWire.TcpIp; using Xunit; namespace ServiceWireTests { [Collection("Sequential Collection Tcp")] public class TcpTests : IDisposable { private INetTester _tester; private TcpHost _tcphost; private IPAddress _ipAddress; private const int Port = 8099; private TcpClient _clientProxy; private IPEndPoint CreateEndPoint() { return new IPEndPoint(_ipAddress, Port); } public TcpTests() { _tester = new NetTester(); _ipAddress = IPAddress.Parse("127.0.0.1"); _tcphost = new TcpHost(CreateEndPoint()); _tcphost.AddService(_tester); _tcphost.Open(); Task.Delay(100); _clientProxy = new TcpClient(CreateEndPoint()); } [Fact] public void SimpleTest() { Task.Delay(100); var rnd = new Random(); var a = rnd.Next(0, 100); var b = rnd.Next(0, 100); var result = _clientProxy.Proxy.Min(a, b); Assert.Equal(Math.Min(a, b), result); Task.Delay(100); } [Fact] public async Task CalculateAsyncTest() { await Task.Delay(100); var rnd = new Random(); var a = rnd.Next(0, 100); var b = rnd.Next(0, 100); var result = await _clientProxy.Proxy.CalculateAsync(a, b); Assert.Equal(a + b, result); await Task.Delay(100); } [Fact(Skip = "Avoid Parallel.For")] public void SimpleParallelTest() { Task.Delay(100); var rnd = new Random(); Parallel.For(0, 4, (index, state) => { var a = rnd.Next(0, 100); var b = rnd.Next(0, 100); using (var clientProxy = new TcpClient(CreateEndPoint())) { var result = clientProxy.Proxy.Min(a, b); if (Math.Min(a, b) != result) { state.Break(); Assert.Equal(Math.Min(a, b), result); } } Task.Delay(100); }); Task.Delay(100); } [Fact] public void ResponseTest() { Task.Delay(100); const int count = 50; const int start = 0; var result = _clientProxy.Proxy.Range(start, count); for (var i = start; i < count; i++) { int temp; Assert.True(result.TryGetValue(i, out temp)); Assert.Equal(i, temp); } Task.Delay(100); } [Fact(Skip = "Avoid Parallel.For")] public void ResponseParallelTest() { Task.Delay(100); Parallel.For(0, 4, (index, state) => { const int count = 50; const int start = 0; using (var clientProxy = new TcpClient(CreateEndPoint())) { var result = clientProxy.Proxy.Range(start, count); for (var i = start; i < count; i++) { int temp; if (result.TryGetValue(i, out temp)) { if (i != temp) state.Break(); Assert.Equal(i, temp); } else { state.Break(); Assert.True(false); } } } Task.Delay(100); }); Task.Delay(100); } [Fact] public void ResponseWithOutParameterTest() { Task.Delay(100); int quantity = 0; var result = _clientProxy.Proxy.Get(Guid.NewGuid(), "SomeLabel", 45.65, out quantity); Assert.Equal(44, quantity); Assert.NotEqual(default(TestResponse), result); Assert.Equal("MyLabel", result.Label); Task.Delay(100); } [Fact] public void GetStringsTest() { Task.Delay(100); var result = _clientProxy.Proxy.GetStrings(); Assert.Equal(4, result.Length); Assert.Null(result[2]); Task.Delay(100); } public void Dispose() { _clientProxy.Dispose(); _tcphost.Close(); _tcphost.Dispose(); } } } ================================================ FILE: src/Tests/Unit/ServiceWireTests/TcpZkTests.cs ================================================ using System; using System.Net; using System.Threading.Tasks; using ServiceWire.TcpIp; using ServiceWire.ZeroKnowledge; using Xunit; namespace ServiceWireTests { public class FakeZkRepository : IZkRepository { private string password = "cc3a6a12-0e5b-47fb-ae45-3485e34582d4"; private ZkProtocol _protocol = new ZkProtocol(); private ZkPasswordHash _hash = null; public ZkPasswordHash GetPasswordHashSet(string username) { if (_hash == null) _hash = _protocol.HashCredentials(username, password); return _hash; } } [Collection("Sequential Collection TcpZk")] public class TcpZkTests : IDisposable { private INetTester _tester; private FakeZkRepository _repo = new FakeZkRepository(); private string username = "myuser@userdomain.com"; private string password = "cc3a6a12-0e5b-47fb-ae45-3485e34582d4"; private TcpHost _tcphost; private IPAddress _ipAddress; private const int Port = 8098; private TcpClient _clientProxy; private IPEndPoint CreateEndPoint() { return new IPEndPoint(_ipAddress, Port); } private TcpZkEndPoint CreateZkClientEndPoint() { return new TcpZkEndPoint(username, password, new IPEndPoint(_ipAddress, Port), connectTimeOutMs: 5000); //expand timeout for CI/CD pipeline } public TcpZkTests() { _tester = new NetTester(); _ipAddress = IPAddress.Parse("127.0.0.1"); _tcphost = new TcpHost(CreateEndPoint(), zkRepository: _repo); _tcphost.AddService(_tester); _tcphost.Open(); Task.Delay(100); _clientProxy = new TcpClient(CreateZkClientEndPoint()); } [Fact] public void SimpleZkTest() { Task.Delay(100); var rnd = new Random(); var a = rnd.Next(0, 100); var b = rnd.Next(0, 100); var result = _clientProxy.Proxy.Min(a, b); Assert.Equal(Math.Min(a, b), result); Task.Delay(100); } [Fact] public async Task CalculateAsyncTest() { Task.Delay(100); var rnd = new Random(); var a = rnd.Next(0, 100); var b = rnd.Next(0, 100); var result = await _clientProxy.Proxy.CalculateAsync(a, b); Assert.Equal(a + b, result); Task.Delay(100); } [Fact(Skip = "Avoid Parallel.For")] public void SimpleParallelZkTest() { Task.Delay(100); var rnd = new Random(); Parallel.For(0, 12, (index, state) => { var a = rnd.Next(0, 100); var b = rnd.Next(0, 100); using (var clientProxy = new TcpClient(CreateZkClientEndPoint())) { var result = _clientProxy.Proxy.Min(a, b); if (Math.Min(a, b) != result) { state.Break(); Assert.Equal(Math.Min(a, b), result); } } Task.Delay(100); }); Task.Delay(100); } [Fact] public void ResponseZkTest() { Task.Delay(100); const int count = 50; const int start = 0; var result = _clientProxy.Proxy.Range(start, count); for (var i = start; i < count; i++) { int temp; if (result.TryGetValue(i, out temp)) { Assert.Equal(i, temp); } else { Assert.True(false); } } Task.Delay(100); } [Fact(Skip = "Avoid Parallel.For")] public void ResponseParallelTest() { Task.Delay(100); Random rnd = new Random(DateTime.Now.Millisecond); Parallel.For(0, 12, (index, state) => { const int count = 50; const int start = 0; using (var clientProxy = new TcpClient(CreateZkClientEndPoint())) { var result = clientProxy.Proxy.Range(start, count); for (var i = start; i < count; i++) { int temp; if (result.TryGetValue(i, out temp)) { if (i != temp) state.Break(); Assert.Equal(i, temp); } else { state.Break(); Assert.True(false); } } } Task.Delay(100); }); Task.Delay(100); } public void Dispose() { _clientProxy.Dispose(); _tcphost.Close(); _tcphost.Dispose(); } } } ================================================ FILE: src/Tests/Unit/ServiceWireTests/ZkProtocolTests.cs ================================================ using ServiceWire.ZeroKnowledge; using Xunit; namespace ServiceWireTests { public class ZkProtocolTests { [Fact] public void BigIntegerArrayTest() { var bigint = new BigInteger(ZkSafePrimes.N4); var bytes = BigInteger.ToByteArray(bigint); Assert.Equal(ZkSafePrimes.N4.Length, bytes.Length); for (int i = 0; i < bytes.Length; i++) { Assert.Equal(ZkSafePrimes.N4[i], bytes[i]); } var big2 = new BigInteger(bytes); Assert.Equal(bigint, big2); } [Fact] public void SimpleProtocolTest() { var sr = new ZkProtocol(); var username = "myuser@userdomain.com"; var pwd = "cc3a6a12-0e5b-47fb-ae45-3485e34582d4"; // prerequisit: generate password hash that would be stored on server var pwdHash = sr.HashCredentials(username, pwd); // Step 1. Client sends username and ephemeral hash of random number. var aRand = sr.CryptRand(); var aClientEphemeral = sr.GetClientEphemeralA(aRand); // send username and aClientEphemeral to server // Step 2. Server looks up username, gets pwd hash, and sends client ephemeral has of params. var bRand = sr.CryptRand(); var bServerEphemeral = sr.GetServerEphemeralB(pwdHash.Salt, pwdHash.Verifier, bRand); // send salt and bServerEphemeral to client var clientSalt = pwdHash.Salt; // Step 3. Client and server calculate random scramble of ephemeral hash values exchanged. var clientScramble = sr.CalculateRandomScramble(aClientEphemeral, bServerEphemeral); var serverScramble = sr.CalculateRandomScramble(aClientEphemeral, bServerEphemeral); var scrambleSame = clientScramble.IsEqualTo(serverScramble); // Step 4. Client computes session key var clientSessionKey = sr.ClientComputeSessionKey(clientSalt, username, pwd, aClientEphemeral, bServerEphemeral, clientScramble); // Step 5. Server computes session key var serverSessionKey = sr.ServerComputeSessionKey(pwdHash.Salt, pwdHash.Key, aClientEphemeral, bServerEphemeral, serverScramble); var sessionKeysSame = clientSessionKey.IsEqualTo(serverSessionKey); // Step 6. Client creates hash of session key and sends to server. Server creates same key and verifies. var clientSessionHash = sr.ClientCreateSessionHash(username, pwdHash.Salt, aClientEphemeral, bServerEphemeral, clientSessionKey); // send to server and server verifies // server validates clientSessionHash is same as serverClientSessionHash var serverClientSessionHash = sr.ClientCreateSessionHash(username, pwdHash.Salt, aClientEphemeral, bServerEphemeral, serverSessionKey); var clientEqualToServer = clientSessionHash.IsEqualTo(serverClientSessionHash); // Step 7. Server creates hash of session key and sends to client. Client creates same key and verifies. var serverSessionHash = sr.ServerCreateSessionHash(aClientEphemeral, clientSessionHash, serverSessionKey); // server sends serverSessionHash to client // validate that serverSessionHash is same as clientServerSessionHash var clientServerSessionHash = sr.ServerCreateSessionHash(aClientEphemeral, clientSessionHash, clientSessionKey); var serverEqualToClient = serverSessionHash.IsEqualTo(clientServerSessionHash); //proof Assert.True(sessionKeysSame); Assert.True(scrambleSame); Assert.True(clientEqualToServer); Assert.True(serverEqualToClient); var data = sr.Combine(sr.CryptRand(), sr.CryptRand(), sr.CryptRand()); var crypto = new ZkCrypto(clientSessionKey, clientScramble); var encrypted = crypto.Encrypt(data); var decrypted = crypto.Decrypt(encrypted); var cryptSame = data.IsEqualTo(decrypted); Assert.True(cryptSame); } } } ================================================ FILE: src/Tests/testing_readme.txt ================================================ * * * * * * * * * * * * * * * * * * ServiceWire testing readme * * * * * * * * * * * * * * * * * To run the tests, set your Visual Studio startup projects and run the test console projects in the following order: ServiceWireTestHost ServiceWireTestClient1 ServiceWireTestClient2 If you run in Debug, the test will run very slowly because Visual Studio will load each dynamically generated client proxy. We recommend that you run this test without debugging. If you want to debug through a test, we recommend that you write your own specific test or that you wait for us to create some additional tests. ================================================ FILE: src/output.txt ================================================ Build started, please wait... Build started, please wait... Build started, please wait... Build started, please wait... Build started, please wait... Build started, please wait... Build started, please wait... Build started, please wait... Build started, please wait... Build completed. Skipping running test for project D:\code\Github\ServiceWire\src\Demo\DemoCommon\DemoCommon.csproj. To run tests with dotnet test add "true" property to project file. Skipping running test for project D:\code\Github\ServiceWire\src\Demo\DemoCommon\DemoCommon.csproj. To run tests with dotnet test add "true" property to project file. Build completed. Skipping running test for project D:\code\Github\ServiceWire\src\ServiceWire\ServiceWire.csproj. To run tests with dotnet test add "true" property to project file. Skipping running test for project D:\code\Github\ServiceWire\src\ServiceWire\ServiceWire.csproj. To run tests with dotnet test add "true" property to project file. Build completed. Skipping running test for project D:\code\Github\ServiceWire\src\Tests\ServiceWireTestCommon\ServiceWireTestCommon.csproj. To run tests with dotnet test add "true" property to project file. Skipping running test for project D:\code\Github\ServiceWire\src\Tests\ServiceWireTestCommon\ServiceWireTestCommon.csproj. To run tests with dotnet test add "true" property to project file. Build completed. Build completed. Build completed. Skipping running test for project D:\code\Github\ServiceWire\src\Demo\DemoHost\DemoHost.csproj. To run tests with dotnet test add "true" property to project file. Build completed. Test run for D:\code\Github\ServiceWire\src\Tests\Unit\ServiceWireTests\bin\Debug\netcoreapp2.2\ServiceWireTests.dll(.NETCoreApp,Version=v2.2) Skipping running test for project D:\code\Github\ServiceWire\src\Demo\DemoHost\DemoHost.csproj. To run tests with dotnet test add "true" property to project file. Skipping running test for project D:\code\Github\ServiceWire\src\Tests\Integration\ServiceWireTestHost\ServiceWireTestHost.csproj. To run tests with dotnet test add "true" property to project file. Skipping running test for project D:\code\Github\ServiceWire\src\Tests\Integration\ServiceWireTestClient2\ServiceWireTestClient2.csproj. To run tests with dotnet test add "true" property to project file. Skipping running test for project D:\code\Github\ServiceWire\src\Tests\Integration\ServiceWireTestHost\ServiceWireTestHost.csproj. To run tests with dotnet test add "true" property to project file. Skipping running test for project D:\code\Github\ServiceWire\src\Tests\Integration\ServiceWireTestClient2\ServiceWireTestClient2.csproj. To run tests with dotnet test add "true" property to project file. Build completed. Build completed. Microsoft (R) Test Execution Command Line Tool Version 15.9.0 Copyright (c) Microsoft Corporation. All rights reserved. Skipping running test for project D:\code\Github\ServiceWire\src\Demo\DemoClient\DemoClient.csproj. To run tests with dotnet test add "true" property to project file. Skipping running test for project D:\code\Github\ServiceWire\src\Tests\Integration\ServiceWireTestClient1\ServiceWireTestClient1.csproj. To run tests with dotnet test add "true" property to project file. Skipping running test for project D:\code\Github\ServiceWire\src\Demo\DemoClient\DemoClient.csproj. To run tests with dotnet test add "true" property to project file. Skipping running test for project D:\code\Github\ServiceWire\src\Tests\Integration\ServiceWireTestClient1\ServiceWireTestClient1.csproj. To run tests with dotnet test add "true" property to project file. Starting test execution, please wait... Attempting to cancel the build... Attempting to cancel the build... Attempting to cancel the build... Attempting to cancel the build... Attempting to cancel the build... Attempting to cancel the build... Attempting to cancel the build... Attempting to cancel the build... C:\Program Files\dotnet\sdk\2.2.101\Microsoft.TestPlatform.targets(33,5): error MSB4018: The "Microsoft.TestPlatform.Build.Tasks.VSTestTask" task failed unexpectedly. [D:\code\Github\ServiceWire\src\Tests\Unit\ServiceWireTests\ServiceWireTests.csproj] C:\Program Files\dotnet\sdk\2.2.101\Microsoft.TestPlatform.targets(33,5): error MSB4018: System.ComponentModel.Win32Exception (5): Access is denied [D:\code\Github\ServiceWire\src\Tests\Unit\ServiceWireTests\ServiceWireTests.csproj] C:\Program Files\dotnet\sdk\2.2.101\Microsoft.TestPlatform.targets(33,5): error MSB4018: at System.Diagnostics.Process.Kill() [D:\code\Github\ServiceWire\src\Tests\Unit\ServiceWireTests\ServiceWireTests.csproj] C:\Program Files\dotnet\sdk\2.2.101\Microsoft.TestPlatform.targets(33,5): error MSB4018: at Microsoft.TestPlatform.Build.Tasks.VSTestForwardingApp.Cancel() [D:\code\Github\ServiceWire\src\Tests\Unit\ServiceWireTests\ServiceWireTests.csproj] C:\Program Files\dotnet\sdk\2.2.101\Microsoft.TestPlatform.targets(33,5): error MSB4018: at Microsoft.TestPlatform.Build.Tasks.VSTestTask.Cancel() [D:\code\Github\ServiceWire\src\Tests\Unit\ServiceWireTests\ServiceWireTests.csproj] C:\Program Files\dotnet\sdk\2.2.101\Microsoft.TestPlatform.targets(33,5): error MSB4018: at Microsoft.Build.BackEnd.TaskExecutionHost.Cancel() [D:\code\Github\ServiceWire\src\Tests\Unit\ServiceWireTests\ServiceWireTests.csproj]