├── .github └── workflows │ └── CI.yml ├── .gitignore ├── JsonRpcSharp.nuspec ├── JsonRpcSharp.sln ├── LICENSE.md ├── README.md ├── buildConf └── Version.props ├── dir.props ├── lib ├── Newtonsoft.Json.dll └── Readme.md ├── nblockchain.snk └── src ├── AssemblyInfo.cs ├── AssemblyInfo.fs ├── JsonRpcSharp.Client ├── ClientBase.cs ├── Configuration.cs ├── DefaultJsonSerializerSettingsFactory.cs ├── IClient.cs ├── IRpcRequestHandler.cs ├── JsonRpcSharp.Client.csproj ├── RPCLogger.cs ├── RPCResponseException.cs ├── RequestInterceptor.cs ├── RpcClientTimeoutException.cs ├── RpcClientUnknownException.cs ├── RpcError.cs ├── RpcMessages │ └── RpcMessages.cs ├── RpcRequest.cs ├── RpcRequestResponseHandler.cs └── RpcRequestResponseHandlerNoParam.cs ├── JsonRpcSharp.HttpClient ├── HttpClient.cs ├── JsonRpcSharp.HttpClient.csproj └── SimpleHttpClient.cs ├── JsonRpcSharp.IpcClient ├── IpcClient.cs ├── IpcClientBase.cs ├── JsonRpcSharp.IpcClient.csproj ├── JsonSerializerExtensions.cs ├── UnixDomainSocketEndPoint.cs └── UnixIpcClient.cs ├── JsonRpcSharp.TcpClient ├── JsonRpcSharp.TcpClient.fsproj └── TcpClient.fs └── JsonRpcSharp.WebSocketClient ├── JsonRpcSharp.WebSocketClient.csproj └── WebSocketClient.cs /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: Build and deploy to NuGet 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | # see https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule 9 | schedule: 10 | # daily 11 | - cron: "0 0 * * *" 12 | 13 | jobs: 14 | build_and_deploy: 15 | env: 16 | BASE_VERSION: 0.99.0 17 | runs-on: windows-latest 18 | steps: 19 | - uses: actions/checkout@v1 20 | - name: Build 21 | shell: cmd 22 | run: | 23 | dotnet build -p:Configuration=Release 24 | - name: Package and upload 25 | if: github.event_name == 'push' && github.ref == 'refs/heads/master' 26 | run: | 27 | git clone https://github.com/nblockchain/fsx 28 | fsx\Tools\fsi.bat fsx\Tools\nugetPush.fsx $env:BASE_VERSION ${{secrets.NUGET_API_KEY}} 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | #Backup 5 | 6 | Backup/ 7 | 8 | # User-specific files 9 | *.suo 10 | *.user 11 | *.userosscache 12 | *.sln.docstates 13 | 14 | # User-specific files (MonoDevelop/Xamarin Studio) 15 | *.userprefs 16 | 17 | # Build results 18 | [Dd]ebug/ 19 | [Dd]ebugPublic/ 20 | [Rr]elease/ 21 | [Rr]eleases/ 22 | x64/ 23 | x86/ 24 | bld/ 25 | [Bb]in/ 26 | [Oo]bj/ 27 | devChainRPCTests/ 28 | testchain/clique/devChain/geth 29 | testchain/devChain/geth 30 | geth.exe 31 | 32 | 33 | # Visual Studio 2015 cache/options directory 34 | .vs/ 35 | # Uncomment if you have tasks that create the project's static files in wwwroot 36 | #wwwroot/ 37 | 38 | # MSTest test Results 39 | [Tt]est[Rr]esult*/ 40 | [Bb]uild[Ll]og.* 41 | 42 | # NUNIT 43 | *.VisualState.xml 44 | TestResult.xml 45 | 46 | # Build Results of an ATL Project 47 | [Dd]ebugPS/ 48 | [Rr]eleasePS/ 49 | dlldata.c 50 | 51 | # DNX 52 | project.lock.json 53 | artifacts/ 54 | 55 | *_i.c 56 | *_p.c 57 | *_i.h 58 | *.ilk 59 | *.meta 60 | *.obj 61 | *.pch 62 | *.pdb 63 | *.pgc 64 | *.pgd 65 | *.rsp 66 | *.sbr 67 | *.tlb 68 | *.tli 69 | *.tlh 70 | *.tmp 71 | *.tmp_proj 72 | *.log 73 | *.vspscc 74 | *.vssscc 75 | .builds 76 | *.pidb 77 | *.svclog 78 | *.scc 79 | 80 | # Chutzpah Test files 81 | _Chutzpah* 82 | 83 | # Visual C++ cache files 84 | ipch/ 85 | *.aps 86 | *.ncb 87 | *.opensdf 88 | *.sdf 89 | *.cachefile 90 | 91 | # Visual Studio profiler 92 | *.psess 93 | *.vsp 94 | *.vspx 95 | *.sap 96 | 97 | # TFS 2012 Local Workspace 98 | $tf/ 99 | 100 | # Guidance Automation Toolkit 101 | *.gpState 102 | 103 | # ReSharper is a .NET coding add-in 104 | _ReSharper*/ 105 | *.[Rr]e[Ss]harper 106 | *.DotSettings.user 107 | 108 | # JustCode is a .NET coding add-in 109 | .JustCode 110 | 111 | # TeamCity is a build add-in 112 | _TeamCity* 113 | 114 | # DotCover is a Code Coverage Tool 115 | *.dotCover 116 | 117 | # NCrunch 118 | _NCrunch_* 119 | .*crunch*.local.xml 120 | nCrunchTemp_* 121 | 122 | # MightyMoose 123 | *.mm.* 124 | AutoTest.Net/ 125 | 126 | # Web workbench (sass) 127 | .sass-cache/ 128 | 129 | # Installshield output folder 130 | [Ee]xpress/ 131 | 132 | # DocProject is a documentation generator add-in 133 | DocProject/buildhelp/ 134 | DocProject/Help/*.HxT 135 | DocProject/Help/*.HxC 136 | DocProject/Help/*.hhc 137 | DocProject/Help/*.hhk 138 | DocProject/Help/*.hhp 139 | DocProject/Help/Html2 140 | DocProject/Help/html 141 | 142 | # Click-Once directory 143 | publish/ 144 | 145 | # Publish Web Output 146 | *.[Pp]ublish.xml 147 | *.azurePubxml 148 | # TODO: Comment the next line if you want to checkin your web deploy settings 149 | # but database connection strings (with potential passwords) will be unencrypted 150 | *.pubxml 151 | *.publishproj 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | 162 | # Windows Azure Build Output 163 | csx/ 164 | *.build.csdef 165 | 166 | # Windows Azure Emulator 167 | efc/ 168 | rfc/ 169 | 170 | # Windows Store app package directory 171 | AppPackages/ 172 | 173 | # Visual Studio cache files 174 | # files ending in .cache can be ignored 175 | *.[Cc]ache 176 | # but keep track of directories ending in .cache 177 | !*.[Cc]ache/ 178 | 179 | # Others 180 | ClientBin/ 181 | [Ss]tyle[Cc]op.* 182 | ~$* 183 | *~ 184 | *.dbmdl 185 | *.dbproj.schemaview 186 | *.pfx 187 | *.publishsettings 188 | node_modules/ 189 | orleans.codegen.cs 190 | 191 | # RIA/Silverlight projects 192 | Generated_Code/ 193 | 194 | # Backup & report files from converting an old project file 195 | # to a newer Visual Studio version. Backup files are not needed, 196 | # because we have git ;-) 197 | _UpgradeReport_Files/ 198 | Backup*/ 199 | UpgradeLog*.XML 200 | UpgradeLog*.htm 201 | 202 | # SQL Server files 203 | *.mdf 204 | *.ldf 205 | 206 | # Business Intelligence projects 207 | *.rdl.data 208 | *.bim.layout 209 | *.bim_*.settings 210 | 211 | # Microsoft Fakes 212 | FakesAssemblies/ 213 | 214 | # GhostDoc plugin setting file 215 | *.GhostDoc.xml 216 | 217 | # Node.js Tools for Visual Studio 218 | .ntvs_analysis.dat 219 | 220 | # Visual Studio 6 build log 221 | *.plg 222 | 223 | # Visual Studio 6 workspace options file 224 | *.opt 225 | 226 | # Visual Studio LightSwitch build output 227 | **/*.HTMLClient/GeneratedArtifacts 228 | **/*.DesktopClient/GeneratedArtifacts 229 | **/*.DesktopClient/ModelManifest.xml 230 | **/*.Server/GeneratedArtifacts 231 | **/*.Server/ModelManifest.xml 232 | _Pvt_Extensions 233 | 234 | # Paket dependency manager 235 | .paket/paket.exe 236 | 237 | # FAKE - F# Make 238 | .fake/ 239 | 240 | # Windows image file caches 241 | Thumbs.db 242 | ehthumbs.db 243 | 244 | # Folder config file 245 | Desktop.ini 246 | 247 | # Recycle Bin used on file shares 248 | 249 | $RECYCLE.BIN/ 250 | 251 | # Windows Installer files 252 | *.cab 253 | *.msi 254 | *.msm 255 | *.msp 256 | 257 | # Windows shortcuts 258 | *.lnk 259 | 260 | # ========================= 261 | # Operating System Files 262 | # ========================= 263 | 264 | # OSX 265 | # ========================= 266 | 267 | .DS_Store 268 | .AppleDouble 269 | .LSOverride 270 | 271 | # Thumbnails 272 | ._* 273 | 274 | # Files that might appear on external disk 275 | .Spotlight-V100 276 | .Trashes 277 | 278 | # Directories potentially created on remote AFP share 279 | .AppleDB 280 | .AppleDesktop 281 | Network Trash Folder 282 | Temporary Items 283 | .apdisk 284 | 285 | # githubActions artifacts 286 | fsx 287 | nuget.exe 288 | -------------------------------------------------------------------------------- /JsonRpcSharp.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JsonRpcSharp 5 | 1.0.0 6 | JsonRpcSharp 7 | juanblanco, knocte 8 | knocte 9 | https://github.com/nblockchain/JsonRpcSharp/blob/master/LICENSE 10 | https://github.com/nblockchain/JsonRpcSharp 11 | https://github.com/nblockchain/JsonRpcSharp/blob/master/images/icon.png 12 | false 13 | 14 | JsonRpcSharp 15 | 16 | 17 | See: https://github.com/nblockchain/JsonRpcSharp/commits/master 18 | 19 | JsonRpcSharp 20 | JsonRpcSharp 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /JsonRpcSharp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2036 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{962EC435-6130-4B75-BAC1-12323B067443}" 7 | ProjectSection(SolutionItems) = preProject 8 | src\AssemblyInfo.cs = src\AssemblyInfo.cs 9 | src\AssemblyInfo.fs = src\AssemblyInfo.fs 10 | EndProjectSection 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonRpcSharp.Client", "src\JsonRpcSharp.Client\JsonRpcSharp.Client.csproj", "{84A6BAA7-32B3-4D99-9280-87023BFF0A5B}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonRpcSharp.IpcClient", "src\JsonRpcSharp.IpcClient\JsonRpcSharp.IpcClient.csproj", "{3D85110C-80C3-4136-A828-22CB066362A0}" 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonRpcSharp.HttpClient", "src\JsonRpcSharp.HttpClient\JsonRpcSharp.HttpClient.csproj", "{3F8D6B13-B483-4873-8946-106D25BF6212}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonRpcSharp.WebSocketClient", "src\JsonRpcSharp.WebSocketClient\JsonRpcSharp.WebSocketClient.csproj", "{82A720E5-CF5C-4963-86CE-BEEE3040B413}" 19 | EndProject 20 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "JsonRpcSharp.TcpClient", "src\JsonRpcSharp.TcpClient\JsonRpcSharp.TcpClient.fsproj", "{B9DDEDCB-3A9F-4C9A-B222-E0F1F24222A5}" 21 | EndProject 22 | Global 23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 24 | Debug|Any CPU = Debug|Any CPU 25 | Debug|ARM = Debug|ARM 26 | Debug|x64 = Debug|x64 27 | Debug|x86 = Debug|x86 28 | Release|Any CPU = Release|Any CPU 29 | Release|ARM = Release|ARM 30 | Release|x64 = Release|x64 31 | Release|x86 = Release|x86 32 | EndGlobalSection 33 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 34 | {84A6BAA7-32B3-4D99-9280-87023BFF0A5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {84A6BAA7-32B3-4D99-9280-87023BFF0A5B}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {84A6BAA7-32B3-4D99-9280-87023BFF0A5B}.Debug|ARM.ActiveCfg = Debug|Any CPU 37 | {84A6BAA7-32B3-4D99-9280-87023BFF0A5B}.Debug|ARM.Build.0 = Debug|Any CPU 38 | {84A6BAA7-32B3-4D99-9280-87023BFF0A5B}.Debug|x64.ActiveCfg = Debug|Any CPU 39 | {84A6BAA7-32B3-4D99-9280-87023BFF0A5B}.Debug|x64.Build.0 = Debug|Any CPU 40 | {84A6BAA7-32B3-4D99-9280-87023BFF0A5B}.Debug|x86.ActiveCfg = Debug|Any CPU 41 | {84A6BAA7-32B3-4D99-9280-87023BFF0A5B}.Debug|x86.Build.0 = Debug|Any CPU 42 | {84A6BAA7-32B3-4D99-9280-87023BFF0A5B}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {84A6BAA7-32B3-4D99-9280-87023BFF0A5B}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {84A6BAA7-32B3-4D99-9280-87023BFF0A5B}.Release|ARM.ActiveCfg = Release|Any CPU 45 | {84A6BAA7-32B3-4D99-9280-87023BFF0A5B}.Release|ARM.Build.0 = Release|Any CPU 46 | {84A6BAA7-32B3-4D99-9280-87023BFF0A5B}.Release|x64.ActiveCfg = Release|Any CPU 47 | {84A6BAA7-32B3-4D99-9280-87023BFF0A5B}.Release|x64.Build.0 = Release|Any CPU 48 | {84A6BAA7-32B3-4D99-9280-87023BFF0A5B}.Release|x86.ActiveCfg = Release|Any CPU 49 | {84A6BAA7-32B3-4D99-9280-87023BFF0A5B}.Release|x86.Build.0 = Release|Any CPU 50 | {3D85110C-80C3-4136-A828-22CB066362A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 51 | {3D85110C-80C3-4136-A828-22CB066362A0}.Debug|Any CPU.Build.0 = Debug|Any CPU 52 | {3D85110C-80C3-4136-A828-22CB066362A0}.Debug|ARM.ActiveCfg = Debug|Any CPU 53 | {3D85110C-80C3-4136-A828-22CB066362A0}.Debug|ARM.Build.0 = Debug|Any CPU 54 | {3D85110C-80C3-4136-A828-22CB066362A0}.Debug|x64.ActiveCfg = Debug|Any CPU 55 | {3D85110C-80C3-4136-A828-22CB066362A0}.Debug|x64.Build.0 = Debug|Any CPU 56 | {3D85110C-80C3-4136-A828-22CB066362A0}.Debug|x86.ActiveCfg = Debug|Any CPU 57 | {3D85110C-80C3-4136-A828-22CB066362A0}.Debug|x86.Build.0 = Debug|Any CPU 58 | {3D85110C-80C3-4136-A828-22CB066362A0}.Release|Any CPU.ActiveCfg = Release|Any CPU 59 | {3D85110C-80C3-4136-A828-22CB066362A0}.Release|Any CPU.Build.0 = Release|Any CPU 60 | {3D85110C-80C3-4136-A828-22CB066362A0}.Release|ARM.ActiveCfg = Release|Any CPU 61 | {3D85110C-80C3-4136-A828-22CB066362A0}.Release|ARM.Build.0 = Release|Any CPU 62 | {3D85110C-80C3-4136-A828-22CB066362A0}.Release|x64.ActiveCfg = Release|Any CPU 63 | {3D85110C-80C3-4136-A828-22CB066362A0}.Release|x64.Build.0 = Release|Any CPU 64 | {3D85110C-80C3-4136-A828-22CB066362A0}.Release|x86.ActiveCfg = Release|Any CPU 65 | {3D85110C-80C3-4136-A828-22CB066362A0}.Release|x86.Build.0 = Release|Any CPU 66 | {3F8D6B13-B483-4873-8946-106D25BF6212}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 67 | {3F8D6B13-B483-4873-8946-106D25BF6212}.Debug|Any CPU.Build.0 = Debug|Any CPU 68 | {3F8D6B13-B483-4873-8946-106D25BF6212}.Debug|ARM.ActiveCfg = Debug|Any CPU 69 | {3F8D6B13-B483-4873-8946-106D25BF6212}.Debug|ARM.Build.0 = Debug|Any CPU 70 | {3F8D6B13-B483-4873-8946-106D25BF6212}.Debug|x64.ActiveCfg = Debug|Any CPU 71 | {3F8D6B13-B483-4873-8946-106D25BF6212}.Debug|x64.Build.0 = Debug|Any CPU 72 | {3F8D6B13-B483-4873-8946-106D25BF6212}.Debug|x86.ActiveCfg = Debug|Any CPU 73 | {3F8D6B13-B483-4873-8946-106D25BF6212}.Debug|x86.Build.0 = Debug|Any CPU 74 | {3F8D6B13-B483-4873-8946-106D25BF6212}.Release|Any CPU.ActiveCfg = Release|Any CPU 75 | {3F8D6B13-B483-4873-8946-106D25BF6212}.Release|Any CPU.Build.0 = Release|Any CPU 76 | {3F8D6B13-B483-4873-8946-106D25BF6212}.Release|ARM.ActiveCfg = Release|Any CPU 77 | {3F8D6B13-B483-4873-8946-106D25BF6212}.Release|ARM.Build.0 = Release|Any CPU 78 | {3F8D6B13-B483-4873-8946-106D25BF6212}.Release|x64.ActiveCfg = Release|Any CPU 79 | {3F8D6B13-B483-4873-8946-106D25BF6212}.Release|x64.Build.0 = Release|Any CPU 80 | {3F8D6B13-B483-4873-8946-106D25BF6212}.Release|x86.ActiveCfg = Release|Any CPU 81 | {3F8D6B13-B483-4873-8946-106D25BF6212}.Release|x86.Build.0 = Release|Any CPU 82 | {82A720E5-CF5C-4963-86CE-BEEE3040B413}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 83 | {82A720E5-CF5C-4963-86CE-BEEE3040B413}.Debug|Any CPU.Build.0 = Debug|Any CPU 84 | {82A720E5-CF5C-4963-86CE-BEEE3040B413}.Debug|ARM.ActiveCfg = Debug|Any CPU 85 | {82A720E5-CF5C-4963-86CE-BEEE3040B413}.Debug|ARM.Build.0 = Debug|Any CPU 86 | {82A720E5-CF5C-4963-86CE-BEEE3040B413}.Debug|x64.ActiveCfg = Debug|Any CPU 87 | {82A720E5-CF5C-4963-86CE-BEEE3040B413}.Debug|x64.Build.0 = Debug|Any CPU 88 | {82A720E5-CF5C-4963-86CE-BEEE3040B413}.Debug|x86.ActiveCfg = Debug|Any CPU 89 | {82A720E5-CF5C-4963-86CE-BEEE3040B413}.Debug|x86.Build.0 = Debug|Any CPU 90 | {82A720E5-CF5C-4963-86CE-BEEE3040B413}.Release|Any CPU.ActiveCfg = Release|Any CPU 91 | {82A720E5-CF5C-4963-86CE-BEEE3040B413}.Release|Any CPU.Build.0 = Release|Any CPU 92 | {82A720E5-CF5C-4963-86CE-BEEE3040B413}.Release|ARM.ActiveCfg = Release|Any CPU 93 | {82A720E5-CF5C-4963-86CE-BEEE3040B413}.Release|ARM.Build.0 = Release|Any CPU 94 | {82A720E5-CF5C-4963-86CE-BEEE3040B413}.Release|x64.ActiveCfg = Release|Any CPU 95 | {82A720E5-CF5C-4963-86CE-BEEE3040B413}.Release|x64.Build.0 = Release|Any CPU 96 | {82A720E5-CF5C-4963-86CE-BEEE3040B413}.Release|x86.ActiveCfg = Release|Any CPU 97 | {82A720E5-CF5C-4963-86CE-BEEE3040B413}.Release|x86.Build.0 = Release|Any CPU 98 | {B9DDEDCB-3A9F-4C9A-B222-E0F1F24222A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 99 | {B9DDEDCB-3A9F-4C9A-B222-E0F1F24222A5}.Debug|Any CPU.Build.0 = Debug|Any CPU 100 | {B9DDEDCB-3A9F-4C9A-B222-E0F1F24222A5}.Debug|ARM.ActiveCfg = Debug|Any CPU 101 | {B9DDEDCB-3A9F-4C9A-B222-E0F1F24222A5}.Debug|ARM.Build.0 = Debug|Any CPU 102 | {B9DDEDCB-3A9F-4C9A-B222-E0F1F24222A5}.Debug|x64.ActiveCfg = Debug|Any CPU 103 | {B9DDEDCB-3A9F-4C9A-B222-E0F1F24222A5}.Debug|x64.Build.0 = Debug|Any CPU 104 | {B9DDEDCB-3A9F-4C9A-B222-E0F1F24222A5}.Debug|x86.ActiveCfg = Debug|Any CPU 105 | {B9DDEDCB-3A9F-4C9A-B222-E0F1F24222A5}.Debug|x86.Build.0 = Debug|Any CPU 106 | {B9DDEDCB-3A9F-4C9A-B222-E0F1F24222A5}.Release|Any CPU.ActiveCfg = Release|Any CPU 107 | {B9DDEDCB-3A9F-4C9A-B222-E0F1F24222A5}.Release|Any CPU.Build.0 = Release|Any CPU 108 | {B9DDEDCB-3A9F-4C9A-B222-E0F1F24222A5}.Release|ARM.ActiveCfg = Release|Any CPU 109 | {B9DDEDCB-3A9F-4C9A-B222-E0F1F24222A5}.Release|ARM.Build.0 = Release|Any CPU 110 | {B9DDEDCB-3A9F-4C9A-B222-E0F1F24222A5}.Release|x64.ActiveCfg = Release|Any CPU 111 | {B9DDEDCB-3A9F-4C9A-B222-E0F1F24222A5}.Release|x64.Build.0 = Release|Any CPU 112 | {B9DDEDCB-3A9F-4C9A-B222-E0F1F24222A5}.Release|x86.ActiveCfg = Release|Any CPU 113 | {B9DDEDCB-3A9F-4C9A-B222-E0F1F24222A5}.Release|x86.Build.0 = Release|Any CPU 114 | EndGlobalSection 115 | GlobalSection(SolutionProperties) = preSolution 116 | HideSolutionNode = FALSE 117 | EndGlobalSection 118 | GlobalSection(NestedProjects) = preSolution 119 | {84A6BAA7-32B3-4D99-9280-87023BFF0A5B} = {962EC435-6130-4B75-BAC1-12323B067443} 120 | {3D85110C-80C3-4136-A828-22CB066362A0} = {962EC435-6130-4B75-BAC1-12323B067443} 121 | {3F8D6B13-B483-4873-8946-106D25BF6212} = {962EC435-6130-4B75-BAC1-12323B067443} 122 | {82A720E5-CF5C-4963-86CE-BEEE3040B413} = {962EC435-6130-4B75-BAC1-12323B067443} 123 | {B9DDEDCB-3A9F-4C9A-B222-E0F1F24222A5} = {962EC435-6130-4B75-BAC1-12323B067443} 124 | EndGlobalSection 125 | GlobalSection(ExtensibilityGlobals) = postSolution 126 | SolutionGuid = {07A44726-8749-4A9A-8079-FA3C3213BDC1} 127 | EndGlobalSection 128 | EndGlobal 129 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Nethereum.com (Juan Blanco) , Logo by Cass (https://github.com/cassiopaia) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or ANY portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | You can consume this library via nuget: https://www.nuget.org/packages/JsonRpcSharp 2 | -------------------------------------------------------------------------------- /buildConf/Version.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 0 4 | 1 5 | 0 6 | $(ReleaseSuffix) 7 | 8 | 9 | $(VersionMajor).$(VersionMinor).$(VersionPatch)-$(RelSuffix) 10 | 11 | $(FullNugetVersion) 12 | $(VersionMajor).$(VersionMinor).$(VersionPatch) 13 | 14 | $(NugetVersion) 15 | 16 | 17 | -------------------------------------------------------------------------------- /dir.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /lib/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nblockchain/JsonRpcSharp/d6738480b27802efffeddcaa44b4e6a1e289124a/lib/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /lib/Readme.md: -------------------------------------------------------------------------------- 1 | #External Libraries 2 | ## Newtonsoft.Json.dll 3 | This library is a modified version of Newtonsoft.Json.dll compatible with Unity3d 4 | For more information check https://github.com/SaladLab/Json.Net.Unity3D 5 | -------------------------------------------------------------------------------- /nblockchain.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nblockchain/JsonRpcSharp/d6738480b27802efffeddcaa44b4e6a1e289124a/nblockchain.snk -------------------------------------------------------------------------------- /src/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 |  2 | using System.Reflection; 3 | 4 | [assembly: AssemblyKeyFile("../../nblockchain.snk")] 5 | -------------------------------------------------------------------------------- /src/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | namespace JsonRpcSharp.TcpClient 2 | 3 | open System.Reflection 4 | 5 | [] 6 | 7 | do 8 | () 9 | -------------------------------------------------------------------------------- /src/JsonRpcSharp.Client/ClientBase.cs: -------------------------------------------------------------------------------- 1 | #if !DOTNET35 2 | using System; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using JsonRpcSharp.Client.RpcMessages; 6 | 7 | namespace JsonRpcSharp.Client 8 | { 9 | public abstract class ClientBase : IClient 10 | { 11 | 12 | public TimeSpan ConnectionTimeout { get; set; } = TimeSpan.FromSeconds(20.0); 13 | 14 | public RequestInterceptor OverridingRequestInterceptor { get; set; } 15 | 16 | public async Task SendRequestAsync(RpcRequest request, 17 | string route = null, 18 | CancellationToken cancellationToken = default(CancellationToken)) 19 | { 20 | if (OverridingRequestInterceptor != null) 21 | return 22 | (T) 23 | await OverridingRequestInterceptor.InterceptSendRequestAsync(SendInnerRequestAsync, 24 | request, 25 | route, 26 | cancellationToken) 27 | .ConfigureAwait(false); 28 | return await SendInnerRequestAsync(request, route, cancellationToken).ConfigureAwait(false); 29 | } 30 | 31 | public async Task SendRequestAsync(string method, 32 | string route = null, 33 | CancellationToken cancellationToken = default(CancellationToken), 34 | params object[] paramList) 35 | { 36 | if (OverridingRequestInterceptor != null) 37 | return 38 | (T) 39 | await OverridingRequestInterceptor.InterceptSendRequestAsync(SendInnerRequestAsync, 40 | method, 41 | route, 42 | cancellationToken, 43 | paramList).ConfigureAwait(false); 44 | return await SendInnerRequestAsync(method, route, cancellationToken, paramList).ConfigureAwait(false); 45 | } 46 | 47 | protected void HandleRpcError(RpcResponseMessage response) 48 | { 49 | if (response.HasError) 50 | throw new RpcResponseException(new RpcError(response.Error.Code, response.Error.Message, 51 | response.Error.Data)); 52 | } 53 | 54 | private async Task SendInnerRequestAsync(RpcRequestMessage reqMsg, 55 | string route = null, 56 | CancellationToken cancellationToken = default(CancellationToken)) 57 | { 58 | var response = await SendAsync(reqMsg, route, cancellationToken).ConfigureAwait(false); 59 | HandleRpcError(response); 60 | try 61 | { 62 | return response.GetResult(); 63 | } 64 | catch (FormatException formatException) 65 | { 66 | throw new RpcResponseFormatException("Invalid format found in RPC response", formatException); 67 | } 68 | } 69 | 70 | protected virtual async Task SendInnerRequestAsync(RpcRequest request, 71 | string route = null, 72 | CancellationToken cancellationToken = default(CancellationToken)) 73 | { 74 | var reqMsg = new RpcRequestMessage(request.Id, 75 | request.Method, 76 | request.RawParameters); 77 | return await SendInnerRequestAsync(reqMsg, route, cancellationToken).ConfigureAwait(false); 78 | } 79 | 80 | protected virtual async Task SendInnerRequestAsync(string method, 81 | string route = null, 82 | CancellationToken cancellationToken = default(CancellationToken), 83 | params object[] paramList) 84 | { 85 | var request = new RpcRequestMessage(Guid.NewGuid().ToString(), method, paramList); 86 | return await SendInnerRequestAsync(request, route, cancellationToken); 87 | } 88 | 89 | public virtual async Task SendRequestAsync(RpcRequest request, 90 | string route = null, 91 | CancellationToken cancellationToken = default(CancellationToken)) 92 | { 93 | var response = 94 | await SendAsync( 95 | new RpcRequestMessage(request.Id, request.Method, request.RawParameters), route, cancellationToken) 96 | .ConfigureAwait(false); 97 | HandleRpcError(response); 98 | } 99 | 100 | protected abstract Task SendAsync(RpcRequestMessage rpcRequestMessage, 101 | string route = null, 102 | CancellationToken cancellationToken = default(CancellationToken)); 103 | 104 | public virtual async Task SendRequestAsync(string method, 105 | string route = null, 106 | CancellationToken cancellationToken = default(CancellationToken), 107 | params object[] paramList) 108 | { 109 | var request = new RpcRequestMessage(Guid.NewGuid().ToString(), method, paramList); 110 | var response = await SendAsync(request, route, cancellationToken).ConfigureAwait(false); 111 | HandleRpcError(response); 112 | } 113 | 114 | protected CancellationToken GetEffectiveCancellationToken(CancellationToken providedToken, TimeSpan timeout) 115 | { 116 | if (providedToken == CancellationToken.None) 117 | { 118 | var cancellationTokenSource = new CancellationTokenSource(); 119 | cancellationTokenSource.CancelAfter(timeout); 120 | return cancellationTokenSource.Token; 121 | } 122 | return providedToken; 123 | } 124 | } 125 | } 126 | #endif -------------------------------------------------------------------------------- /src/JsonRpcSharp.Client/Configuration.cs: -------------------------------------------------------------------------------- 1 | namespace JsonRpcSharp.Client 2 | { 3 | public class Configuration 4 | { 5 | public static object DefaultRequestId { get; set; } = 1; 6 | } 7 | } -------------------------------------------------------------------------------- /src/JsonRpcSharp.Client/DefaultJsonSerializerSettingsFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using Newtonsoft.Json; 6 | using Newtonsoft.Json.Serialization; 7 | 8 | namespace JsonRpcSharp.Client 9 | { 10 | public static class DefaultJsonSerializerSettingsFactory 11 | { 12 | public static JsonSerializerSettings BuildDefaultJsonSerializerSettings() 13 | { 14 | return new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, 15 | MissingMemberHandling = MissingMemberHandling.Ignore }; 16 | //ContractResolver = new NullParamsFirstElementResolver()}; Not required anymore. 17 | } 18 | } 19 | #if !DOTNET35 20 | //Passing a null value as the first parameter in the rpc (as no value) causes issues on client as it is not being ignored deserialising, as it is treated as the first element of the array. 21 | public class NullParamsFirstElementResolver : DefaultContractResolver 22 | { 23 | protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) 24 | { 25 | return type.GetTypeInfo().DeclaredProperties 26 | .Select(p => 27 | { 28 | var jp = CreateProperty(p, memberSerialization); 29 | jp.ValueProvider = new NullParamsValueProvider(p); 30 | return jp; 31 | }).ToList(); 32 | } 33 | } 34 | 35 | public class NullParamsValueProvider : IValueProvider 36 | { 37 | private readonly PropertyInfo memberInfo; 38 | 39 | public NullParamsValueProvider(PropertyInfo memberInfo) 40 | { 41 | this.memberInfo = memberInfo; 42 | } 43 | 44 | public object GetValue(object target) 45 | { 46 | var result = memberInfo.GetValue(target); 47 | if (memberInfo.Name == "RawParameters") 48 | { 49 | var array = result as object[]; 50 | if (array != null && array.Length == 1 && array[0] == null) 51 | result = "[]"; 52 | } 53 | return result; 54 | } 55 | 56 | public void SetValue(object target, object value) 57 | { 58 | memberInfo.SetValue(target, value); 59 | } 60 | } 61 | #endif 62 | } -------------------------------------------------------------------------------- /src/JsonRpcSharp.Client/IClient.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace JsonRpcSharp.Client 5 | { 6 | public interface IClient 7 | { 8 | #if !DOTNET35 9 | RequestInterceptor OverridingRequestInterceptor { get; set; } 10 | #endif 11 | Task SendRequestAsync(RpcRequest request, 12 | string route = null, 13 | CancellationToken cancellationToken = default(CancellationToken)); 14 | 15 | Task SendRequestAsync(string method, 16 | string route = null, 17 | CancellationToken cancellationToken = default(CancellationToken), 18 | params object[] paramList); 19 | 20 | Task SendRequestAsync(RpcRequest request, 21 | string route = null, 22 | CancellationToken cancellationToken = default(CancellationToken)); 23 | 24 | Task SendRequestAsync(string method, 25 | string route = null, 26 | CancellationToken cancellationToken = default(CancellationToken), 27 | params object[] paramList); 28 | } 29 | } -------------------------------------------------------------------------------- /src/JsonRpcSharp.Client/IRpcRequestHandler.cs: -------------------------------------------------------------------------------- 1 | namespace JsonRpcSharp.Client 2 | { 3 | public interface IRpcRequestHandler 4 | { 5 | string MethodName { get; } 6 | IClient Client { get; } 7 | } 8 | } -------------------------------------------------------------------------------- /src/JsonRpcSharp.Client/JsonRpcSharp.Client.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | JsonRpcSharp.Client core library to use in conjunction with either the JsonRpcSharp.HttpClient, the JsonRpcSharp.IpcClient or other custom Rpc provider 4 | Juan Blanco 5 | JsonRpcSharp.Client 6 | $(JsonRpcSharpVersion) 7 | netstandard2.0 8 | JsonRpcSharp.Client 9 | JsonRpcSharp.Client 10 | Json;RPC 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | AssemblyInfo.cs 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/JsonRpcSharp.Client/RPCLogger.cs: -------------------------------------------------------------------------------- 1 | #if !DOTNET35 2 | using System; 3 | using Common.Logging; 4 | using JsonRpcSharp.Client.RpcMessages; 5 | 6 | namespace JsonRpcSharp.Client 7 | { 8 | 9 | public class RpcLogger 10 | { 11 | public RpcLogger(ILog log) 12 | { 13 | Log = log; 14 | } 15 | public ILog Log { get; private set; } 16 | public string RequestJsonMessage { get; private set; } 17 | public RpcResponseMessage ResponseMessage { get; private set; } 18 | 19 | public void LogRequest(string requestJsonMessage) 20 | { 21 | RequestJsonMessage = requestJsonMessage; 22 | if (IsLogTraceEnabled()) 23 | { 24 | Log.Trace(GetRPCRequestLogMessage() ); 25 | } 26 | } 27 | 28 | private string GetRPCRequestLogMessage() 29 | { 30 | return $"RPC Request: {RequestJsonMessage}"; 31 | } 32 | 33 | private string GetRPCResponseLogMessage() 34 | { 35 | return ResponseMessage != null ? $"RPC Response: {ResponseMessage.Result}" : String.Empty; 36 | } 37 | private bool IsLogErrorEnabled() 38 | { 39 | return Log != null && Log.IsErrorEnabled; 40 | } 41 | 42 | public void LogResponse(RpcResponseMessage responseMessage) 43 | { 44 | if (responseMessage == null) 45 | throw new ArgumentNullException(nameof(responseMessage)); 46 | 47 | ResponseMessage = responseMessage; 48 | 49 | if (IsLogTraceEnabled()) 50 | { 51 | Log.Trace(GetRPCResponseLogMessage()); 52 | } 53 | 54 | if (HasError(responseMessage) && IsLogErrorEnabled()) 55 | { 56 | if (!IsLogTraceEnabled()) 57 | { 58 | Log.Trace(GetRPCResponseLogMessage()); 59 | } 60 | Log.Error($"RPC Response Error: {responseMessage.Error.Message}"); 61 | } 62 | } 63 | 64 | public void LogException(Exception ex) 65 | { 66 | if (IsLogErrorEnabled()) 67 | { 68 | Log.Error("RPC Exception, " + GetRPCRequestLogMessage() + GetRPCResponseLogMessage(), ex); 69 | } 70 | } 71 | 72 | private bool HasError(RpcResponseMessage message) 73 | { 74 | return message.Error != null && message.HasError; 75 | } 76 | 77 | private bool IsLogTraceEnabled() 78 | { 79 | return Log != null && Log.IsTraceEnabled; 80 | } 81 | 82 | } 83 | 84 | } 85 | #endif -------------------------------------------------------------------------------- /src/JsonRpcSharp.Client/RPCResponseException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace JsonRpcSharp.Client 4 | { 5 | public class RpcResponseException : Exception 6 | { 7 | public RpcResponseException(RpcError rpcError) : base(rpcError.Message) 8 | { 9 | RpcError = rpcError; 10 | } 11 | 12 | public RpcError RpcError { get; } 13 | } 14 | 15 | public class RpcResponseFormatException : Exception 16 | { 17 | public RpcResponseFormatException(string message, FormatException innerException) 18 | : base(message, innerException) 19 | { 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/JsonRpcSharp.Client/RequestInterceptor.cs: -------------------------------------------------------------------------------- 1 | #if !DOTNET35 2 | using System; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace JsonRpcSharp.Client 7 | { 8 | public abstract class RequestInterceptor 9 | { 10 | public virtual async Task InterceptSendRequestAsync( 11 | Func> interceptedSendRequestAsync, 12 | RpcRequest request, 13 | string route = null, 14 | CancellationToken cancellationToken = default(CancellationToken)) 15 | { 16 | return await interceptedSendRequestAsync(request, route, cancellationToken).ConfigureAwait(false); 17 | } 18 | 19 | public virtual async Task InterceptSendRequestAsync( 20 | Func interceptedSendRequestAsync, 21 | RpcRequest request, 22 | CancellationToken cancellationToken = default(CancellationToken), 23 | string route = null) 24 | { 25 | await interceptedSendRequestAsync(request, route, cancellationToken).ConfigureAwait(false); 26 | } 27 | 28 | public virtual async Task InterceptSendRequestAsync( 29 | Func> interceptedSendRequestAsync, 30 | string method, 31 | string route = null, 32 | CancellationToken cancellationToken = default(CancellationToken), 33 | params object[] paramList) 34 | { 35 | return await interceptedSendRequestAsync(method, route, cancellationToken, paramList).ConfigureAwait(false); 36 | } 37 | 38 | public virtual Task InterceptSendRequestAsync( 39 | Func interceptedSendRequestAsync, 40 | string method, 41 | string route = null, 42 | CancellationToken cancellationToken = default(CancellationToken), 43 | params object[] paramList) 44 | { 45 | return interceptedSendRequestAsync(method, route, cancellationToken, paramList); 46 | } 47 | } 48 | } 49 | #endif -------------------------------------------------------------------------------- /src/JsonRpcSharp.Client/RpcClientTimeoutException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace JsonRpcSharp.Client 4 | { 5 | public class RpcClientTimeoutException : Exception 6 | { 7 | public RpcClientTimeoutException(string message) : base(message) 8 | { 9 | 10 | } 11 | 12 | public RpcClientTimeoutException(string message, Exception innerException) : base(message, innerException) 13 | { 14 | 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/JsonRpcSharp.Client/RpcClientUnknownException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace JsonRpcSharp.Client 4 | { 5 | public class RpcClientUnknownException : Exception 6 | { 7 | public RpcClientUnknownException(string message) : base(message) 8 | { 9 | 10 | } 11 | 12 | public RpcClientUnknownException(string message, Exception innerException) : base(message, innerException) 13 | { 14 | 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/JsonRpcSharp.Client/RpcError.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | 3 | namespace JsonRpcSharp.Client 4 | { 5 | public class RpcError 6 | { 7 | public RpcError(int code, string message, JToken data = null) 8 | { 9 | Code = code; 10 | Message = message; 11 | Data = data; 12 | } 13 | 14 | public int Code { get; private set; } 15 | public string Message { get; private set; } 16 | public JToken Data { get; private set; } 17 | } 18 | } -------------------------------------------------------------------------------- /src/JsonRpcSharp.Client/RpcMessages/RpcMessages.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Newtonsoft.Json; 5 | using Newtonsoft.Json.Linq; 6 | 7 | namespace JsonRpcSharp.Client.RpcMessages 8 | { 9 | /* 10 | 11 | RPC Model simplified and downported to net351 from EdjCase.JsonRPC.Core 12 | https://github.com/edjCase/JsonRpc/tree/master/src/EdjCase.JsonRpc.Core 13 | 14 | The MIT License (MIT) 15 | Copyright(c) 2015 Gekctek 16 | 17 | Permission is hereby granted, free of charge, to any person obtaining a copy 18 | of this software and associated documentation files (the "Software"), to deal 19 | in the Software without restriction, including without limitation the rights 20 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 21 | copies of the Software, and to permit persons to whom the Software is 22 | furnished to do so, subject to the following conditions: 23 | 24 | The above copyright notice and this permission notice shall be included in all 25 | copies or substantial portions of the Software. 26 | 27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 30 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 32 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 33 | SOFTWARE. 34 | */ 35 | public static class RpcResponseExtensions 36 | { 37 | /// 38 | /// Parses and returns the result of the rpc response as the type specified. 39 | /// Otherwise throws a parsing exception 40 | /// 41 | /// Type of object to parse the response as 42 | /// Rpc response object 43 | /// Returns the type's default value if the result is null. Otherwise throws parsing exception 44 | /// Result of response as type specified 45 | public static T GetResult(this RpcResponseMessage response, bool returnDefaultIfNull = true, JsonSerializerSettings settings = null) 46 | { 47 | if (response.Result == null) 48 | { 49 | if (!returnDefaultIfNull && default(T) != null) 50 | { 51 | throw new Exception("Unable to convert the result (null) to type " + typeof(T)); 52 | } 53 | return default(T); 54 | } 55 | try 56 | { 57 | if (settings == null) 58 | { 59 | return response.Result.ToObject(); 60 | } 61 | else 62 | { 63 | JsonSerializer jsonSerializer = JsonSerializer.Create(settings); 64 | return response.Result.ToObject(jsonSerializer); 65 | } 66 | } 67 | catch (FormatException ex) 68 | { 69 | throw new FormatException("Invalid format when trying to convert the result to type " + typeof(T), ex); 70 | } 71 | catch (Exception ex) 72 | { 73 | throw new Exception("Unable to convert the result to type " + typeof(T), ex); 74 | } 75 | } 76 | } 77 | 78 | [JsonObject] 79 | public class RpcResponseMessage 80 | { 81 | [JsonConstructor] 82 | protected RpcResponseMessage() 83 | { 84 | JsonRpcVersion = "2.0"; 85 | } 86 | 87 | /// Request id 88 | protected RpcResponseMessage(object id) 89 | { 90 | this.Id = id; 91 | JsonRpcVersion = "2.0"; 92 | } 93 | 94 | /// Request id 95 | /// Request error 96 | public RpcResponseMessage(object id, RpcError error) : this(id) 97 | { 98 | this.Error = error; 99 | } 100 | 101 | /// Request id 102 | /// Response result object 103 | public RpcResponseMessage(object id, JToken result) : this(id) 104 | { 105 | this.Result = result; 106 | } 107 | 108 | /// 109 | /// Request id (Required but nullable) 110 | /// 111 | [JsonProperty("id", Required = Required.AllowNull)] 112 | public object Id { get; private set; } 113 | 114 | /// 115 | /// Rpc request version (Required) 116 | /// 117 | [JsonProperty("jsonrpc", Required = Required.Always)] 118 | public string JsonRpcVersion { get; private set; } 119 | 120 | /// 121 | /// Reponse result object (Required) 122 | /// 123 | [JsonProperty("result", Required = Required.Default)] 124 | public JToken Result { get; private set; } 125 | 126 | /// 127 | /// Error from processing Rpc request (Required) 128 | /// 129 | [JsonProperty("error", Required = Required.Default)] 130 | public RpcError Error { get; private set; } 131 | 132 | [JsonIgnore] 133 | public bool HasError { get { return this.Error != null; } } 134 | } 135 | 136 | [JsonObject] 137 | public class RpcRequestMessage 138 | { 139 | [JsonConstructor] 140 | private RpcRequestMessage() 141 | { 142 | 143 | } 144 | 145 | /// Request id 146 | /// Target method name 147 | /// List of parameters for the target method 148 | public RpcRequestMessage(object id, string method, params object[] parameterList) 149 | { 150 | this.Id = id; 151 | this.JsonRpcVersion = "2.0"; 152 | this.Method = method; 153 | this.RawParameters = parameterList; 154 | } 155 | 156 | /// Request id 157 | /// Target method name 158 | /// Map of parameter name to parameter value for the target method 159 | public RpcRequestMessage(object id, string method, Dictionary parameterMap) 160 | { 161 | this.Id = id; 162 | this.JsonRpcVersion = "2.0"; 163 | this.Method = method; 164 | this.RawParameters = parameterMap; 165 | } 166 | 167 | /// 168 | /// Request Id (Optional) 169 | /// 170 | [JsonProperty("id")] 171 | public object Id { get; private set; } 172 | /// 173 | /// Version of the JsonRpc to be used (Required) 174 | /// 175 | [JsonProperty("jsonrpc", Required = Required.Always)] 176 | public string JsonRpcVersion { get; private set; } 177 | /// 178 | /// Name of the target method (Required) 179 | /// 180 | [JsonProperty("method", Required = Required.Always)] 181 | public string Method { get; private set; } 182 | /// 183 | /// Parameters to invoke the method with (Optional) 184 | /// 185 | [JsonProperty("params")] 186 | [JsonConverter(typeof(RpcParametersJsonConverter))] 187 | public object RawParameters { get; private set; } 188 | 189 | } 190 | /// 191 | /// Json converter for Rpc parameters 192 | /// 193 | public class RpcParametersJsonConverter : JsonConverter 194 | { 195 | /// 196 | /// Writes the value of the parameters to json format 197 | /// 198 | /// Json writer 199 | /// Value to be converted to json format 200 | /// Json serializer 201 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 202 | { 203 | serializer.Serialize(writer, value); 204 | } 205 | 206 | /// 207 | /// Read the json format and return the correct object type/value for it 208 | /// 209 | /// Json reader 210 | /// Type of property being set 211 | /// The current value of the property being set 212 | /// Json serializer 213 | /// The object value of the converted json value 214 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 215 | { 216 | switch (reader.TokenType) 217 | { 218 | case JsonToken.StartObject: 219 | try 220 | { 221 | JObject jObject = JObject.Load(reader); 222 | return jObject.ToObject>(); 223 | } 224 | catch (Exception) 225 | { 226 | throw new Exception("Request parameters can only be an associative array, list or null."); 227 | } 228 | case JsonToken.StartArray: 229 | return JArray.Load(reader).ToObject(serializer); 230 | case JsonToken.Null: 231 | return null; 232 | } 233 | throw new Exception("Request parameters can only be an associative array, list or null."); 234 | } 235 | 236 | /// 237 | /// Determines if the type can be convertered with this converter 238 | /// 239 | /// Type of the object 240 | /// True if the converter converts the specified type, otherwise False 241 | public override bool CanConvert(Type objectType) 242 | { 243 | return true; 244 | } 245 | } 246 | 247 | [JsonObject] 248 | public class RpcError 249 | { 250 | [JsonConstructor] 251 | private RpcError() 252 | { 253 | } 254 | /// 255 | /// Rpc error code 256 | /// 257 | [JsonProperty("code")] 258 | public int Code { get; private set; } 259 | 260 | /// 261 | /// Error message (Required) 262 | /// 263 | [JsonProperty("message", Required = Required.Always)] 264 | public string Message { get; private set; } 265 | 266 | /// 267 | /// Error data (Optional) 268 | /// 269 | [JsonProperty("data")] 270 | public JToken Data { get; private set; } 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /src/JsonRpcSharp.Client/RpcRequest.cs: -------------------------------------------------------------------------------- 1 | namespace JsonRpcSharp.Client 2 | { 3 | public class RpcRequest 4 | { 5 | public RpcRequest(object id, string method, params object[] parameterList) 6 | { 7 | Id = id; 8 | Method = method; 9 | RawParameters = parameterList; 10 | } 11 | 12 | public object Id { get; private set; } 13 | public string Method { get; private set; } 14 | public object[] RawParameters { get; private set; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/JsonRpcSharp.Client/RpcRequestResponseHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace JsonRpcSharp.Client 5 | { 6 | public class RpcRequestResponseHandler : IRpcRequestHandler 7 | { 8 | public RpcRequestResponseHandler(IClient client, string methodName) 9 | { 10 | MethodName = methodName; 11 | Client = client; 12 | } 13 | 14 | public string MethodName { get; } 15 | 16 | public IClient Client { get; } 17 | 18 | protected Task SendRequestAsync(object id, 19 | CancellationToken cancellationToken = default(CancellationToken), 20 | params object[] paramList) 21 | { 22 | var request = BuildRequest(id, paramList); 23 | return Client.SendRequestAsync(request, null, cancellationToken); 24 | } 25 | 26 | public RpcRequest BuildRequest(object id, params object[] paramList) 27 | { 28 | if (id == null) id = Configuration.DefaultRequestId; 29 | return new RpcRequest(id, MethodName, paramList); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/JsonRpcSharp.Client/RpcRequestResponseHandlerNoParam.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace JsonRpcSharp.Client 5 | { 6 | public class RpcRequestResponseHandlerNoParam : IRpcRequestHandler 7 | { 8 | public RpcRequestResponseHandlerNoParam(IClient client, string methodName) 9 | { 10 | MethodName = methodName; 11 | Client = client; 12 | } 13 | 14 | public string MethodName { get; } 15 | public IClient Client { get; } 16 | 17 | public virtual Task SendRequestAsync(object id, 18 | CancellationToken cancellationToken = default(CancellationToken)) 19 | { 20 | return Client.SendRequestAsync(BuildRequest(id), null, cancellationToken); 21 | } 22 | 23 | public RpcRequest BuildRequest(object id = null) 24 | { 25 | if (id == null) id = Configuration.DefaultRequestId; 26 | 27 | return new RpcRequest(id, MethodName); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/JsonRpcSharp.HttpClient/HttpClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net.Http; 4 | using System.Net.Http.Headers; 5 | using System.Runtime.Serialization; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using Common.Logging; 10 | using JsonRpcSharp.Client.RpcMessages; 11 | using Newtonsoft.Json; 12 | 13 | namespace JsonRpcSharp.Client 14 | { 15 | [Serializable] 16 | public class DeserializationException : Exception 17 | { 18 | public DeserializationException() 19 | { 20 | } 21 | 22 | 23 | public DeserializationException(string originalText, Exception innerException) 24 | : base("Couldn't deserialize to JSON: " + originalText, innerException) 25 | { 26 | } 27 | 28 | public DeserializationException(string originalText) 29 | : base("Couldn't deserialize to JSON (result was null): " + originalText) 30 | { 31 | } 32 | 33 | protected DeserializationException(SerializationInfo info, StreamingContext context) : base(info, context) 34 | { 35 | } 36 | } 37 | 38 | public class HttpClient : ClientBase 39 | { 40 | private const int NUMBER_OF_SECONDS_TO_RECREATE_HTTP_CLIENT = 60; 41 | private readonly AuthenticationHeaderValue _authHeaderValue; 42 | private readonly Uri _baseUrl; 43 | private readonly HttpClientHandler _httpClientHandler; 44 | private readonly ILog _log; 45 | private readonly JsonSerializerSettings _jsonSerializerSettings; 46 | private volatile bool _firstHttpClient; 47 | private System.Net.Http.HttpClient _httpClient; 48 | private System.Net.Http.HttpClient _httpClient2; 49 | private DateTime _httpClientLastCreatedAt; 50 | private readonly object _lockObject = new object(); 51 | 52 | public HttpClient(Uri baseUrl, TimeSpan connectionTimeout, AuthenticationHeaderValue authHeaderValue = null, 53 | JsonSerializerSettings jsonSerializerSettings = null, System.Net.Http.HttpClientHandler httpClientHandler = null, ILog log = null) 54 | { 55 | _baseUrl = baseUrl; 56 | _authHeaderValue = authHeaderValue; 57 | if (jsonSerializerSettings == null) 58 | jsonSerializerSettings = DefaultJsonSerializerSettingsFactory.BuildDefaultJsonSerializerSettings(); 59 | _jsonSerializerSettings = jsonSerializerSettings; 60 | _httpClientHandler = httpClientHandler; 61 | _log = log; 62 | CreateNewHttpClient(); 63 | this.ConnectionTimeout = connectionTimeout; 64 | } 65 | 66 | private string GetOriginalText(StreamReader streamReader) 67 | { 68 | if (streamReader == null) 69 | throw new ArgumentNullException(nameof(streamReader)); 70 | streamReader.DiscardBufferedData(); 71 | streamReader.BaseStream.Seek(0, SeekOrigin.Begin); 72 | return streamReader.ReadToEnd(); 73 | } 74 | 75 | protected override async Task SendAsync(RpcRequestMessage request, 76 | string route = null, 77 | CancellationToken cancellationToken = default(CancellationToken)) 78 | { 79 | var logger = new RpcLogger(_log); 80 | try 81 | { 82 | var httpClient = GetOrCreateHttpClient(); 83 | var rpcRequestJson = JsonConvert.SerializeObject(request, _jsonSerializerSettings); 84 | var httpContent = new StringContent(rpcRequestJson, Encoding.UTF8, "application/json"); 85 | var effectiveCancellationToken = GetEffectiveCancellationToken(cancellationToken, ConnectionTimeout); 86 | logger.LogRequest(rpcRequestJson); 87 | 88 | var httpResponseMessage = await httpClient.PostAsync(route, httpContent, effectiveCancellationToken).ConfigureAwait(false); 89 | 90 | cancellationToken.ThrowIfCancellationRequested(); 91 | httpResponseMessage.EnsureSuccessStatusCode(); 92 | 93 | var stream = await httpResponseMessage.Content.ReadAsStreamAsync(); 94 | using (var streamReader = new StreamReader(stream)) 95 | using (var reader = new JsonTextReader(streamReader)) 96 | { 97 | var serializer = JsonSerializer.Create(_jsonSerializerSettings); 98 | RpcResponseMessage message; 99 | try 100 | { 101 | message = serializer.Deserialize(reader); 102 | } 103 | catch (JsonReaderException e) 104 | { 105 | var originalText = GetOriginalText(streamReader); 106 | throw new DeserializationException(originalText, e); 107 | } 108 | catch (JsonSerializationException e) 109 | { 110 | var originalText = GetOriginalText(streamReader); 111 | throw new DeserializationException(originalText, e); 112 | } 113 | /* for debugging purposes 114 | finally 115 | { 116 | streamReader.DiscardBufferedData(); 117 | streamReader.BaseStream.Seek(0, SeekOrigin.Begin); 118 | var originalText = streamReader.ReadToEnd(); 119 | Console.WriteLine("_________DEBUG:" + originalText); 120 | Console.Out.Flush(); 121 | } 122 | */ 123 | 124 | if (message == null) 125 | { 126 | var originalText = GetOriginalText(streamReader); 127 | throw new DeserializationException(originalText); 128 | } 129 | 130 | logger.LogResponse(message); 131 | 132 | return message; 133 | } 134 | } 135 | catch(TaskCanceledException ex) 136 | { 137 | throw new RpcClientTimeoutException($"Rpc timeout after {ConnectionTimeout.TotalMilliseconds} milliseconds", ex); 138 | } 139 | catch (Exception ex) 140 | { 141 | logger.LogException(ex); 142 | throw new RpcClientUnknownException("Error occurred when trying to send rpc requests(s)", ex); 143 | } 144 | } 145 | 146 | 147 | 148 | private System.Net.Http.HttpClient GetOrCreateHttpClient() 149 | { 150 | lock (_lockObject) 151 | { 152 | var timeSinceCreated = DateTime.UtcNow - _httpClientLastCreatedAt; 153 | if (timeSinceCreated.TotalSeconds > NUMBER_OF_SECONDS_TO_RECREATE_HTTP_CLIENT) 154 | CreateNewHttpClient(); 155 | return GetClient(); 156 | } 157 | } 158 | 159 | private System.Net.Http.HttpClient GetClient() 160 | { 161 | lock (_lockObject) 162 | { 163 | return _firstHttpClient ? _httpClient : _httpClient2; 164 | } 165 | } 166 | 167 | private void CreateNewHttpClient() 168 | { 169 | var httpClient = _httpClientHandler != null ? new System.Net.Http.HttpClient(_httpClientHandler) : new System.Net.Http.HttpClient(); 170 | httpClient.DefaultRequestHeaders.Authorization = _authHeaderValue; 171 | httpClient.BaseAddress = _baseUrl; 172 | _httpClientLastCreatedAt = DateTime.UtcNow; 173 | if (_firstHttpClient) 174 | lock (_lockObject) 175 | { 176 | _firstHttpClient = false; 177 | _httpClient2 = httpClient; 178 | } 179 | else 180 | lock (_lockObject) 181 | { 182 | _firstHttpClient = true; 183 | _httpClient = httpClient; 184 | } 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/JsonRpcSharp.HttpClient/JsonRpcSharp.HttpClient.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | JsonRpc Http Client provider 4 | Juan Blanco 5 | JsonRpcSharp.HttpClient 6 | $(JsonRpcSharpVersion) 7 | netstandard2.0 8 | JsonRpcSharp.HttpClient 9 | JsonRpcSharp.HttpClient 10 | Json;RPC;HTTP;HTTPS 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | AssemblyInfo.cs 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/JsonRpcSharp.HttpClient/SimpleHttpClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net.Http; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using JsonRpcSharp.Client.RpcMessages; 8 | using Newtonsoft.Json; 9 | 10 | namespace JsonRpcSharp.Client 11 | { 12 | public class SimpleHttpClient : ClientBase 13 | { 14 | private readonly JsonSerializerSettings _jsonSerializerSettings; 15 | private readonly System.Net.Http.HttpClient _httpClient; 16 | 17 | public SimpleHttpClient(Uri baseUrl, System.Net.Http.HttpClient httpClient, 18 | JsonSerializerSettings jsonSerializerSettings = null) 19 | { 20 | if (jsonSerializerSettings == null) 21 | jsonSerializerSettings = DefaultJsonSerializerSettingsFactory.BuildDefaultJsonSerializerSettings(); 22 | _jsonSerializerSettings = jsonSerializerSettings; 23 | _httpClient = httpClient; 24 | _httpClient.BaseAddress = baseUrl; 25 | } 26 | 27 | protected override async Task SendAsync(RpcRequestMessage request, 28 | string route = null, 29 | CancellationToken cancellationToken = default(CancellationToken)) 30 | { 31 | try 32 | { 33 | var rpcRequestJson = JsonConvert.SerializeObject(request, _jsonSerializerSettings); 34 | var httpContent = new StringContent(rpcRequestJson, Encoding.UTF8, "application/json"); 35 | 36 | var effectiveToken = GetEffectiveCancellationToken(cancellationToken, ConnectionTimeout); 37 | var httpResponseMessage = await _httpClient.PostAsync(route, httpContent, effectiveToken) 38 | .ConfigureAwait(false); 39 | 40 | cancellationToken.ThrowIfCancellationRequested(); 41 | httpResponseMessage.EnsureSuccessStatusCode(); 42 | 43 | var stream = await httpResponseMessage.Content.ReadAsStreamAsync(); 44 | using (var streamReader = new StreamReader(stream)) 45 | using (var reader = new JsonTextReader(streamReader)) 46 | { 47 | var serializer = JsonSerializer.Create(_jsonSerializerSettings); 48 | var message = serializer.Deserialize(reader); 49 | 50 | return message; 51 | } 52 | } 53 | catch (TaskCanceledException ex) 54 | { 55 | throw new RpcClientTimeoutException($"Rpc timeout after {ConnectionTimeout.TotalMilliseconds} milliseconds", ex); 56 | } 57 | catch (Exception ex) 58 | { 59 | throw new RpcClientUnknownException("Error occurred when trying to send rpc requests(s)", ex); 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/JsonRpcSharp.IpcClient/IpcClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.IO.Pipes; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using JsonRpcSharp.Client; 7 | using Newtonsoft.Json; 8 | using Common.Logging; 9 | using JsonRpcSharp.Client.RpcMessages; 10 | using System.Threading; 11 | 12 | namespace JsonRpcSharp.IpcClient 13 | { 14 | public class IpcClient : IpcClientBase 15 | { 16 | private readonly object _lockingObject = new object(); 17 | private readonly ILog _log; 18 | 19 | private NamedPipeClientStream _pipeClient; 20 | 21 | 22 | public IpcClient(string ipcPath, JsonSerializerSettings jsonSerializerSettings = null, ILog log = null) : base(ipcPath, jsonSerializerSettings) 23 | { 24 | _log = log; 25 | } 26 | 27 | private NamedPipeClientStream GetPipeClient() 28 | { 29 | try 30 | { 31 | if (_pipeClient == null || !_pipeClient.IsConnected) 32 | { 33 | _pipeClient = new NamedPipeClientStream(IpcPath); 34 | _pipeClient.Connect((int)ConnectionTimeout.TotalMilliseconds); 35 | } 36 | } 37 | catch (TimeoutException ex) 38 | { 39 | throw new RpcClientTimeoutException($"Rpc timeout afer {ConnectionTimeout.TotalMilliseconds} milliseconds", ex); 40 | } 41 | catch 42 | { 43 | //Connection error we want to allow to retry. 44 | _pipeClient = null; 45 | throw; 46 | } 47 | return _pipeClient; 48 | } 49 | 50 | 51 | public int ReceiveBufferedResponse(NamedPipeClientStream client, byte[] buffer) 52 | { 53 | int bytesRead = 0; 54 | if (Task.Run(async () => 55 | bytesRead = await client.ReadAsync(buffer, 0, buffer.Length) 56 | ).Wait(ForceCompleteReadTotalMiliseconds)) 57 | { 58 | return bytesRead; 59 | } 60 | else 61 | { 62 | return bytesRead; 63 | } 64 | } 65 | 66 | public MemoryStream ReceiveFullResponse(NamedPipeClientStream client) 67 | { 68 | var readBufferSize = 512; 69 | var memoryStream = new MemoryStream(); 70 | 71 | int bytesRead = 0; 72 | byte[] buffer = new byte[readBufferSize]; 73 | bytesRead = ReceiveBufferedResponse(client, buffer); 74 | while (bytesRead > 0) 75 | { 76 | memoryStream.Write(buffer, 0, bytesRead); 77 | var lastByte = buffer[bytesRead - 1]; 78 | 79 | if (lastByte == 10) //return signalled with a line feed 80 | { 81 | bytesRead = 0; 82 | } 83 | else 84 | { 85 | bytesRead = ReceiveBufferedResponse(client, buffer); 86 | } 87 | } 88 | return memoryStream; 89 | } 90 | 91 | protected override async Task SendAsync(RpcRequestMessage request, 92 | string route = null, 93 | CancellationToken cancellationToken = default(CancellationToken)) 94 | { 95 | var logger = new RpcLogger(_log); 96 | try 97 | { 98 | lock (_lockingObject) 99 | { 100 | var rpcRequestJson = JsonConvert.SerializeObject(request, JsonSerializerSettings); 101 | var requestBytes = Encoding.UTF8.GetBytes(rpcRequestJson); 102 | logger.LogRequest(rpcRequestJson); 103 | GetPipeClient().Write(requestBytes, 0, requestBytes.Length); 104 | 105 | using (var memoryData = ReceiveFullResponse(GetPipeClient())) 106 | { 107 | memoryData.Position = 0; 108 | using (StreamReader streamReader = new StreamReader(memoryData)) 109 | using (JsonTextReader reader = new JsonTextReader(streamReader)) 110 | { 111 | var serializer = JsonSerializer.Create(JsonSerializerSettings); 112 | var message = serializer.Deserialize(reader); 113 | logger.LogResponse(message); 114 | return message; 115 | } 116 | } 117 | } 118 | } 119 | catch (Exception ex) 120 | { 121 | logger.LogException(ex); 122 | throw new RpcClientUnknownException("Error occurred when trying to send ipc request(s)", ex); 123 | } 124 | } 125 | 126 | #region IDisposable Support 127 | 128 | private bool disposedValue; 129 | 130 | protected override void Dispose(bool disposing) 131 | { 132 | if (!disposedValue) 133 | { 134 | if (disposing) 135 | if (_pipeClient != null) 136 | { 137 | _pipeClient.Close(); 138 | _pipeClient.Dispose(); 139 | } 140 | 141 | disposedValue = true; 142 | } 143 | } 144 | #endregion 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/JsonRpcSharp.IpcClient/IpcClientBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using JsonRpcSharp.Client; 4 | using Newtonsoft.Json; 5 | using System.Text; 6 | using System.IO; 7 | 8 | namespace JsonRpcSharp.IpcClient 9 | { 10 | public abstract class IpcClientBase : ClientBase, IDisposable 11 | { 12 | protected readonly string IpcPath; 13 | public static int ForceCompleteReadTotalMiliseconds { get; set; } = 2000; 14 | 15 | public IpcClientBase(string ipcPath, JsonSerializerSettings jsonSerializerSettings = null) 16 | { 17 | if (jsonSerializerSettings == null) 18 | jsonSerializerSettings = DefaultJsonSerializerSettingsFactory.BuildDefaultJsonSerializerSettings(); 19 | this.IpcPath = ipcPath; 20 | JsonSerializerSettings = jsonSerializerSettings; 21 | } 22 | 23 | public JsonSerializerSettings JsonSerializerSettings { get; set; } 24 | 25 | 26 | public string ReadJson(JsonReader reader) 27 | { 28 | var sb = new StringBuilder(); 29 | var sw = new StringWriter(sb); 30 | 31 | using (var writer = new JsonTextWriter(sw)) 32 | { 33 | if (reader.TokenType == JsonToken.StartObject) 34 | { 35 | int depth = 1; 36 | writer.WriteStartObject(); 37 | 38 | while (depth > 0) 39 | { 40 | if (!TryRead(reader)) 41 | break; 42 | switch (reader.TokenType) 43 | { 44 | case JsonToken.PropertyName: 45 | writer.WritePropertyName(reader.Value.ToString()); 46 | break; 47 | case JsonToken.StartArray: 48 | writer.WriteStartArray(); 49 | break; 50 | case JsonToken.Boolean: 51 | case JsonToken.Integer: 52 | case JsonToken.Float: 53 | case JsonToken.String: 54 | case JsonToken.Date: 55 | case JsonToken.Null: 56 | writer.WriteValue(reader.Value); 57 | break; 58 | case JsonToken.EndArray: 59 | writer.WriteEndArray(); 60 | break; 61 | case JsonToken.StartObject: 62 | writer.WriteStartObject(); 63 | depth++; 64 | break; 65 | case JsonToken.EndObject: 66 | writer.WriteEndObject(); 67 | depth--; 68 | break; 69 | } 70 | } 71 | while (depth > 0) 72 | { 73 | depth--; 74 | writer.WriteEndObject(); 75 | } 76 | } 77 | } 78 | return sb.ToString(); 79 | } 80 | 81 | public bool TryRead(JsonReader jsonReader) 82 | { 83 | if (Task.Run(() => jsonReader.Read()).Wait(1000)) 84 | { 85 | return true; 86 | } 87 | else 88 | { 89 | return false; 90 | } 91 | } 92 | 93 | #region IDisposable Support 94 | 95 | private bool disposedValue; 96 | 97 | protected virtual void Dispose(bool disposing) 98 | { 99 | } 100 | 101 | public void Dispose() 102 | { 103 | Dispose(true); 104 | } 105 | 106 | #endregion 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/JsonRpcSharp.IpcClient/JsonRpcSharp.IpcClient.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | JsonRpcSharp IpcClient for NamedPipes and UnixSockets Class Library 4 | Juan Blanco 5 | JsonRpcSharp.IpcClient 6 | $(JsonRpcSharpVersion) 7 | netstandard2.0 8 | JsonRpcSharp.IpcClient 9 | JsonRpcSharp.IpcClient 10 | Json;RPC 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | AssemblyInfo.cs 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/JsonRpcSharp.IpcClient/JsonSerializerExtensions.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace JsonRpcSharp.IpcClient 9 | { 10 | public static class JsonSerializerExtensions 11 | { 12 | public static void CopySerializerSettings(this JsonSerializer jsonSerializer, JsonSerializerSettings serializerSettings) 13 | { 14 | if (serializerSettings != null) 15 | { 16 | jsonSerializer.ConstructorHandling = serializerSettings.ConstructorHandling; 17 | jsonSerializer.CheckAdditionalContent = serializerSettings.CheckAdditionalContent; 18 | jsonSerializer.DateFormatHandling = serializerSettings.DateFormatHandling; 19 | jsonSerializer.DateFormatString = serializerSettings.DateFormatString; 20 | jsonSerializer.DateParseHandling = serializerSettings.DateParseHandling; 21 | jsonSerializer.DateTimeZoneHandling = serializerSettings.DateTimeZoneHandling; 22 | jsonSerializer.DefaultValueHandling = serializerSettings.DefaultValueHandling; 23 | jsonSerializer.EqualityComparer = serializerSettings.EqualityComparer; 24 | jsonSerializer.FloatFormatHandling = serializerSettings.FloatFormatHandling; 25 | jsonSerializer.Formatting = serializerSettings.Formatting; 26 | jsonSerializer.FloatParseHandling = serializerSettings.FloatParseHandling; 27 | jsonSerializer.MaxDepth = serializerSettings.MaxDepth; 28 | jsonSerializer.MetadataPropertyHandling = serializerSettings.MetadataPropertyHandling; 29 | jsonSerializer.MissingMemberHandling = serializerSettings.MissingMemberHandling; 30 | jsonSerializer.NullValueHandling = serializerSettings.NullValueHandling; 31 | jsonSerializer.ObjectCreationHandling = serializerSettings.ObjectCreationHandling; 32 | jsonSerializer.PreserveReferencesHandling = serializerSettings.PreserveReferencesHandling; 33 | jsonSerializer.ReferenceLoopHandling = serializerSettings.ReferenceLoopHandling; 34 | jsonSerializer.StringEscapeHandling = serializerSettings.StringEscapeHandling; 35 | jsonSerializer.TraceWriter = serializerSettings.TraceWriter; 36 | jsonSerializer.TypeNameHandling = serializerSettings.TypeNameHandling; 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/JsonRpcSharp.IpcClient/UnixDomainSocketEndPoint.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Net.Sockets; 4 | using System.Net; 5 | using System.Diagnostics; 6 | 7 | namespace JsonRpcSharp.IpcClient 8 | { 9 | // Licensed to the .NET Foundation under one or more agreements. 10 | // The .NET Foundation licenses this file to you under the MIT license. 11 | // See https://github.com/dotnet/corefx/blob/master/LICENSE.TXT 12 | 13 | //This is part of Dotnet Core, waiting to be released externally. NamedPipesStream uses it but it does not allow for paths. 14 | 15 | public sealed class UnixDomainSocketEndPoint : EndPoint 16 | { 17 | private const AddressFamily EndPointAddressFamily = AddressFamily.Unix; 18 | 19 | private static readonly Encoding s_pathEncoding = Encoding.UTF8; 20 | private static readonly int s_nativePathOffset = 2; // = offsetof(struct sockaddr_un, sun_path). It's the same on Linux and OSX 21 | private static readonly int s_nativePathLength = 91; // sockaddr_un.sun_path at http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_un.h.html, -1 for terminator 22 | private static readonly int s_nativeAddressSize = s_nativePathOffset + s_nativePathLength; 23 | 24 | private readonly string _path; 25 | private readonly byte[] _encodedPath; 26 | 27 | public UnixDomainSocketEndPoint(string path) 28 | { 29 | if (path == null) 30 | { 31 | throw new ArgumentNullException(nameof(path)); 32 | } 33 | 34 | _path = path; 35 | _encodedPath = s_pathEncoding.GetBytes(_path); 36 | 37 | if (path.Length == 0 || _encodedPath.Length > s_nativePathLength) 38 | { 39 | throw new ArgumentOutOfRangeException(nameof(path)); 40 | } 41 | } 42 | 43 | internal UnixDomainSocketEndPoint(SocketAddress socketAddress) 44 | { 45 | if (socketAddress == null) 46 | { 47 | throw new ArgumentNullException(nameof(socketAddress)); 48 | } 49 | 50 | if (socketAddress.Family != EndPointAddressFamily || 51 | socketAddress.Size > s_nativeAddressSize) 52 | { 53 | throw new ArgumentOutOfRangeException(nameof(socketAddress)); 54 | } 55 | 56 | if (socketAddress.Size > s_nativePathOffset) 57 | { 58 | _encodedPath = new byte[socketAddress.Size - s_nativePathOffset]; 59 | for (int i = 0; i < _encodedPath.Length; i++) 60 | { 61 | _encodedPath[i] = socketAddress[s_nativePathOffset + i]; 62 | } 63 | 64 | _path = s_pathEncoding.GetString(_encodedPath, 0, _encodedPath.Length); 65 | } 66 | else 67 | { 68 | _encodedPath = new byte[0]; 69 | _path = string.Empty; 70 | } 71 | } 72 | 73 | public override SocketAddress Serialize() 74 | { 75 | var result = new SocketAddress(AddressFamily.Unix, s_nativeAddressSize); 76 | Debug.Assert(_encodedPath.Length + s_nativePathOffset <= result.Size, "Expected path to fit in address"); 77 | 78 | for (int index = 0; index < _encodedPath.Length; index++) 79 | { 80 | result[s_nativePathOffset + index] = _encodedPath[index]; 81 | } 82 | result[s_nativePathOffset + _encodedPath.Length] = 0; // path must be null-terminated 83 | 84 | return result; 85 | } 86 | 87 | public override EndPoint Create(SocketAddress socketAddress) => new UnixDomainSocketEndPoint(socketAddress); 88 | 89 | public override AddressFamily AddressFamily => EndPointAddressFamily; 90 | 91 | public override string ToString() => _path; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/JsonRpcSharp.IpcClient/UnixIpcClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | using Newtonsoft.Json; 6 | using System.Net.Sockets; 7 | using Common.Logging; 8 | using JsonRpcSharp.Client; 9 | using JsonRpcSharp.Client.RpcMessages; 10 | using System.Threading; 11 | 12 | namespace JsonRpcSharp.IpcClient 13 | { 14 | public class UnixIpcClient : IpcClientBase 15 | { 16 | private readonly object _lockingObject = new object(); 17 | private readonly ILog _log; 18 | public UnixIpcClient(string ipcPath, JsonSerializerSettings jsonSerializerSettings = null, ILog log = null) : base(ipcPath, jsonSerializerSettings) 19 | { 20 | _log = log; 21 | } 22 | 23 | private Socket _socket; 24 | 25 | private Socket GetSocket() 26 | { 27 | try 28 | { 29 | if (_socket == null || !_socket.Connected) 30 | { 31 | var endPoint = new UnixDomainSocketEndPoint(IpcPath); 32 | _socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified); 33 | if (!Task.Run(() => 34 | _socket.Connect(endPoint)).Wait(ConnectionTimeout)) 35 | { 36 | throw new RpcClientTimeoutException($"Rpc timeout afer {ConnectionTimeout.TotalMilliseconds} milliseconds"); 37 | } 38 | } 39 | } 40 | catch 41 | { 42 | //Connection error we want to allow to retry. 43 | _socket = null; 44 | throw; 45 | } 46 | return _socket; 47 | } 48 | 49 | public int ReceiveBufferedResponse(Socket client, byte[] buffer) 50 | { 51 | int bytesRead = 0; 52 | if (Task.Run(async () => 53 | bytesRead = await client.ReceiveAsync(new ArraySegment(buffer), SocketFlags.None) 54 | ).Wait(ForceCompleteReadTotalMiliseconds)) 55 | { 56 | return bytesRead; 57 | } 58 | else 59 | { 60 | return bytesRead; 61 | } 62 | } 63 | 64 | public MemoryStream ReceiveFullResponse(Socket client) 65 | { 66 | var readBufferSize = 512; 67 | var memoryStream = new MemoryStream(); 68 | 69 | int bytesRead = 0; 70 | byte[] buffer = new byte[readBufferSize]; 71 | bytesRead = ReceiveBufferedResponse(client, buffer); 72 | while (bytesRead > 0) 73 | { 74 | memoryStream.Write(buffer, 0, bytesRead); 75 | var lastByte = buffer[bytesRead - 1]; 76 | 77 | if (lastByte == 10) //return signalled with a line feed 78 | { 79 | bytesRead = 0; 80 | } 81 | else 82 | { 83 | bytesRead = ReceiveBufferedResponse(client, buffer); 84 | } 85 | } 86 | return memoryStream; 87 | } 88 | 89 | protected override async Task SendAsync(RpcRequestMessage request, 90 | string route = null, 91 | CancellationToken cancellationToken = default(CancellationToken)) 92 | { 93 | var logger = new RpcLogger(_log); 94 | try 95 | { 96 | lock (_lockingObject) 97 | { 98 | var rpcRequestJson = JsonConvert.SerializeObject(request, JsonSerializerSettings); 99 | var requestBytes = Encoding.UTF8.GetBytes(rpcRequestJson); 100 | logger.LogRequest(rpcRequestJson); 101 | var client = GetSocket(); 102 | client.SendBufferSize = requestBytes.Length; 103 | var val = client.SendAsync(new ArraySegment(requestBytes, 0, requestBytes.Length), SocketFlags.None).Result; 104 | using (var memoryStream = ReceiveFullResponse(client)) 105 | { 106 | memoryStream.Position = 0; 107 | using (var streamReader = new StreamReader(memoryStream)) 108 | using (var reader = new JsonTextReader(streamReader)) 109 | { 110 | var serializer = JsonSerializer.Create(JsonSerializerSettings); 111 | var message = serializer.Deserialize(reader); 112 | logger.LogResponse(message); 113 | return message; 114 | } 115 | } 116 | } 117 | 118 | } catch (Exception ex) { 119 | logger.LogException(ex); 120 | throw new RpcClientUnknownException("Error occurred when trying to send ipc requests(s)", ex); 121 | } 122 | } 123 | 124 | #region IDisposable Support 125 | 126 | private bool disposedValue; 127 | 128 | protected override void Dispose(bool disposing) 129 | { 130 | if (!disposedValue) 131 | { 132 | disposedValue = true; 133 | } 134 | } 135 | 136 | #endregion 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/JsonRpcSharp.TcpClient/JsonRpcSharp.TcpClient.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | netstandard 6 | 7 | 8 | 9 | 10 | AssemblyInfo.fs 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/JsonRpcSharp.TcpClient/TcpClient.fs: -------------------------------------------------------------------------------- 1 | namespace JsonRpcSharp.TcpClient 2 | 3 | open System 4 | open System.Buffers 5 | open System.Net 6 | open System.Net.Sockets 7 | open System.IO.Pipelines 8 | open System.Text 9 | open System.Threading.Tasks 10 | 11 | type CommunicationUnsuccessfulException(msg: string, innerException: Exception) = 12 | inherit Exception(msg, innerException) 13 | 14 | type TimeoutOrResult<'T> = 15 | | Timeout 16 | | Result of 'T 17 | 18 | type JsonRpcClient(resolveHostAsync: unit->Async, port, timeout: TimeSpan) = 19 | let minimumBufferSize = 1024 20 | 21 | let withTimeout (timeout: TimeSpan) (job: Async<_>) = async { 22 | let read = async { 23 | let! value = job 24 | return value |> Result |> Some 25 | } 26 | 27 | let delay = async { 28 | do! Async.Sleep (int timeout.TotalMilliseconds) 29 | return Some Timeout 30 | } 31 | 32 | let! result = Async.Choice([read; delay]) 33 | match result with 34 | | Some x -> return x 35 | | None -> return Timeout 36 | } 37 | 38 | let unwrapTimeout timeoutMsg job = async { 39 | let! maybeRes = job 40 | match maybeRes with 41 | | Timeout -> 42 | let timeoutEx = TimeoutException(timeoutMsg) 43 | return raise <| CommunicationUnsuccessfulException(timeoutMsg, timeoutEx) 44 | | Result res -> 45 | return res 46 | } 47 | 48 | let rec writeToPipeAsync (writer: PipeWriter) (socket: Socket) = async { 49 | try 50 | let segment = Array.zeroCreate minimumBufferSize |> ArraySegment 51 | let! read = socket.ReceiveAsync(segment, SocketFlags.None) 52 | |> Async.AwaitTask |> withTimeout timeout |> unwrapTimeout "Socket read timed out" 53 | 54 | match read with 55 | | 0 -> 56 | return writer.Complete() 57 | | bytesRead -> 58 | segment.Array.CopyTo(writer.GetMemory(bytesRead)) 59 | writer.Advance bytesRead 60 | let! cancelToken = Async.CancellationToken 61 | let! flusher = (writer.FlushAsync cancelToken).AsTask() |> Async.AwaitTask 62 | if flusher.IsCompleted then 63 | return writer.Complete() 64 | else 65 | cancelToken.ThrowIfCancellationRequested() 66 | return! writeToPipeAsync writer socket 67 | with 68 | | ex -> return writer.Complete(ex) 69 | } 70 | 71 | let rec readFromPipeAsync (reader: PipeReader) (state: StringBuilder * int) = async { 72 | let! cancelToken = Async.CancellationToken 73 | let! result = (reader.ReadAsync cancelToken).AsTask() |> Async.AwaitTask 74 | 75 | let mutable buffer = result.Buffer 76 | let sb = fst state 77 | 78 | let str = BuffersExtensions.ToArray(& buffer) |> Encoding.UTF8.GetString 79 | str |> sb.Append |> ignore 80 | let bracesCount = str |> Seq.sumBy (function | '{' -> 1 | '}' -> -1 | _ -> 0) 81 | 82 | reader.AdvanceTo(buffer.End) 83 | 84 | let braces = (snd state) + bracesCount 85 | if result.IsCompleted || braces = 0 then 86 | return sb.ToString() 87 | else 88 | cancelToken.ThrowIfCancellationRequested() 89 | return! readFromPipeAsync reader (sb, braces) 90 | } 91 | 92 | let RequestImplAsync (json: string) = 93 | async { 94 | let! endpoint = resolveHostAsync() |> withTimeout timeout |> unwrapTimeout "Name resolution timed out" 95 | 96 | let! cancelToken = Async.CancellationToken 97 | cancelToken.ThrowIfCancellationRequested() 98 | 99 | use socket = new Socket(SocketType.Stream, ProtocolType.Tcp) 100 | let! connect = socket.ConnectAsync(endpoint, port) 101 | |> Async.AwaitTask |> withTimeout timeout |> unwrapTimeout "Socket connect timed out" 102 | 103 | cancelToken.ThrowIfCancellationRequested() 104 | 105 | let segment = UTF8Encoding.UTF8.GetBytes(json + Environment.NewLine) |> ArraySegment 106 | let! send = socket.SendAsync(segment, SocketFlags.None) 107 | |> Async.AwaitTask |> withTimeout timeout |> unwrapTimeout "Socket send timed out" 108 | 109 | cancelToken.ThrowIfCancellationRequested() 110 | 111 | let pipe = Pipe() 112 | let! _ = writeToPipeAsync pipe.Writer socket |> Async.StartChild 113 | return! readFromPipeAsync pipe.Reader (StringBuilder(), 0) 114 | } 115 | 116 | abstract member RequestAsync: string -> Async 117 | abstract member RequestAsyncAsTask: string -> Task 118 | 119 | default __.RequestAsync (json: string) = 120 | async { 121 | try 122 | return! RequestImplAsync json 123 | with 124 | | :? AggregateException as ae when ae.Flatten().InnerExceptions 125 | |> Seq.exists (fun x -> x :? SocketException || 126 | x :? TimeoutException || 127 | x :? CommunicationUnsuccessfulException) -> 128 | return raise <| CommunicationUnsuccessfulException(ae.Message, ae) 129 | | :? SocketException as ex -> 130 | return raise <| CommunicationUnsuccessfulException(ex.Message, ex) 131 | } 132 | 133 | default self.RequestAsyncAsTask (json: string) = 134 | self.RequestAsync json |> Async.StartAsTask 135 | -------------------------------------------------------------------------------- /src/JsonRpcSharp.WebSocketClient/JsonRpcSharp.WebSocketClient.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | JsonRpcSharp WebSocketClient 4 | Juan Blanco 5 | JsonRpcSharp.WebSocketClient 6 | $(JsonRpcSharpVersion) 7 | netstandard2.0 8 | JsonRpcSharp.WebSocketClient 9 | JsonRpcSharp.WebSocketClient 10 | Json;RPC 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | AssemblyInfo.cs 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/JsonRpcSharp.WebSocketClient/WebSocketClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net.WebSockets; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using Common.Logging; 8 | using JsonRpcSharp.Client; 9 | using JsonRpcSharp.Client.RpcMessages; 10 | using Newtonsoft.Json; 11 | 12 | namespace JsonRpcSharp.WebSocketClient 13 | { 14 | public class WebSocketClient : ClientBase, IDisposable 15 | { 16 | private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1); 17 | protected readonly string Path; 18 | public static TimeSpan ForceCompleteReadTimeout { get; set; } = TimeSpan.FromMilliseconds(2000); 19 | 20 | private WebSocketClient(string path, JsonSerializerSettings jsonSerializerSettings = null) 21 | { 22 | if (jsonSerializerSettings == null) 23 | jsonSerializerSettings = DefaultJsonSerializerSettingsFactory.BuildDefaultJsonSerializerSettings(); 24 | this.Path = path; 25 | JsonSerializerSettings = jsonSerializerSettings; 26 | } 27 | 28 | public JsonSerializerSettings JsonSerializerSettings { get; set; } 29 | private readonly object _lockingObject = new object(); 30 | private readonly ILog _log; 31 | 32 | private ClientWebSocket _clientWebSocket; 33 | 34 | 35 | public WebSocketClient(string path, JsonSerializerSettings jsonSerializerSettings = null, ILog log = null) : this(path, jsonSerializerSettings) 36 | { 37 | _log = log; 38 | } 39 | 40 | private async Task GetClientWebSocketAsync(CancellationToken cancellationToken) 41 | { 42 | try 43 | { 44 | if (_clientWebSocket == null || _clientWebSocket.State != WebSocketState.Open) 45 | { 46 | _clientWebSocket = new ClientWebSocket(); 47 | await _clientWebSocket.ConnectAsync(new Uri(Path), cancellationToken).ConfigureAwait(false); 48 | 49 | } 50 | } 51 | catch (TaskCanceledException ex) 52 | { 53 | throw new RpcClientTimeoutException($"Rpc timeout after {ConnectionTimeout.TotalMilliseconds} milliseconds", ex); 54 | } 55 | catch 56 | { 57 | //Connection error we want to allow to retry. 58 | _clientWebSocket = null; 59 | throw; 60 | } 61 | return _clientWebSocket; 62 | } 63 | 64 | 65 | public async Task ReceiveBufferedResponseAsync(ClientWebSocket client, 66 | byte[] buffer, 67 | CancellationToken cancellationToken) 68 | { 69 | try 70 | { 71 | var effectiveCancellationToken = GetEffectiveCancellationToken(cancellationToken, ForceCompleteReadTimeout); 72 | var segmentBuffer = new ArraySegment(buffer); 73 | var result = await client 74 | .ReceiveAsync(segmentBuffer, effectiveCancellationToken) 75 | .ConfigureAwait(false); 76 | return result.Count; 77 | } 78 | catch (TaskCanceledException ex) 79 | { 80 | throw new RpcClientTimeoutException($"Rpc timeout after {ConnectionTimeout.TotalMilliseconds} milliseconds", ex); 81 | } 82 | } 83 | 84 | public async Task ReceiveFullResponseAsync(ClientWebSocket client, CancellationToken cancellationToken) 85 | { 86 | var readBufferSize = 512; 87 | var memoryStream = new MemoryStream(); 88 | 89 | int bytesRead = 0; 90 | byte[] buffer = new byte[readBufferSize]; 91 | bytesRead = await ReceiveBufferedResponseAsync(client, buffer, cancellationToken).ConfigureAwait(false); 92 | while (bytesRead > 0) 93 | { 94 | memoryStream.Write(buffer, 0, bytesRead); 95 | var lastByte = buffer[bytesRead - 1]; 96 | 97 | if (lastByte == 10) //return signalled with a line feed 98 | { 99 | bytesRead = 0; 100 | } 101 | else 102 | { 103 | bytesRead = await ReceiveBufferedResponseAsync(client, buffer, cancellationToken).ConfigureAwait(false); 104 | } 105 | } 106 | return memoryStream; 107 | } 108 | 109 | protected override async Task SendAsync(RpcRequestMessage request, 110 | string route = null, 111 | CancellationToken cancellationToken = default(CancellationToken)) 112 | { 113 | var logger = new RpcLogger(_log); 114 | try 115 | { 116 | var effectiveCancellationToken = GetEffectiveCancellationToken(cancellationToken, ConnectionTimeout); 117 | await semaphoreSlim.WaitAsync(effectiveCancellationToken).ConfigureAwait(false); 118 | var rpcRequestJson = JsonConvert.SerializeObject(request, JsonSerializerSettings); 119 | var requestBytes = new ArraySegment(Encoding.UTF8.GetBytes(rpcRequestJson)); 120 | logger.LogRequest(rpcRequestJson); 121 | 122 | var webSocket = await GetClientWebSocketAsync(effectiveCancellationToken).ConfigureAwait(false); 123 | await webSocket.SendAsync(requestBytes, WebSocketMessageType.Text, true, effectiveCancellationToken) 124 | .ConfigureAwait(false); 125 | 126 | using (var memoryData = await ReceiveFullResponseAsync(webSocket, cancellationToken).ConfigureAwait(false)) 127 | { 128 | memoryData.Position = 0; 129 | using (var streamReader = new StreamReader(memoryData)) 130 | using (var reader = new JsonTextReader(streamReader)) 131 | { 132 | var serializer = JsonSerializer.Create(JsonSerializerSettings); 133 | var message = serializer.Deserialize(reader); 134 | logger.LogResponse(message); 135 | return message; 136 | } 137 | } 138 | } 139 | catch (Exception ex) 140 | { 141 | logger.LogException(ex); 142 | throw new RpcClientUnknownException("Error occurred trying to send web socket requests(s)", ex); 143 | } 144 | finally 145 | { 146 | semaphoreSlim.Release(); 147 | } 148 | } 149 | 150 | public void Dispose() 151 | { 152 | _clientWebSocket?.Dispose(); 153 | } 154 | } 155 | } 156 | 157 | --------------------------------------------------------------------------------