├── .azuredevops └── dependabot.yml ├── .config └── dotnet-tools.json ├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .editorconfig ├── .gitattributes ├── .github ├── .editorconfig ├── actions │ └── publish-artifacts │ │ └── action.yaml ├── renovate.json └── workflows │ ├── docs.yml │ ├── docs_validate.yml │ └── libtemplate-update.yml ├── .gitignore ├── .prettierrc.yaml ├── .vscode ├── extensions.json ├── settings.json └── tasks.json ├── BannedSymbols.txt ├── CONTRIBUTING.md ├── Directory.Build.props ├── Directory.Build.rsp ├── Directory.Build.targets ├── Directory.Packages.props ├── LICENSE ├── README.md ├── SECURITY.md ├── StreamJsonRpc.sln ├── azure-pipelines.yml ├── azure-pipelines ├── Archive-SourceCode.ps1 ├── BuildStageVariables.yml ├── CredScanSuppressions.json ├── Get-InsertionPRId.ps1 ├── GlobalVariables.yml ├── Install-NuGetPackage.ps1 ├── Merge-CodeCoverage.ps1 ├── NuGetSbom.props ├── OptProf.yml ├── OptProf_part2.yml ├── PoliCheckExclusions.xml ├── PostPRMessage.ps1 ├── TSAOptions.json ├── WIFtoPATauth.yml ├── apiscan.yml ├── archive-sourcecode.yml ├── build.yml ├── dotnet.yml ├── falsepositives.gdnsuppress ├── install-dependencies.yml ├── libtemplate-update.yml ├── microbuild.after.yml ├── microbuild.before.yml ├── no_authenticode.txt ├── no_strongname.txt ├── official.yml ├── prepare-insertion-stages.yml ├── publish-codecoverage.yml ├── publish-symbols.yml ├── publish_artifacts.ps1 ├── release-deployment-prep.yml ├── release.yml ├── schedule-only-steps.yml ├── unofficial.yml ├── vs-insertion.yml └── vs-validation.yml ├── azurepipelines-coverage.yml ├── docfx ├── .gitignore ├── docfx.json ├── docs │ ├── connecting.md │ ├── disconnecting.md │ ├── dynamicproxy.md │ ├── exceptions.md │ ├── extensibility.md │ ├── getting-started.md │ ├── joinableTaskFactory.md │ ├── recvrequest.md │ ├── remotetargets.md │ ├── resiliency.md │ ├── security.md │ ├── sendrequest.md │ ├── testing.md │ ├── toc.yml │ ├── tracecontext.md │ ├── troubleshooting.md │ └── vs.md ├── exotic_types │ ├── asyncenumerable.md │ ├── disposable.md │ ├── general_marshaled_objects.md │ ├── general_marshaled_objects_2.md │ ├── index.md │ ├── observer.md │ ├── oob_streams.md │ ├── progresssupport.md │ ├── rpc_marshalable_objects.md │ └── toc.yml ├── index.md └── toc.yml ├── global.json ├── init.cmd ├── init.ps1 ├── loc └── lcl │ ├── CHS │ └── StreamJsonRpc.dll.lcl │ ├── CHT │ └── StreamJsonRpc.dll.lcl │ ├── CSY │ └── StreamJsonRpc.dll.lcl │ ├── DEU │ └── StreamJsonRpc.dll.lcl │ ├── ESN │ └── StreamJsonRpc.dll.lcl │ ├── FRA │ └── StreamJsonRpc.dll.lcl │ ├── ITA │ └── StreamJsonRpc.dll.lcl │ ├── JPN │ └── StreamJsonRpc.dll.lcl │ ├── KOR │ └── StreamJsonRpc.dll.lcl │ ├── PLK │ └── StreamJsonRpc.dll.lcl │ ├── PTB │ └── StreamJsonRpc.dll.lcl │ ├── RUS │ └── StreamJsonRpc.dll.lcl │ └── TRK │ └── StreamJsonRpc.dll.lcl ├── nuget.config ├── settings.VisualStudio.json ├── src ├── .editorconfig ├── AssemblyInfo.cs ├── AssemblyInfo.vb ├── Directory.Build.props ├── Directory.Build.targets ├── OptProf.targets ├── PackageIcon.png ├── StreamJsonRpc │ ├── ActivityTracingStrategy.cs │ ├── AwaitExtensions.cs │ ├── CommonMethodNameTransforms.cs │ ├── CorrelationManagerTracingStrategy.cs │ ├── DisposableAction.cs │ ├── EventArgs │ │ ├── DisconnectedReason.cs │ │ └── JsonRpcDisconnectedEventArgs.cs │ ├── ExceptionProcessing.cs │ ├── ExceptionSettings.cs │ ├── Exceptions │ │ ├── BadRpcHeaderException.cs │ │ ├── ConnectionLostException.cs │ │ ├── LocalRpcException.cs │ │ ├── RemoteInvocationException.cs │ │ ├── RemoteMethodNotFoundException.cs │ │ ├── RemoteRpcException.cs │ │ ├── RemoteSerializationException.cs │ │ ├── RpcArgumentDeserializationException.cs │ │ ├── UnexpectedEmptyEnumerableResponseException.cs │ │ └── UnrecognizedJsonRpcMessageException.cs │ ├── FormatterBase.cs │ ├── HeaderDelimitedMessageHandler.cs │ ├── Hex.cs │ ├── IActivityTracingStrategy.cs │ ├── ICancellationStrategy.cs │ ├── IJsonRpcAsyncMessageFormatter.cs │ ├── IJsonRpcAsyncMessageTextFormatter.cs │ ├── IJsonRpcClientProxy.cs │ ├── IJsonRpcClientProxyInternal.cs │ ├── IJsonRpcInstanceContainer.cs │ ├── IJsonRpcMessageFactory.cs │ ├── IJsonRpcMessageFormatter.cs │ ├── IJsonRpcMessageHandler.cs │ ├── IJsonRpcMessageTextFormatter.cs │ ├── IRpcMarshaledContext.cs │ ├── JsonMessageFormatter.cs │ ├── JsonRpc.cs │ ├── JsonRpcEnumerableSettings.cs │ ├── JsonRpcEventSource.cs │ ├── JsonRpcExtensions.cs │ ├── JsonRpcProxyOptions.cs │ ├── JsonRpcTargetOptions.cs │ ├── LengthHeaderMessageHandler.cs │ ├── MessageHandlerBase.cs │ ├── MessagePackFormatter.cs │ ├── NewLineDelimitedMessageHandler.cs │ ├── OptProf.targets │ ├── PipeMessageHandler.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Protocol │ │ ├── CommonErrorData.cs │ │ ├── Constants.cs │ │ ├── IJsonRpcMessageWithId.cs │ │ ├── JsonRpcError.cs │ │ ├── JsonRpcErrorCode.cs │ │ ├── JsonRpcMessage.cs │ │ ├── JsonRpcRequest.cs │ │ ├── JsonRpcResult.cs │ │ └── TraceParent.cs │ ├── ProxyGeneration.cs │ ├── README.md │ ├── Reflection │ │ ├── CodeGenHelpers.cs │ │ ├── ExceptionSerializationHelpers.cs │ │ ├── IJsonRpcFormatterCallbacks.cs │ │ ├── IJsonRpcFormatterState.cs │ │ ├── IJsonRpcFormatterTracingCallbacks.cs │ │ ├── IJsonRpcMessageBufferManager.cs │ │ ├── IJsonRpcTracingCallbacks.cs │ │ ├── JsonRpcIgnoreAttribute.cs │ │ ├── JsonRpcMessageEventArgs.cs │ │ ├── JsonRpcMethodAttribute.cs │ │ ├── JsonRpcResponseEventArgs.cs │ │ ├── MessageFormatterDuplexPipeTracker.cs │ │ ├── MessageFormatterEnumerableTracker.cs │ │ ├── MessageFormatterProgressTracker.cs │ │ ├── MessageFormatterRpcMarshaledContextTracker.cs │ │ ├── MethodSignature.cs │ │ ├── MethodSignatureAndTarget.cs │ │ ├── RpcMarshalableAttribute.cs │ │ ├── RpcMarshalableOptionalInterfaceAttribute.cs │ │ ├── RpcTargetInfo.cs │ │ ├── TargetMethod.cs │ │ └── TrackerHelpers.cs │ ├── RequestId.cs │ ├── RequestIdJsonConverter.cs │ ├── Resources.resx │ ├── RpcMarshaledContext.cs │ ├── SharedUtilities.cs │ ├── SkipClrVisibilityChecks.cs │ ├── StandardCancellationStrategy.cs │ ├── StreamJsonRpc.csproj │ ├── StreamMessageHandler.cs │ ├── SystemTextJsonFormatter.cs │ ├── Utilities.cs │ ├── WebSocketMessageHandler.cs │ ├── net8.0 │ │ ├── PublicAPI.Shipped.txt │ │ └── PublicAPI.Unshipped.txt │ ├── netstandard2.0 │ │ ├── PublicAPI.Shipped.txt │ │ └── PublicAPI.Unshipped.txt │ └── netstandard2.1 │ │ ├── PublicAPI.Shipped.txt │ │ └── PublicAPI.Unshipped.txt └── VSInsertionMetadata │ ├── ProfilingInputs.props │ ├── StreamJsonRpc.VSInsertionMetadata.proj │ └── VSInsertionMetadata.targets ├── stylecop.json ├── test ├── .editorconfig ├── Benchmarks │ ├── .gitignore │ ├── Benchmarks.csproj │ ├── InvokeBenchmarks.cs │ ├── NotifyBenchmarks.cs │ ├── Program.cs │ ├── ShortLivedConnectionBenchmarks.cs │ └── run.cmd ├── Directory.Build.props ├── Directory.Build.targets ├── StreamJsonRpc.Tests.ExternalAssembly │ ├── AssemblyInfo.cs │ ├── IInternalGenericInterface.cs │ ├── IPublicNestedInInternalInterface.cs │ ├── ISomeInternalProxyInterface.cs │ ├── InternalStruct.cs │ ├── SomeOtherInternalType.cs │ └── StreamJsonRpc.Tests.ExternalAssembly.csproj └── StreamJsonRpc.Tests │ ├── .editorconfig │ ├── ActivityTracingStrategyTests.cs │ ├── AssemblyLoadTests.cs │ ├── AsyncEnumerableJsonTests.cs │ ├── AsyncEnumerableJsonTypeHandlingTests.cs │ ├── AsyncEnumerableMessagePackTests.cs │ ├── AsyncEnumerableSystemTextJsonTests.cs │ ├── AsyncEnumerableTests.cs │ ├── CollectingTraceListener.cs │ ├── CommonErrorDataTests.cs │ ├── CommonMethodNameTransformsTests.cs │ ├── CorrelationManagerTracingStrategyTests.cs │ ├── CustomCancellationStrategyJsonTypeHandlingTests.cs │ ├── CustomCancellationStrategyTests.cs │ ├── DirectMessageHandler.cs │ ├── DisposableProxyJsonTests.cs │ ├── DisposableProxyMessagePackTests.cs │ ├── DisposableProxySystemTextJsonTests.cs │ ├── DisposableProxyTests.cs │ ├── DuplexPipeMarshalingJsonTests.cs │ ├── DuplexPipeMarshalingMessagePackTests.cs │ ├── DuplexPipeMarshalingSystemTextJsonTests.cs │ ├── DuplexPipeMarshalingTests.cs │ ├── ExceptionSettingsTests.cs │ ├── Exceptions │ ├── RemoteInvocationExceptionTests.cs │ └── RpcArgumentDeserializationExceptionTests.cs │ ├── FormatterTestBase.cs │ ├── FullDuplexStream.cs │ ├── HeaderDelimitedMessageHandlerTests.cs │ ├── InteropTestBase.cs │ ├── JsonContractResolverTests.cs │ ├── JsonMessageFormatterTests.cs │ ├── JsonRpcClient10InteropTests.cs │ ├── JsonRpcClient20InteropJsonTypeHandlingTests.cs │ ├── JsonRpcClient20InteropTests.cs │ ├── JsonRpcCustomRequestIdTests.cs │ ├── JsonRpcDelegatedDispatchAndSendTests.cs │ ├── JsonRpcEnumerableSettingsTests.cs │ ├── JsonRpcExtensionsTests.cs │ ├── JsonRpcJsonHeadersTests.cs │ ├── JsonRpcJsonHeadersTypeHandlingTests.cs │ ├── JsonRpcMessagePackLengthTests.cs │ ├── JsonRpcMethodAttributeTests.cs │ ├── JsonRpcProxyGenerationTests.cs │ ├── JsonRpcRawStreamTests.cs │ ├── JsonRpcRemoteTargetJsonMessageFormatterTests.cs │ ├── JsonRpcRemoteTargetMessagePackFormatterTests.cs │ ├── JsonRpcRemoteTargetSystemTextJsonFormatterTests.cs │ ├── JsonRpcRemoteTargetTests.cs │ ├── JsonRpcServerInteropTests.cs │ ├── JsonRpcSystemTextJsonHeadersTests.cs │ ├── JsonRpcTargetOptionsTests.cs │ ├── JsonRpcTests.cs │ ├── JsonRpcWithFatalExceptionsTests.cs │ ├── JsonRpcWithMessageFactoryTests.cs │ ├── LengthHeaderMessageHandlerTests.cs │ ├── LocalRpcExceptionTests.cs │ ├── MarshalableProxyJsonTests.cs │ ├── MarshalableProxyMessagePackTests.cs │ ├── MarshalableProxySystemTextJsonTests.cs │ ├── MarshalableProxyTests.cs │ ├── MessageHeaderTests.cs │ ├── MessagePackFormatterTests.cs │ ├── NewLineDelimitedMessageHandlerTests.cs │ ├── ObserverMarshalingJsonTests.cs │ ├── ObserverMarshalingMessagePackTests.cs │ ├── ObserverMarshalingSystemTextJsonTests.cs │ ├── ObserverMarshalingTests.cs │ ├── PerfTests.cs │ ├── Protocol │ └── JsonRpcRequestTests.cs │ ├── RequestIdTests.cs │ ├── RpcOrderPreservingSynchronizationContext.cs │ ├── Samples │ └── HttpClientMessageHandler.cs │ ├── SpecialCaseTests.cs │ ├── StreamJsonRpc.Tests.csproj │ ├── StreamMessageHandlerTests.cs │ ├── SystemTextJsonFormatterTests.cs │ ├── TargetObjectEventsJsonTests.cs │ ├── TargetObjectEventsMessagePackTests.cs │ ├── TargetObjectEventsSystemTextJsonTests.cs │ ├── TargetObjectEventsTests.cs │ ├── TestBase.cs │ ├── Usings.cs │ ├── WebSocketMessageHandlerJsonTests.cs │ ├── WebSocketMessageHandlerMessagePackTests.cs │ ├── WebSocketMessageHandlerSystemTextJsonTests.cs │ ├── WebSocketMessageHandlerTests.cs │ ├── WrappedStream.cs │ ├── XunitTraceListener.cs │ └── xunit.runner.json ├── tools ├── Check-DotNetRuntime.ps1 ├── Check-DotNetSdk.ps1 ├── Convert-PDB.ps1 ├── Get-ArtifactsStagingDirectory.ps1 ├── Get-CodeCovTool.ps1 ├── Get-LibTemplateBasis.ps1 ├── Get-NuGetTool.ps1 ├── Get-ProcDump.ps1 ├── Get-SymbolFiles.ps1 ├── Get-TempToolsPath.ps1 ├── Install-DotNetSdk.ps1 ├── Install-NuGetCredProvider.ps1 ├── MergeFrom-Template.ps1 ├── Prepare-Legacy-Symbols.ps1 ├── Set-EnvVars.ps1 ├── artifacts │ ├── APIScanInputs.ps1 │ ├── LocBin.ps1 │ ├── VSInsertion.ps1 │ ├── Variables.ps1 │ ├── _all.ps1 │ ├── _stage_all.ps1 │ ├── build_logs.ps1 │ ├── coverageResults.ps1 │ ├── deployables.ps1 │ ├── projectAssetsJson.ps1 │ ├── symbols.ps1 │ ├── testResults.ps1 │ └── test_symbols.ps1 ├── dotnet-test-cloud.ps1 ├── publish-CodeCov.ps1 ├── test.runsettings └── variables │ ├── BusinessGroupName.ps1 │ ├── DotNetSdkVersion.ps1 │ ├── FailsOnMonoFilter.ps1 │ ├── InsertJsonValues.ps1 │ ├── InsertPropsValues.ps1 │ ├── InsertTargetBranch.ps1 │ ├── InsertVersionsValues.ps1 │ ├── LocLanguages.ps1 │ ├── ProfilingInputsDropName.ps1 │ ├── ShouldSkipOptimize.ps1 │ ├── SymbolsFeatureName.ps1 │ ├── VstsDropNames.ps1 │ ├── _all.ps1 │ └── _define.ps1 └── version.json /.azuredevops/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Please see the documentation for all configuration options: 2 | # https://eng.ms/docs/products/dependabot/configuration/version_updates 3 | 4 | version: 2 5 | updates: 6 | - package-ecosystem: nuget 7 | directory: / 8 | schedule: 9 | interval: monthly 10 | -------------------------------------------------------------------------------- /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "powershell": { 6 | "version": "7.5.0", 7 | "commands": [ 8 | "pwsh" 9 | ], 10 | "rollForward": false 11 | }, 12 | "dotnet-coverage": { 13 | "version": "17.14.2", 14 | "commands": [ 15 | "dotnet-coverage" 16 | ], 17 | "rollForward": false 18 | }, 19 | "nbgv": { 20 | "version": "3.7.115", 21 | "commands": [ 22 | "nbgv" 23 | ], 24 | "rollForward": false 25 | }, 26 | "docfx": { 27 | "version": "2.78.3", 28 | "commands": [ 29 | "docfx" 30 | ], 31 | "rollForward": false 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # Refer to https://hub.docker.com/_/microsoft-dotnet-sdk for available versions 2 | FROM mcr.microsoft.com/dotnet/sdk:9.0.203-noble@sha256:2c9a4956a61fc45d9111fb36ec9fb86932e4842af9eb9bc9306bf8757f53674d 3 | 4 | # Installing mono makes `dotnet test` work without errors even for net472. 5 | # But installing it takes a long time, so it's excluded by default. 6 | #RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF 7 | #RUN echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" | tee /etc/apt/sources.list.d/mono-official-stable.list 8 | #RUN apt-get update 9 | #RUN DEBIAN_FRONTEND=noninteractive apt-get install -y mono-devel 10 | 11 | # Clear the NUGET_XMLDOC_MODE env var so xml api doc files get unpacked, allowing a rich experience in Intellisense. 12 | # See https://github.com/dotnet/dotnet-docker/issues/2790 for a discussion on this, where the prioritized use case 13 | # was *not* devcontainers, sadly. 14 | ENV NUGET_XMLDOC_MODE= 15 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Dev space", 3 | "dockerFile": "Dockerfile", 4 | "customizations": { 5 | "vscode": { 6 | "settings": { 7 | "terminal.integrated.shell.linux": "/usr/bin/pwsh" 8 | }, 9 | "extensions": [ 10 | "ms-azure-devops.azure-pipelines", 11 | "ms-dotnettools.csharp", 12 | "k--kato.docomment", 13 | "editorconfig.editorconfig", 14 | "esbenp.prettier-vscode", 15 | "pflannery.vscode-versionlens", 16 | "davidanson.vscode-markdownlint", 17 | "dotjoshjohnson.xml", 18 | "ms-vscode-remote.remote-containers", 19 | "ms-azuretools.vscode-docker", 20 | "tintoy.msbuild-project-tools" 21 | ] 22 | } 23 | }, 24 | "postCreateCommand": "./init.ps1 -InstallLocality machine" 25 | } 26 | -------------------------------------------------------------------------------- /.github/.editorconfig: -------------------------------------------------------------------------------- 1 | [renovate.json*] 2 | indent_style = tab 3 | -------------------------------------------------------------------------------- /.github/actions/publish-artifacts/action.yaml: -------------------------------------------------------------------------------- 1 | name: Publish artifacts 2 | description: Publish artifacts 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | - name: 📥 Collect artifacts 8 | run: tools/artifacts/_stage_all.ps1 9 | shell: pwsh 10 | if: always() 11 | 12 | # TODO: replace this hard-coded list with a loop that utilizes the NPM package at 13 | # https://github.com/actions/toolkit/tree/main/packages/artifact (or similar) to push the artifacts. 14 | 15 | - name: 📢 Upload project.assets.json files 16 | if: always() 17 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 18 | with: 19 | name: projectAssetsJson-${{ runner.os }} 20 | path: ${{ runner.temp }}/_artifacts/projectAssetsJson 21 | continue-on-error: true 22 | - name: 📢 Upload variables 23 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 24 | with: 25 | name: variables-${{ runner.os }} 26 | path: ${{ runner.temp }}/_artifacts/Variables 27 | continue-on-error: true 28 | - name: 📢 Upload build_logs 29 | if: always() 30 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 31 | with: 32 | name: build_logs-${{ runner.os }} 33 | path: ${{ runner.temp }}/_artifacts/build_logs 34 | continue-on-error: true 35 | - name: 📢 Upload testResults 36 | if: always() 37 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 38 | with: 39 | name: testResults-${{ runner.os }} 40 | path: ${{ runner.temp }}/_artifacts/testResults 41 | continue-on-error: true 42 | - name: 📢 Upload coverageResults 43 | if: always() 44 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 45 | with: 46 | name: coverageResults-${{ runner.os }} 47 | path: ${{ runner.temp }}/_artifacts/coverageResults 48 | continue-on-error: true 49 | - name: 📢 Upload symbols 50 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 51 | with: 52 | name: symbols-${{ runner.os }} 53 | path: ${{ runner.temp }}/_artifacts/symbols 54 | continue-on-error: true 55 | - name: 📢 Upload deployables 56 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 57 | with: 58 | name: deployables-${{ runner.os }} 59 | path: ${{ runner.temp }}/_artifacts/deployables 60 | if: always() 61 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "github>microsoft/vs-renovate-presets:microbuild", 5 | "github>microsoft/vs-renovate-presets:vs_main_dependencies", 6 | "github>microsoft/vs-renovate-presets:dotnet_packages_LTS" 7 | ], 8 | "packageRules": [ 9 | { 10 | "matchPackageNames": ["Microsoft.AspNetCore.TestHost"], 11 | "allowedVersions": "<2.3" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: 📚 Docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 9 | permissions: 10 | actions: read 11 | pages: write 12 | id-token: write 13 | contents: read 14 | 15 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 16 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 17 | concurrency: 18 | group: pages 19 | cancel-in-progress: false 20 | 21 | jobs: 22 | publish-docs: 23 | environment: 24 | name: github-pages 25 | url: ${{ steps.deployment.outputs.page_url }} 26 | runs-on: ubuntu-latest 27 | steps: 28 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 29 | with: 30 | fetch-depth: 0 # avoid shallow clone so nbgv can do its work. 31 | - name: ⚙ Install prerequisites 32 | run: ./init.ps1 -UpgradePrerequisites 33 | 34 | - run: dotnet docfx docfx/docfx.json 35 | name: 📚 Generate documentation 36 | 37 | - name: Upload artifact 38 | uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3 39 | with: 40 | path: docfx/_site 41 | 42 | - name: Deploy to GitHub Pages 43 | id: deployment 44 | uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4 45 | -------------------------------------------------------------------------------- /.github/workflows/docs_validate.yml: -------------------------------------------------------------------------------- 1 | name: 📃 Docfx Validate 2 | 3 | on: 4 | pull_request: 5 | workflow_dispatch: 6 | push: 7 | branches: 8 | - main 9 | - microbuild 10 | 11 | jobs: 12 | build: 13 | name: 📚 Doc validation 14 | runs-on: ubuntu-24.04 15 | steps: 16 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 17 | with: 18 | fetch-depth: 0 # avoid shallow clone so nbgv can do its work. 19 | - name: 🔗 Markup Link Checker (mlc) 20 | uses: becheran/mlc@c925f90a9a25e16e4c4bfa29058f6f9ffa9f0d8c # v0.21.0 21 | with: 22 | args: --do-not-warn-for-redirect-to https://learn.microsoft.com*,https://dotnet.microsoft.com/*,https://dev.azure.com/*,https://app.codecov.io/*,https://microsoft.github.io/vs-threading/* -p docfx -i https://aka.ms/onboardsupport,https://aka.ms/spot,https://msrc.microsoft.com/*,https://www.microsoft.com/msrc*,https://microsoft.com/msrc* 23 | - name: ⚙ Install prerequisites 24 | run: | 25 | ./init.ps1 -UpgradePrerequisites 26 | dotnet --info 27 | shell: pwsh 28 | - name: 📚 Verify docfx build 29 | run: dotnet docfx docfx/docfx.json --warningsAsErrors --disableGitFeatures 30 | if: runner.os == 'Linux' 31 | -------------------------------------------------------------------------------- /.prettierrc.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vs-streamjsonrpc/23f1c7d9ff245fef4b7beb7703b97fa1bc7476b2/.prettierrc.yaml -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | // List of extensions which should be recommended for users of this workspace. 5 | "recommendations": [ 6 | "ms-azure-devops.azure-pipelines", 7 | "ms-dotnettools.csharp", 8 | "k--kato.docomment", 9 | "editorconfig.editorconfig", 10 | "esbenp.prettier-vscode", 11 | "pflannery.vscode-versionlens", 12 | "davidanson.vscode-markdownlint", 13 | "dotjoshjohnson.xml", 14 | "ms-vscode-remote.remote-containers", 15 | "ms-azuretools.vscode-docker", 16 | "tintoy.msbuild-project-tools" 17 | ], 18 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace. 19 | "unwantedRecommendations": [] 20 | } 21 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.trimTrailingWhitespace": true, 3 | "files.insertFinalNewline": true, 4 | "files.trimFinalNewlines": true, 5 | "azure-pipelines.1ESPipelineTemplatesSchemaFile": true, 6 | "omnisharp.enableEditorConfigSupport": true, 7 | "omnisharp.enableRoslynAnalyzers": true, 8 | "dotnet.defaultSolution": "StreamJsonRpc.sln", 9 | "dotnet.completion.showCompletionItemsFromUnimportedNamespaces": true, 10 | "editor.formatOnSave": true, 11 | "[xml]": { 12 | "editor.wordWrap": "off" 13 | }, 14 | // Treat these files as Azure Pipelines files 15 | "files.associations": { 16 | "**/azure-pipelines/**/*.yml": "azure-pipelines", 17 | "azure-pipelines.yml": "azure-pipelines" 18 | }, 19 | // Use Prettier as the default formatter for Azure Pipelines files. 20 | // Needs to be explicitly configured: https://github.com/Microsoft/azure-pipelines-vscode#document-formatting 21 | "[azure-pipelines]": { 22 | "editor.defaultFormatter": "esbenp.prettier-vscode", 23 | "editor.formatOnSave": false // enable this when they conform 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /BannedSymbols.txt: -------------------------------------------------------------------------------- 1 | M:Newtonsoft.Json.JsonSerializer.CreateDefault(); depends on JsonConvert.DefaultSettings 2 | M:Newtonsoft.Json.Linq.JObject.FromObject(System.Object); depends on JsonConvert.DefaultSettings 3 | M:Newtonsoft.Json.Linq.JToken.FromObject(System.Object); depends on JsonConvert.DefaultSettings 4 | M:Newtonsoft.Json.Linq.JToken.ToObject(System.Type); depends on JsonConvert.DefaultSettings 5 | M:Newtonsoft.Json.Linq.JToken.ToObject``1; depends on JsonConvert.DefaultSettings 6 | -------------------------------------------------------------------------------- /Directory.Build.rsp: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------ 2 | # This file contains command-line options that MSBuild will process as part of 3 | # every build, unless the "/noautoresponse" switch is specified. 4 | # 5 | # MSBuild processes the options in this file first, before processing the 6 | # options on the command line. As a result, options on the command line can 7 | # override the options in this file. However, depending on the options being 8 | # set, the overriding can also result in conflicts. 9 | # 10 | # NOTE: The "/noautoresponse" switch cannot be specified in this file, nor in 11 | # any response file that is referenced by this file. 12 | #------------------------------------------------------------------------------ 13 | /nr:false 14 | /m 15 | /verbosity:minimal 16 | /clp:Summary;ForceNoAlign 17 | -------------------------------------------------------------------------------- /Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 5 | 16.9 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | StreamJsonRpc 2 | Copyright (c) Microsoft Corporation 3 | All rights reserved.  4 | 5 | MIT License 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vs-StreamJsonRpc 2 | 3 | [![codecov](https://codecov.io/gh/Microsoft/vs-streamjsonrpc/branch/main/graph/badge.svg)](https://codecov.io/gh/Microsoft/vs-streamjsonrpc) 4 | 5 | ## StreamJsonRpc 6 | 7 | [![NuGet package](https://img.shields.io/nuget/v/StreamJsonRpc.svg)](https://www.nuget.org/packages/StreamJsonRpc) 8 | [![Build Status](https://dev.azure.com/azure-public/vside/_apis/build/status/vs-streamjsonrpc)](https://dev.azure.com/azure-public/vside/_build/latest?definitionId=13) 9 | 10 | StreamJsonRpc is a cross-platform, .NET portable library that implements the 11 | [JSON-RPC][JSONRPC] wire protocol. 12 | 13 | It works over [Stream](https://docs.microsoft.com/dotnet/api/system.io.stream), [WebSocket](https://docs.microsoft.com/dotnet/api/system.net.websockets.websocket), or System.IO.Pipelines pipes, independent of the underlying transport. 14 | 15 | Bonus features beyond the JSON-RPC spec include: 16 | 17 | 1. Request cancellation 18 | 1. .NET Events as notifications 19 | 1. Dynamic client proxy generation 20 | 1. Support for [compact binary serialization](https://microsoft.github.io/vs-streamjsonrpc/docs/extensibility.html) via MessagePack 21 | 1. Pluggable architecture for custom message handling and formatting. 22 | 23 | Learn about the use cases for JSON-RPC and how to use this library from our [documentation](https://microsoft.github.io/vs-streamjsonrpc/). 24 | 25 | [JSONRPC]: https://www.jsonrpc.org/ 26 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | batch: true 3 | branches: 4 | include: 5 | - main 6 | - 'v*.*' 7 | - 'validate/*' 8 | paths: 9 | exclude: 10 | - doc/ 11 | - '*.md' 12 | - .vscode/ 13 | - .github/ 14 | - azure-pipelines/release.yml 15 | 16 | parameters: 17 | - name: EnableMacOSBuild 18 | displayName: Build on macOS 19 | type: boolean 20 | default: false # macOS is often bogged down in Azure Pipelines 21 | - name: RunTests 22 | displayName: Run tests 23 | type: boolean 24 | default: true 25 | 26 | variables: 27 | - template: /azure-pipelines/BuildStageVariables.yml@self 28 | 29 | jobs: 30 | - template: azure-pipelines/build.yml 31 | parameters: 32 | Is1ESPT: false 33 | EnableMacOSBuild: ${{ parameters.EnableMacOSBuild }} 34 | RunTests: ${{ parameters.RunTests }} 35 | ShouldSkipOptimize: false 36 | -------------------------------------------------------------------------------- /azure-pipelines/BuildStageVariables.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true 3 | BuildConfiguration: Release 4 | NUGET_PACKAGES: $(Agent.TempDirectory)/.nuget/packages/ 5 | codecov_token: 9a7c2ba3-0a4b-4479-96e8-3bfd01a982f6 6 | -------------------------------------------------------------------------------- /azure-pipelines/CredScanSuppressions.json: -------------------------------------------------------------------------------- 1 | { 2 | "tool": "Credential Scanner", 3 | "suppressions": [ 4 | { 5 | "folder": "testResults-Windows", 6 | "_justification": "Test logs never include credentials, but false positives abound." 7 | }, 8 | { 9 | "folder": "test_logs", 10 | "_justification": "Test logs never include credentials, but false positives abound." 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /azure-pipelines/Get-InsertionPRId.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Look up the pull request URL of the insertion PR. 4 | #> 5 | $stagingFolder = $env:BUILD_STAGINGDIRECTORY 6 | if (!$stagingFolder) { 7 | $stagingFolder = $env:SYSTEM_DEFAULTWORKINGDIRECTORY 8 | if (!$stagingFolder) { 9 | Write-Error "This script must be run in an Azure Pipeline." 10 | exit 1 11 | } 12 | } 13 | $markdownFolder = Join-Path $stagingFolder (Join-Path 'MicroBuild' 'Output') 14 | $markdownFile = Join-Path $markdownFolder 'PullRequestUrl.md' 15 | if (!(Test-Path $markdownFile)) { 16 | Write-Error "This script should be run after the MicroBuildInsertVsPayload task." 17 | exit 2 18 | } 19 | 20 | $insertionPRUrl = Get-Content $markdownFile 21 | if (!($insertionPRUrl -match 'https:.+?/pullrequest/(\d+)')) { 22 | Write-Error "Failed to parse pull request URL: $insertionPRUrl" 23 | exit 3 24 | } 25 | 26 | $Matches[1] 27 | -------------------------------------------------------------------------------- /azure-pipelines/GlobalVariables.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | # These variables are required for MicroBuild tasks 3 | TeamName: VS Core - Extensibility 4 | TeamEmail: vsideexeng@microsoft.com 5 | # These variables influence insertion pipelines 6 | ContainsVsix: false # This should be true when the repo builds a VSIX that should be inserted to VS. 7 | -------------------------------------------------------------------------------- /azure-pipelines/Install-NuGetPackage.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Installs a NuGet package. 4 | .PARAMETER PackageID 5 | The Package ID to install. 6 | .PARAMETER Version 7 | The version of the package to install. If unspecified, the latest stable release is installed. 8 | .PARAMETER Source 9 | The package source feed to find the package to install from. 10 | .PARAMETER PackagesDir 11 | The directory to install the package to. By default, it uses the Packages folder at the root of the repo. 12 | .PARAMETER ConfigFile 13 | The nuget.config file to use. By default, it uses :/nuget.config. 14 | .OUTPUTS 15 | System.String. The path to the installed package. 16 | #> 17 | [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='Low')] 18 | Param( 19 | [Parameter(Position=1,Mandatory=$true)] 20 | [string]$PackageId, 21 | [Parameter()] 22 | [string]$Version, 23 | [Parameter()] 24 | [string]$Source, 25 | [Parameter()] 26 | [switch]$Prerelease, 27 | [Parameter()] 28 | [string]$PackagesDir="$PSScriptRoot\..\packages", 29 | [Parameter()] 30 | [string]$ConfigFile="$PSScriptRoot\..\nuget.config", 31 | [Parameter()] 32 | [ValidateSet('Quiet','Normal','Detailed')] 33 | [string]$Verbosity='normal' 34 | ) 35 | 36 | $nugetPath = & "$PSScriptRoot\..\tools\Get-NuGetTool.ps1" 37 | 38 | try { 39 | Write-Verbose "Installing $PackageId..." 40 | $nugetArgs = "Install",$PackageId,"-OutputDirectory",$PackagesDir,'-ConfigFile',$ConfigFile 41 | if ($Version) { $nugetArgs += "-Version",$Version } 42 | if ($Source) { $nugetArgs += "-FallbackSource",$Source } 43 | if ($Prerelease) { $nugetArgs += "-Prerelease" } 44 | $nugetArgs += '-Verbosity',$Verbosity 45 | 46 | if ($PSCmdlet.ShouldProcess($PackageId, 'nuget install')) { 47 | $p = Start-Process $nugetPath $nugetArgs -NoNewWindow -Wait -PassThru 48 | if ($null -ne $p.ExitCode -and $p.ExitCode -ne 0) { throw } 49 | } 50 | 51 | # Provide the path to the installed package directory to our caller. 52 | Write-Output (Get-ChildItem "$PackagesDir\$PackageId.*")[0].FullName 53 | } finally { 54 | Pop-Location 55 | } 56 | -------------------------------------------------------------------------------- /azure-pipelines/Merge-CodeCoverage.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | 3 | <# 4 | .SYNOPSIS 5 | Merges code coverage reports. 6 | .PARAMETER Path 7 | The path(s) to search for Cobertura code coverage reports. 8 | .PARAMETER Format 9 | The format for the merged result. The default is Cobertura 10 | .PARAMETER OutputDir 11 | The directory the merged result will be written to. The default is `coveragereport` in the root of this repo. 12 | #> 13 | [CmdletBinding()] 14 | Param( 15 | [Parameter(Mandatory=$true)] 16 | [string[]]$Path, 17 | [ValidateSet('Badges', 'Clover', 'Cobertura', 'CsvSummary', 'Html', 'Html_Dark', 'Html_Light', 'HtmlChart', 'HtmlInline', 'HtmlInline_AzurePipelines', 'HtmlInline_AzurePipelines_Dark', 'HtmlInline_AzurePipelines_Light', 'HtmlSummary', 'JsonSummary', 'Latex', 'LatexSummary', 'lcov', 'MarkdownSummary', 'MHtml', 'PngChart', 'SonarQube', 'TeamCitySummary', 'TextSummary', 'Xml', 'XmlSummary')] 18 | [string]$Format='Cobertura', 19 | [string]$OutputFile=("$PSScriptRoot/../coveragereport/merged.cobertura.xml") 20 | ) 21 | 22 | $RepoRoot = [string](Resolve-Path $PSScriptRoot/..) 23 | Push-Location $RepoRoot 24 | try { 25 | Write-Verbose "Searching $Path for *.cobertura.xml files" 26 | $reports = Get-ChildItem -Recurse $Path -Filter *.cobertura.xml 27 | 28 | if ($reports) { 29 | $reports |% { $_.FullName } |% { 30 | # In addition to replacing {reporoot}, we also normalize on one kind of slash so that the report aggregates data for a file whether data was collected on Windows or not. 31 | $xml = [xml](Get-Content -LiteralPath $_) 32 | $xml.coverage.packages.package.classes.class |? { $_.filename} |% { 33 | $_.filename = $_.filename.Replace('{reporoot}', $RepoRoot).Replace([IO.Path]::AltDirectorySeparatorChar, [IO.Path]::DirectorySeparatorChar) 34 | } 35 | 36 | $xml.Save($_) 37 | } 38 | 39 | $Inputs = $reports |% { Resolve-Path -relative $_.FullName } 40 | 41 | if ((Split-Path $OutputFile) -and -not (Test-Path (Split-Path $OutputFile))) { 42 | New-Item -Type Directory -Path (Split-Path $OutputFile) | Out-Null 43 | } 44 | 45 | & dotnet dotnet-coverage merge $Inputs -o $OutputFile -f cobertura 46 | } else { 47 | Write-Error "No reports found to merge." 48 | } 49 | } finally { 50 | Pop-Location 51 | } 52 | -------------------------------------------------------------------------------- /azure-pipelines/NuGetSbom.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | 2 5 | 6 | 7 | -------------------------------------------------------------------------------- /azure-pipelines/PoliCheckExclusions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | NODE_MODULES|.STORE 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /azure-pipelines/PostPRMessage.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding(SupportsShouldProcess = $true)] 2 | param( 3 | [Parameter(Mandatory=$true)] 4 | $AccessToken, 5 | [Parameter(Mandatory=$true)] 6 | $Markdown, 7 | [ValidateSet('Active','ByDesign','Closed','Fixed','Pending','Unknown','WontFix')] 8 | $CommentState='Active' 9 | ) 10 | 11 | # See https://docs.microsoft.com/en-us/dotnet/api/microsoft.teamfoundation.sourcecontrol.webapi.commentthreadstatus?view=azure-devops-dotnet 12 | if ($CommentState -eq 'Active') { 13 | $StatusCode = 1 14 | } elseif ($CommentState -eq 'ByDesign') { 15 | $StatusCode = 5 16 | } elseif ($CommentState -eq 'Closed') { 17 | $StatusCode = 4 18 | } elseif ($CommentState -eq 'Fixed') { 19 | $StatusCode = 2 20 | } elseif ($CommentState -eq 'Pending') { 21 | $StatusCode = 6 22 | } elseif ($CommentState -eq 'Unknown') { 23 | $StatusCode = 0 24 | } elseif ($CommentState -eq 'WontFix') { 25 | $StatusCode = 3 26 | } 27 | 28 | # Build the JSON body up 29 | $body = ConvertTo-Json @{ 30 | comments = @(@{ 31 | parentCommentId = 0 32 | content = $Markdown 33 | commentType = 1 34 | }) 35 | status = $StatusCode 36 | } 37 | 38 | Write-Verbose "Posting JSON payload: `n$Body" 39 | 40 | # Post the message to the Pull Request 41 | # https://docs.microsoft.com/en-us/rest/api/azure/devops/git/pull%20request%20threads?view=azure-devops-rest-5.1 42 | $url = "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$env:SYSTEM_TEAMPROJECTID/_apis/git/repositories/$($env:BUILD_REPOSITORY_NAME)/pullRequests/$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)/threads?api-version=5.1" 43 | if ($PSCmdlet.ShouldProcess($url, 'Post comment via REST call')) { 44 | try { 45 | if (!$env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI) { 46 | Write-Error "Posting to the pull request requires that the script is running in an Azure Pipelines context." 47 | exit 1 48 | } 49 | Write-Host "Posting PR comment to: $url" 50 | Invoke-RestMethod -Uri $url -Method POST -Headers @{Authorization = "Bearer $AccessToken"} -Body $Body -ContentType application/json 51 | } 52 | catch { 53 | Write-Error $_ 54 | Write-Error $_.Exception.Message 55 | exit 2 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /azure-pipelines/TSAOptions.json: -------------------------------------------------------------------------------- 1 | { 2 | "tsaVersion": "TsaV2", 3 | "codebase": "NewOrUpdate", 4 | "codebaseName": "StreamJsonRpc", 5 | "tsaStamp": "DevDiv", 6 | "tsaEnvironment": "PROD", 7 | "notificationAliases": [ 8 | "vsidemicrobuild@microsoft.com" 9 | ], 10 | "codebaseAdmins": [ 11 | "REDMOND\\andarno" 12 | ], 13 | "instanceUrl": "https://devdiv.visualstudio.com", 14 | "projectName": "DevDiv", 15 | "areaPath": "DevDiv\\VS Core\\Extensibility\\StreamJsonRpc", 16 | "iterationPath": "DevDiv", 17 | "alltools": true, 18 | "repositoryName": "vs-StreamJsonRpc" 19 | } 20 | -------------------------------------------------------------------------------- /azure-pipelines/WIFtoPATauth.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: deadPATServiceConnectionId # The GUID of the PAT-based service connection whose access token must be replaced. 3 | type: string 4 | - name: wifServiceConnectionName # The name of the WIF service connection to use to get the access token. 5 | type: string 6 | - name: resource # The scope for which the access token is requested. 7 | type: string 8 | default: 499b84ac-1321-427f-aa17-267ca6975798 # Azure Artifact feeds (any of them) 9 | 10 | steps: 11 | - task: AzureCLI@2 12 | displayName: 🔏 Authenticate with WIF service connection 13 | inputs: 14 | azureSubscription: ${{ parameters.wifServiceConnectionName }} 15 | scriptType: pscore 16 | scriptLocation: inlineScript 17 | inlineScript: | 18 | $accessToken = az account get-access-token --query accessToken --resource '${{ parameters.resource }}' -o tsv 19 | # Set the access token as a secret, so it doesn't get leaked in the logs 20 | Write-Host "##vso[task.setsecret]$accessToken" 21 | # Override the apitoken of the nuget service connection, for the duration of this stage 22 | Write-Host "##vso[task.setendpoint id=${{ parameters.deadPATServiceConnectionId }};field=authParameter;key=apitoken]$accessToken" 23 | -------------------------------------------------------------------------------- /azure-pipelines/apiscan.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: windowsPool 3 | type: object 4 | - name: RealSign 5 | type: boolean 6 | 7 | jobs: 8 | - job: apiscan 9 | displayName: APIScan 10 | dependsOn: Windows 11 | pool: ${{ parameters.windowsPool }} 12 | timeoutInMinutes: 120 13 | templateContext: 14 | ${{ if not(parameters.RealSign) }}: 15 | mb: 16 | signing: # if the build is test-signed, install the signing plugin so that CSVTestSignPolicy.xml is available 17 | enabled: true 18 | zipSources: false 19 | signType: test 20 | outputs: 21 | - output: pipelineArtifact 22 | displayName: 📢 collect apiscan artifact 23 | targetPath: $(Pipeline.Workspace)/.gdn/.r/apiscan/001/Logs 24 | artifactName: apiscan-logs 25 | condition: succeededOrFailed() 26 | variables: 27 | - name: SymbolsFeatureName 28 | value: $[ dependencies.Windows.outputs['SetPipelineVariables.SymbolsFeatureName'] ] 29 | - name: NBGV_MajorMinorVersion 30 | value: $[ dependencies.Windows.outputs['nbgv.NBGV_MajorMinorVersion'] ] 31 | - ${{ if eq(variables['system.collectionId'], '011b8bdf-6d56-4f87-be0d-0092136884d9') }}: 32 | # https://dev.azure.com/devdiv/DevDiv/_wiki/wikis/DevDiv.wiki/25351/APIScan-step-by-step-guide-to-setting-up-a-Pipeline 33 | - group: VSEng sponsored APIScan # Expected to provide ApiScanClientId 34 | steps: 35 | # We need TSAOptions.json 36 | - checkout: self 37 | fetchDepth: 1 38 | 39 | - download: current 40 | artifact: APIScanInputs 41 | displayName: 🔻 Download APIScanInputs artifact 42 | 43 | - task: APIScan@2 44 | displayName: 🔍 Run APIScan 45 | inputs: 46 | softwareFolder: $(Pipeline.Workspace)/APIScanInputs 47 | softwareName: $(SymbolsFeatureName) 48 | softwareVersionNum: $(NBGV_MajorMinorVersion) 49 | isLargeApp: false 50 | toolVersion: Latest 51 | preserveLogsFolder: true 52 | env: 53 | AzureServicesAuthConnectionString: runAs=App;AppId=$(ApiScanClientId) 54 | 55 | # File bugs when APIScan finds issues 56 | - task: TSAUpload@2 57 | displayName: 🪳 TSA upload 58 | inputs: 59 | GdnPublishTsaOnboard: True 60 | GdnPublishTsaConfigFile: $(Build.SourcesDirectory)\azure-pipelines\TSAOptions.json 61 | condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main')) 62 | -------------------------------------------------------------------------------- /azure-pipelines/falsepositives.gdnsuppress: -------------------------------------------------------------------------------- 1 | { 2 | "version": "latest", 3 | "suppressionSets": { 4 | "falsepositives": { 5 | "name": "falsepositives", 6 | "createdDate": "2021-12-04 13:44:31Z", 7 | "lastUpdatedDate": "2021-12-04 13:44:31Z" 8 | } 9 | }, 10 | "results": { 11 | "1a16aa9483f5e231d7752acab742c307290e6984e855810f9799f02f6ecef7ce": { 12 | "signature": "1a16aa9483f5e231d7752acab742c307290e6984e855810f9799f02f6ecef7ce", 13 | "alternativeSignatures": [ 14 | "ad8debd5159bf5995c89a18408ab058ab4a168f17d7a5aaffb1d94a1a5d99cfc" 15 | ], 16 | "target": "streamjsonrpc/release/netstandard2.0/streamjsonrpc.dll", 17 | "memberOf": [ 18 | "falsepositives" 19 | ], 20 | "tool": "apiscan", 21 | "ruleId": "documentationnotfound", 22 | "justification": null, 23 | "createdDate": "2021-12-04 13:44:31Z", 24 | "expirationDate": null, 25 | "type": null 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /azure-pipelines/install-dependencies.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: initArgs 3 | type: string 4 | default: '' 5 | - name: needsAzurePublicFeeds 6 | type: boolean 7 | default: true # If nuget.config pulls from the azure-public account, we need to authenticate when building on the devdiv account. 8 | 9 | steps: 10 | - ${{ if and(parameters.needsAzurePublicFeeds, eq(variables['system.collectionId'], '011b8bdf-6d56-4f87-be0d-0092136884d9')) }}: 11 | - template: WIFtoPATauth.yml 12 | parameters: 13 | wifServiceConnectionName: azure-public/vside package pull 14 | deadPATServiceConnectionId: 46f0d4d4-9fff-4c58-a1ab-3b8f97e3b78a # azure-public/msft_consumption_public 15 | 16 | - task: NuGetAuthenticate@1 17 | displayName: 🔏 Authenticate NuGet feeds 18 | inputs: 19 | ${{ if and(parameters.needsAzurePublicFeeds, eq(variables['system.collectionId'], '011b8bdf-6d56-4f87-be0d-0092136884d9')) }}: 20 | nuGetServiceConnections: azure-public/msft_consumption_public 21 | 22 | - powershell: | 23 | $AccessToken = '$(System.AccessToken)' # Avoid specifying the access token directly on the init.ps1 command line to avoid it showing up in errors 24 | .\init.ps1 -AccessToken $AccessToken ${{ parameters['initArgs'] }} -UpgradePrerequisites -NoNuGetCredProvider 25 | dotnet --info 26 | 27 | # Print mono version if it is present. 28 | if (Get-Command mono -ErrorAction SilentlyContinue) { 29 | mono --version 30 | } 31 | displayName: ⚙ Install prerequisites 32 | 33 | - powershell: tools/variables/_define.ps1 34 | failOnStderr: true 35 | displayName: ⚙ Set pipeline variables based on source 36 | name: SetPipelineVariables 37 | -------------------------------------------------------------------------------- /azure-pipelines/microbuild.after.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: EnableOptProf 3 | type: boolean 4 | default: false 5 | - name: IsOptProf 6 | type: boolean 7 | default: false 8 | - name: SkipCodesignVerify 9 | type: boolean 10 | 11 | steps: 12 | - ${{ if not(parameters.SkipCodesignVerify) }}: # skip CodesignVerify on validation builds because we don't even test-sign nupkg's. 13 | - task: MicroBuildCodesignVerify@3 14 | displayName: 🔍 Verify Signed Files 15 | inputs: 16 | ApprovalListPathForSigs: $(Build.SourcesDirectory)\azure-pipelines\no_strongname.txt 17 | ApprovalListPathForCerts: $(Build.SourcesDirectory)\azure-pipelines\no_authenticode.txt 18 | TargetFolders: | 19 | $(Build.SourcesDirectory)/bin/Packages/$(BuildConfiguration) 20 | condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) 21 | 22 | - ${{ if parameters.IsOptProf }}: 23 | - task: ms-vscs-artifact.build-tasks.artifactDropTask-1.artifactDropTask@0 24 | inputs: 25 | dropServiceURI: https://devdiv.artifacts.visualstudio.com 26 | buildNumber: $(ProfilingInputsDropName) 27 | sourcePath: $(Build.ArtifactStagingDirectory)\OptProf\ProfilingInputs 28 | toLowerCase: false 29 | usePat: true 30 | displayName: 📢 Publish to Artifact Services - ProfilingInputs 31 | condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) 32 | 33 | - task: PublishBuildArtifacts@1 34 | inputs: 35 | PathtoPublish: $(Build.ArtifactStagingDirectory)/InsertionOutputs 36 | ArtifactName: InsertionOutputs 37 | ArtifactType: Container 38 | displayName: 📢 Publish InsertionOutputs as Azure DevOps artifacts 39 | -------------------------------------------------------------------------------- /azure-pipelines/microbuild.before.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: EnableLocalization 3 | type: boolean 4 | default: false 5 | - name: EnableOptProf 6 | type: boolean 7 | default: false 8 | - name: IsOptProf 9 | type: boolean 10 | default: false 11 | - name: ShouldSkipOptimize 12 | type: boolean 13 | default: false 14 | - name: RealSign 15 | type: boolean 16 | 17 | steps: 18 | - ${{ if and(not(parameters.IsOptProf), ne(variables['Build.Reason'], 'PullRequest')) }}: 19 | # notice@0 requires CG detection to run first, and non-default branches don't inject it automatically. 20 | - ${{ if ne(variables['Build.SourceBranch'], 'refs/heads/main') }}: 21 | - task: ComponentGovernanceComponentDetection@0 22 | displayName: 🔍 Component Detection 23 | 24 | - task: notice@0 25 | displayName: 🛠️ Generate NOTICE file 26 | inputs: 27 | outputfile: $(System.DefaultWorkingDirectory)/obj/NOTICE 28 | outputformat: text 29 | retryCountOnTaskFailure: 3 # fails when the cloud service is overloaded 30 | continueOnError: ${{ not(parameters.RealSign) }} # Tolerate failures when we're not building something that may ship. 31 | 32 | - ${{ if parameters.IsOptProf }}: 33 | # We have to install these plugins ourselves for Optprof runs because those pipelines haven't migrated to 1ES PT yet. 34 | - task: MicroBuildOptProfPlugin@6 35 | inputs: 36 | ProfilingInputsDropName: $(ProfilingInputsDropName) 37 | OptimizationInputsLookupMethod: DropPrefix 38 | DropNamePrefix: OptimizationInputs/$(System.TeamProject)/$(Build.Repository.Name) 39 | ShouldSkipOptimize: ${{ parameters.ShouldSkipOptimize }} 40 | AccessToken: $(System.AccessToken) 41 | displayName: 🔧 Install OptProf Plugin 42 | 43 | - task: MicroBuildSigningPlugin@4 44 | inputs: 45 | signType: Real 46 | zipSources: false 47 | displayName: 🔧 Install MicroBuild Signing Plugin 48 | 49 | - ${{ if parameters.EnableLocalization }}: 50 | - task: MicroBuildLocalizationPlugin@4 51 | inputs: 52 | languages: $(LocLanguages) 53 | displayName: 🔧 Install MicroBuild Localization Plugin 54 | -------------------------------------------------------------------------------- /azure-pipelines/no_authenticode.txt: -------------------------------------------------------------------------------- 1 | bin\packages\release\vsix\_manifest\manifest.cat,sbom signed 2 | bin\packages\release\vsix\_manifest\spdx_2.2\manifest.cat,sbom signed 3 | -------------------------------------------------------------------------------- /azure-pipelines/no_strongname.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vs-streamjsonrpc/23f1c7d9ff245fef4b7beb7703b97fa1bc7476b2/azure-pipelines/no_strongname.txt -------------------------------------------------------------------------------- /azure-pipelines/publish-codecoverage.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: EnableMacOSBuild 3 | type: boolean 4 | - name: EnableLinuxBuild 5 | type: boolean 6 | 7 | steps: 8 | - download: current 9 | artifact: coverageResults-Windows 10 | displayName: 🔻 Download Windows code coverage results 11 | continueOnError: true 12 | - ${{ if parameters.EnableLinuxBuild }}: 13 | - download: current 14 | artifact: coverageResults-Linux 15 | displayName: 🔻 Download Linux code coverage results 16 | continueOnError: true 17 | - ${{ if parameters.EnableMacOSBuild }}: 18 | - download: current 19 | artifact: coverageResults-macOS 20 | displayName: 🔻 Download macOS code coverage results 21 | continueOnError: true 22 | - powershell: azure-pipelines/Merge-CodeCoverage.ps1 -Path '$(Pipeline.Workspace)' -OutputFile coveragereport/merged.cobertura.xml -Format Cobertura -Verbose 23 | displayName: ⚙ Merge coverage 24 | - task: PublishCodeCoverageResults@2 25 | displayName: 📢 Publish code coverage results to Azure DevOps 26 | inputs: 27 | summaryFileLocation: coveragereport/merged.cobertura.xml 28 | failIfCoverageEmpty: true 29 | -------------------------------------------------------------------------------- /azure-pipelines/publish-symbols.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: EnableMacOSBuild 3 | type: boolean 4 | - name: EnableLinuxBuild 5 | type: boolean 6 | 7 | steps: 8 | - task: DownloadPipelineArtifact@2 9 | inputs: 10 | artifact: symbols-Windows 11 | path: $(Pipeline.Workspace)/symbols/Windows 12 | displayName: 🔻 Download Windows symbols 13 | continueOnError: true 14 | - ${{ if parameters.EnableLinuxBuild }}: 15 | - task: DownloadPipelineArtifact@2 16 | inputs: 17 | artifact: symbols-Linux 18 | path: $(Pipeline.Workspace)/symbols/Linux 19 | displayName: 🔻 Download Linux symbols 20 | continueOnError: true 21 | - ${{ if parameters.EnableMacOSBuild }}: 22 | - task: DownloadPipelineArtifact@2 23 | inputs: 24 | artifact: symbols-macOS 25 | path: $(Pipeline.Workspace)/symbols/macOS 26 | displayName: 🔻 Download macOS symbols 27 | continueOnError: true 28 | 29 | - task: DownloadPipelineArtifact@2 30 | inputs: 31 | artifact: test_symbols-Windows 32 | path: $(Pipeline.Workspace)/test_symbols/Windows 33 | displayName: 🔻 Download Windows test symbols 34 | continueOnError: true 35 | - ${{ if parameters.EnableLinuxBuild }}: 36 | - task: DownloadPipelineArtifact@2 37 | inputs: 38 | artifact: test_symbols-Linux 39 | path: $(Pipeline.Workspace)/test_symbols/Linux 40 | displayName: 🔻 Download Linux test symbols 41 | continueOnError: true 42 | - ${{ if parameters.EnableMacOSBuild }}: 43 | - task: DownloadPipelineArtifact@2 44 | inputs: 45 | artifact: test_symbols-macOS 46 | path: $(Pipeline.Workspace)/test_symbols/macOS 47 | displayName: 🔻 Download macOS test symbols 48 | continueOnError: true 49 | 50 | - task: PublishSymbols@2 51 | inputs: 52 | SymbolsFolder: $(Pipeline.Workspace)/symbols 53 | SearchPattern: '**/*.pdb' 54 | IndexSources: false 55 | SymbolServerType: TeamServices 56 | displayName: 📢 Publish symbols 57 | 58 | - task: PublishSymbols@2 59 | inputs: 60 | SymbolsFolder: $(Pipeline.Workspace)/test_symbols 61 | SearchPattern: '**/*.pdb' 62 | IndexSources: false 63 | SymbolServerType: TeamServices 64 | displayName: 📢 Publish test symbols 65 | 66 | - powershell: tools/Prepare-Legacy-Symbols.ps1 -Path $(Pipeline.Workspace)/symbols/Windows 67 | displayName: ⚙ Prepare symbols for symbol archival 68 | -------------------------------------------------------------------------------- /azure-pipelines/publish_artifacts.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This script translates all the artifacts described by _all.ps1 4 | into commands that instruct Azure Pipelines to actually collect those artifacts. 5 | #> 6 | 7 | [CmdletBinding()] 8 | param ( 9 | [string]$ArtifactNameSuffix, 10 | [switch]$StageOnly, 11 | [switch]$AvoidSymbolicLinks 12 | ) 13 | 14 | Function Set-PipelineVariable($name, $value) { 15 | if ((Test-Path "Env:\$name") -and (Get-Item "Env:\$name").Value -eq $value) { 16 | return # already set 17 | } 18 | 19 | #New-Item -LiteralPath "Env:\$name".ToUpper() -Value $value -Force | Out-Null 20 | Write-Host "##vso[task.setvariable variable=$name]$value" 21 | } 22 | 23 | Function Test-ArtifactUploaded($artifactName) { 24 | $varName = "ARTIFACTUPLOADED_$($artifactName.ToUpper())" 25 | Test-Path "env:$varName" 26 | } 27 | 28 | & "$PSScriptRoot/../tools/artifacts/_stage_all.ps1" -ArtifactNameSuffix $ArtifactNameSuffix -AvoidSymbolicLinks:$AvoidSymbolicLinks |% { 29 | # Set a variable which will out-live this script so that a subsequent attempt to collect and upload artifacts 30 | # will skip this one from a check in the _all.ps1 script. 31 | Set-PipelineVariable "ARTIFACTSTAGED_$($_.Name.ToUpper())" 'true' 32 | Write-Host "Staged artifact $($_.Name) to $($_.Path)" 33 | 34 | if (!$StageOnly) { 35 | if (Test-ArtifactUploaded $_.Name) { 36 | Write-Host "Skipping $($_.Name) because it has already been uploaded." -ForegroundColor DarkGray 37 | } else { 38 | Write-Host "##vso[artifact.upload containerfolder=$($_.Name);artifactname=$($_.Name);]$($_.Path)" 39 | 40 | # Set a variable which will out-live this script so that a subsequent attempt to collect and upload artifacts 41 | # will skip this one from a check in the _all.ps1 script. 42 | Set-PipelineVariable "ARTIFACTUPLOADED_$($_.Name.ToUpper())" 'true' 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /azure-pipelines/release-deployment-prep.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - download: CI 3 | artifact: Variables-Windows 4 | displayName: 🔻 Download Variables-Windows artifact 5 | - powershell: $(Pipeline.Workspace)/CI/Variables-Windows/_define.ps1 6 | displayName: ⚙️ Set pipeline variables based on artifacts 7 | -------------------------------------------------------------------------------- /azure-pipelines/schedule-only-steps.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - powershell: echo "##vso[build.addbuildtag]auto-insertion" 3 | displayName: Tag for auto-insertion 4 | -------------------------------------------------------------------------------- /azurepipelines-coverage.yml: -------------------------------------------------------------------------------- 1 | # https://learn.microsoft.com/azure/devops/pipelines/test/codecoverage-for-pullrequests?view=azure-devops 2 | coverage: 3 | status: 4 | comments: on # add comment to PRs reporting diff in coverage of modified files 5 | diff: # diff coverage is code coverage only for the lines changed in a pull request. 6 | target: 70% # set this to a desired %. Default is 70% 7 | -------------------------------------------------------------------------------- /docfx/.gitignore: -------------------------------------------------------------------------------- 1 | _site/ 2 | api/ 3 | -------------------------------------------------------------------------------- /docfx/docfx.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": [ 3 | { 4 | "src": [ 5 | { 6 | "src": "../src/StreamJsonRpc", 7 | "files": [ 8 | "**/*.csproj" 9 | ] 10 | } 11 | ], 12 | "dest": "api" 13 | } 14 | ], 15 | "build": { 16 | "content": [ 17 | { 18 | "files": [ 19 | "**/*.{md,yml}" 20 | ], 21 | "exclude": [ 22 | "_site/**" 23 | ] 24 | } 25 | ], 26 | "resource": [ 27 | { 28 | "files": [ 29 | "images/**" 30 | ] 31 | } 32 | ], 33 | "xref": [ 34 | "https://dotnet.github.io/Nerdbank.Streams/xrefmap.yml", 35 | "https://microsoft.github.io/vs-threading/xrefmap.yml", 36 | "https://microsoft.github.io/vs-validation/xrefmap.yml", 37 | "https://learn.microsoft.com/en-us/dotnet/.xrefmap.json" 38 | ], 39 | "output": "_site", 40 | "template": [ 41 | "default", 42 | "modern" 43 | ], 44 | "globalMetadata": { 45 | "_appName": "StreamJsonRpc", 46 | "_appTitle": "StreamJsonRpc", 47 | "_enableSearch": true, 48 | "pdf": false 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /docfx/docs/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ## Installation 4 | 5 | Consume this library via its NuGet Package. 6 | Click on the badge to find its latest version and the instructions for consuming it that best apply to your project. 7 | 8 | [![NuGet package](https://img.shields.io/nuget/v/StreamJsonRpc.svg)](https://nuget.org/packages/StreamJsonRpc) 9 | 10 | ## Use cases 11 | 12 | If you're new to JSON-RPC and/or StreamJsonRpc, check out our brief [overview](../index.md). 13 | The rest of our documentation are organized around use case. 14 | 15 | 1. [Establish a connection](connecting.md) 16 | 1. [Send an RPC request](sendrequest.md) 17 | 1. [Receive an RPC request](recvrequest.md) 18 | 1. [Disconnect](disconnecting.md) 19 | 1. [Test your code](testing.md) 20 | 1. [Write resilient code](resiliency.md) 21 | 1. [Remote Targets](remotetargets.md) 22 | 1. [Pass certain special types in arguments or return values](../exotic_types/index.md) 23 | 1. [Trace context](tracecontext.md) 24 | 1. [JoinableTaskFactory integration](joinableTaskFactory.md) 25 | 1. [Troubleshoot](troubleshooting.md) 26 | 27 | See [full samples](https://github.com/AArnott/StreamJsonRpc.Sample) demonstrating two processes 28 | on the same machine utilizing this library for RPC, using either named pipes or web sockets. 29 | 30 | [Learn more about how you can customize the JSON-RPC protocol's wire format](extensibility.md) that StreamJsonRpc uses for better interoperability or better performance. 31 | 32 | See also [Visual Studio specific concerns](vs.md). 33 | -------------------------------------------------------------------------------- /docfx/docs/remotetargets.md: -------------------------------------------------------------------------------- 1 | # Adding Remote RPC Targets 2 | 3 | There are scenarios where users may need to add remote RPC targets to facilitate communication between two endpoints that have no direct RPC connection channel. Consider the following 3 endpoints: 4 | 5 | * client 6 | * server 7 | * remote 8 | 9 | There is a direct RPC connection between client and server, and server and remote. However, client and remote may need to send messages to each other as well. To do so, users can utilize the remote target feature: 10 | 11 | ``` csharp 12 | var serverToClientRpc = new JsonRpc(clientStream, new ClientTarget()); 13 | var serverToRemoteRpc = new JsonRpc(remoteStream, new RemoteTarget()); 14 | 15 | // This allows the client endpoint to send messages to the remote endpoint. 16 | serverToClientRpc.AddRemoteTarget(serverToRemoteRpc); 17 | 18 | // This allows the remote endpoint to send messages to the client endpoint. 19 | serverToRemoteRpc.AddRemoteTarget(serverToClientRpc); 20 | ``` 21 | 22 | ### Intercepting Remote Target Calls 23 | 24 | The client can only make a call to the remote endpoint if the server (what's in the middle) hasn't defined a method with the same signature. For example, consider the following: 25 | 26 | ``` csharp 27 | public class ServerTarget 28 | { 29 | public int GetInt() 30 | { 31 | return 5; 32 | } 33 | } 34 | 35 | public class RemoteTarget 36 | { 37 | public int GetInt() 38 | { 39 | return 10; 40 | } 41 | } 42 | 43 | public static class Sample 44 | { 45 | public static async Task InterceptionTest() 46 | { 47 | var clientRpc = new JsonRpc(clientServerStream); 48 | var serverClientRpc = new JsonRpc(serverClientStream, new ServerTarget()); 49 | var serverRemoteRpc = new JsonRpc(serverRemoteStream); 50 | var remoteServerRpc = new JsonRpc(remoteServerStream, new RemoteTarget()); 51 | 52 | serverClientRpc.AddRemoteTarget(serverRemoteRpc); 53 | 54 | clientRpc.StartListening(); 55 | serverClientRpc.StartListening(); 56 | remoteServerRpc.StartListening(); 57 | 58 | var result = clientRpc.InvokeAsync("GetInt"); 59 | } 60 | } 61 | ``` 62 | 63 | The `result` captured in `InterceptionTest()` would be 5 instead of 10. When invoking methods on the remote target, StreamJsonRpc will first check to see if there are local methods matching the invocation signature. Only when it does not find a method will it forward the call to the remote target. 64 | -------------------------------------------------------------------------------- /docfx/docs/security.md: -------------------------------------------------------------------------------- 1 | # StreamJsonRpc security guidance 2 | 3 | The following links contain information about the security practices followed in this repo: 4 | 5 | * [Registering all public methods/events on an object](https://github.com/microsoft/vs-streamjsonrpc/blob/main/doc/recvrequest.md#registering-all-public-methodsevents-on-an-object) 6 | * [Message formatters](https://github.com/microsoft/vs-streamjsonrpc/blob/main/doc/extensibility.md#message-formatters) 7 | * [MessagePack security](https://github.com/MessagePack-CSharp/MessagePack-CSharp#security) 8 | -------------------------------------------------------------------------------- /docfx/docs/testing.md: -------------------------------------------------------------------------------- 1 | # Testability/mockability 2 | 3 | Testing this library or users of this library can be done without any transport 4 | by using @Nerdbank.Streams.FullDuplexStream.CreatePair*?displayProperty=nameWithType from the the [Nerdbank.Streams](https://www.nuget.org/packages/nerdbank.streams) library in your tests 5 | to produce a pair of full-duplex Stream objects. 6 | -------------------------------------------------------------------------------- /docfx/docs/toc.yml: -------------------------------------------------------------------------------- 1 | items: 2 | - href: getting-started.md 3 | - href: connecting.md 4 | - href: sendrequest.md 5 | - href: recvrequest.md 6 | - href: disconnecting.md 7 | - href: testing.md 8 | - href: resiliency.md 9 | - href: remotetargets.md 10 | - href: tracecontext.md 11 | - href: joinableTaskFactory.md 12 | - href: troubleshooting.md 13 | - href: extensibility.md 14 | - href: vs.md 15 | 16 | - href: dynamicproxy.md 17 | - href: exceptions.md 18 | - href: security.md 19 | -------------------------------------------------------------------------------- /docfx/docs/troubleshooting.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | ## Tracing 4 | 5 | When investigating failures, you may find StreamJsonRpc's tracing functionality useful. 6 | 7 | With the property, you can listen for: 8 | 9 | 1. Which server methods are registered 10 | 1. Incoming and outgoing JSON-RPC messages and how they're being handled along the entire pipeline 11 | 1. When listening is started 12 | 1. RPC method invocation failures with full exception callstacks. 13 | 14 | The above is just a sample. The full list of events is available on the enum. 15 | 16 | ## Other issues 17 | 18 | ### Requests failing with ConnectionLostException 19 | 20 | Please see our [disconnecting](disconnecting.md) documentation. 21 | 22 | ### Hangs after connecting over IPC pipes 23 | 24 | When connecting two processes using Windows (named) pipes, be sure to use 25 | when creating those pipes to be used with our @StreamJsonRpc.JsonRpc class. All our I/O is asynchronous, and 26 | without that flag, .NET Framework and .NET Core will hang. 27 | See [dotnet/runtime#31390](https://github.com/dotnet/runtime/issues/31390). 28 | -------------------------------------------------------------------------------- /docfx/docs/vs.md: -------------------------------------------------------------------------------- 1 | # Visual Studio specific concerns 2 | 3 | ## Versions to reference for Visual Studio extensions 4 | 5 | When building a Visual Studio extension, you should not include StreamJsonRpc in your VSIX. Instead, you should compile your extension against the version of StreamJsonRpc that is included in the Visual Studio version that you are targeting as your minimum supported version and rely on Visual Studio's copy of the library to be present at runtime. 6 | 7 | | VS version | StreamJsonRpc version | 8 | | -- | -- | 9 | | VS 2017.0 | 1.0.x 10 | | VS 2017.3 | 1.1.x 11 | | VS 2017.5 | 1.2.x 12 | | VS 2017.6 | 1.3.x 13 | | VS 2019.0 | 1.5.x 14 | | VS 2019.1 | 1.5.x, 2.0.x 15 | | VS 2019.3 | 1.5.x, 2.1.x 16 | | VS 2019.4 | 1.5.x, 2.2.x 17 | | VS 2019.5 | 1.5.x, 2.3.x 18 | | VS 2019.6 | 1.5.x, 2.4.x 19 | | VS 2019.7 | 1.5.x, 2.5.x 20 | | VS 2019.8 | 1.5.x, 2.6.x 21 | | VS 2019.9 | 1.5.x, 2.7.x 22 | | VS 2019.10 | 1.5.x, 2.8.x 23 | | VS 2022.0 | 1.5.x, 2.9.x 24 | | VS 2022.1 | 1.5.x, 2.10.x 25 | | VS 2022.2 | 1.5.x, 2.11.x 26 | | VS 2022.3 | 1.5.x, 2.12.x 27 | | VS 2022.4 | 1.5.x, 2.13.x 28 | | VS 2022.5 | 1.5.x, 2.14.x 29 | | VS 2022.6 | 1.5.x, 2.15.x 30 | | VS 2022.7 | 1.5.x, 2.16.x 31 | | VS 2022.8 | 1.5.x, 2.17.x 32 | | VS 2022.10 | 1.5.x, 2.18.x 33 | | VS 2022.11 | 1.5.x, 2.19.x 34 | | VS 2022.12 | 1.5.x, 2.20.x 35 | | VS 2022.13 | 1.5.x, 2.21.x 36 | | VS 2022.14 | 1.5.x, 2.22.x 37 | | VS 2022.15 | 1.5.x, 2.23.x 38 | 39 | StreamJsonRpc versions are forwards and backwards compatible "over the wire". For example it is perfectly legitimate to use StreamJsonRpc 2.4 on the server-side even if the client only uses 1.0, or vice versa. If an RPC method utilizes a newer StreamJsonRpc feature (e.g. `IAsyncEnumerable` return value) and an older client that doesn't support these specially marshaled objects is used to call that method, a memory leak on the server may result. 40 | 41 | StreamJsonRpc is binary compatible within a major version. If you compile against 1.3 for targeting VS 2017.6, you'll successfully run against the StreamJsonRpc 1.5 version when installed in a later version of Visual Studio. 42 | StreamJsonRpc 2.0 introduced breaking changes, so folks who compile against 1.x will continue to run on 1.x, while folks who want the additional functionality of 2.0 may recompile against that and work in VS 2019.1 and later. 43 | -------------------------------------------------------------------------------- /docfx/exotic_types/index.md: -------------------------------------------------------------------------------- 1 | # Exotic Types 2 | 3 | Some types are not serializable but are specially recognized and marshaled by StreamJsonRpc when used in arguments or return values. 4 | 5 | * [`CancellationToken`](../docs/sendrequest.md#cancellation) 6 | * [`IProgress`](progresssupport.md) 7 | * [`Stream`, `IDuplexPipe`, `PipeReader`, `PipeWriter`](oob_streams.md) 8 | * [`IAsyncEnumerable`](asyncenumerable.md) 9 | * [`IObserver`](observer.md) 10 | * [`IDisposable`](disposable.md) 11 | * [Interfaces marked with `RpcMarshalableAttribute`](rpc_marshalable_objects.md) 12 | * [General marshalable object support](general_marshaled_objects.md) 13 | 14 | The @System.Threading.CancellationToken support is built into the @StreamJsonRpc.JsonRpc class itself so that it works in any configuration, provided the remote side also supports it. 15 | 16 | The rest of the types listed above require support by the in use. All formatters that ship within the StreamJsonRpc library come with built-in support for all of these. 17 | -------------------------------------------------------------------------------- /docfx/exotic_types/toc.yml: -------------------------------------------------------------------------------- 1 | items: 2 | - href: index.md 3 | - href: asyncenumerable.md 4 | - href: disposable.md 5 | - href: observer.md 6 | - href: oob_streams.md 7 | - href: progresssupport.md 8 | - href: rpc_marshalable_objects.md 9 | - href: general_marshaled_objects.md 10 | - href: general_marshaled_objects_2.md 11 | -------------------------------------------------------------------------------- /docfx/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | _layout: landing 3 | --- 4 | 5 | # Overview 6 | 7 | ## Protocol overview 8 | 9 | [JSON-RPC][spec] is a two-party, peer-to-peer based protocol by which one party can request a method's invocation 10 | of the other party, and optionally receive a response from the server with the result of that invocation. 11 | 12 | While we may often use the words 'client' and 'server' in discussions and documentation around JSON-RPC, 13 | these are just convenient terms for referring to the party that is requesting a method invocation vs. the 14 | party that is serving that request. At any given moment, *either party* may send an RPC request to the other party. 15 | 16 | A common pattern is that one party tends to issue most of the RPC requests, while the other party may occasionally 17 | transmit requests as a "call back" to the client for raising notifications. This is merely an artifact of architectural 18 | expediency for many applications and not due to any design of the JSON-RPC protocol, or this library's particular 19 | implementation of it. 20 | 21 | For this reason, our documentation is organized around individual scenarios rather than on the client and server roles. 22 | 23 | ## StreamJsonRpc's role 24 | 25 | StreamJsonRpc is a .NET library that implements the JSON-RPC protocol and exposes it via a .NET API to easily 26 | send and receive RPC requests. 27 | 28 | StreamJsonRpc works on any transport (e.g. Stream, WebSocket, Pipe). 29 | 30 | ## Security 31 | 32 | The fundamental feature of the JSON-RPC protocol is the ability to request code execution of another party, including 33 | passing data either direction that may influence code execution. 34 | Neither the JSON-RPC protocol nor this library attempts to address the applicable security risks entailed. 35 | 36 | Before establishing a JSON-RPC connection with a party that exists outside your own trust boundary, consider 37 | the threats and how to mitigate them at your application level. 38 | 39 | [spec]: https://www.jsonrpc.org/specification 40 | -------------------------------------------------------------------------------- /docfx/toc.yml: -------------------------------------------------------------------------------- 1 | items: 2 | - name: Docs 3 | href: docs/ 4 | - name: Exotic Types 5 | href: exotic_types/ 6 | - name: API 7 | href: api/ 8 | - name: GitHub 9 | href: https://github.com/microsoft/vs-streamjsonrpc 10 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "9.0.203", 4 | "rollForward": "patch", 5 | "allowPrerelease": false 6 | }, 7 | "msbuild-sdks": { 8 | "Microsoft.Build.NoTargets": "3.7.56" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /init.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | SETLOCAL 3 | set PS1UnderCmd=1 4 | 5 | :: Get the datetime in a format that can go in a filename. 6 | set _my_datetime=%date%_%time% 7 | set _my_datetime=%_my_datetime: =_% 8 | set _my_datetime=%_my_datetime::=% 9 | set _my_datetime=%_my_datetime:/=_% 10 | set _my_datetime=%_my_datetime:.=_% 11 | set CmdEnvScriptPath=%temp%\envvarscript_%_my_datetime%.cmd 12 | 13 | powershell.exe -NoProfile -NoLogo -ExecutionPolicy bypass -Command "try { & '%~dpn0.ps1' %*; exit $LASTEXITCODE } catch { write-host $_; exit 1 }" 14 | 15 | :: Set environment variables in the parent cmd.exe process. 16 | IF EXIST "%CmdEnvScriptPath%" ( 17 | ENDLOCAL 18 | CALL "%CmdEnvScriptPath%" 19 | DEL "%CmdEnvScriptPath%" 20 | ) 21 | -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /settings.VisualStudio.json: -------------------------------------------------------------------------------- 1 | { 2 | "textEditor.codeCleanup.profile": "profile1" 3 | } 4 | -------------------------------------------------------------------------------- /src/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | dotnet_diagnostic.VSTHRD111.severity = error 4 | dotnet_diagnostic.VSTHRD100.severity = error 5 | dotnet_diagnostic.VSTHRD200.severity = error 6 | -------------------------------------------------------------------------------- /src/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Runtime.InteropServices; 5 | 6 | [assembly: DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] 7 | -------------------------------------------------------------------------------- /src/AssemblyInfo.vb: -------------------------------------------------------------------------------- 1 | ' Copyright (c) Microsoft Corporation. All rights reserved. 2 | ' Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | Imports System.Runtime.InteropServices 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | README.md 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | PackageIcon.png 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/OptProf.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | IBC 5 | Common7\IDE\PrivateAssemblies\$(TargetFileName) 6 | /ExeConfig:"%VisualStudio.InstallationUnderTest.Path%\Common7\IDE\vsn.exe" 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/PackageIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vs-streamjsonrpc/23f1c7d9ff245fef4b7beb7703b97fa1bc7476b2/src/PackageIcon.png -------------------------------------------------------------------------------- /src/StreamJsonRpc/CommonMethodNameTransforms.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Text.Json; 5 | 6 | namespace StreamJsonRpc; 7 | 8 | /// 9 | /// Common RPC method transform functions that may be supplied to 10 | /// by way of . 11 | /// 12 | public static class CommonMethodNameTransforms 13 | { 14 | /// 15 | /// Gets a function that converts a given string from PascalCase to camelCase. 16 | /// 17 | public static Func CamelCase 18 | { 19 | get 20 | { 21 | return name => 22 | { 23 | if (name is null) 24 | { 25 | throw new ArgumentNullException(); 26 | } 27 | 28 | return Utilities.ToCamelCase(name); 29 | }; 30 | } 31 | } 32 | 33 | /// 34 | /// Gets a function that prepends a particular string in front of any RPC method name. 35 | /// 36 | /// 37 | /// The prefix to prepend to any method name. 38 | /// This value must not be null. 39 | /// When this value is the empty string, no transformation is performed by the returned function. 40 | /// 41 | /// The transform function. 42 | public static Func Prepend(string prefix) 43 | { 44 | Requires.NotNull(prefix, nameof(prefix)); 45 | 46 | if (prefix.Length == 0) 47 | { 48 | return name => name; 49 | } 50 | else 51 | { 52 | // Using a local variable for the closure avoids C# from allocating the closure 53 | // earlier in the method, which would impact even the fast path. 54 | string localPrefix = prefix; 55 | return name => localPrefix + name; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/DisposableAction.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace StreamJsonRpc; 5 | 6 | internal class DisposableAction : IDisposableObservable 7 | { 8 | private static readonly Action EmptyAction = () => { }; 9 | private Action? disposeAction; 10 | 11 | internal DisposableAction(Action? disposeAction) 12 | { 13 | this.disposeAction = disposeAction ?? EmptyAction; 14 | } 15 | 16 | public bool IsDisposed => this.disposeAction is null; 17 | 18 | public void Dispose() 19 | { 20 | Interlocked.Exchange(ref this.disposeAction, null)?.Invoke(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/EventArgs/DisconnectedReason.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace StreamJsonRpc; 5 | 6 | /// 7 | /// Identifies a reason for a stream disconnection. 8 | /// 9 | public enum DisconnectedReason 10 | { 11 | /// 12 | /// An error occurred while accessing the stream. 13 | /// 14 | StreamError, 15 | 16 | /// 17 | /// A syntax or schema error while reading a JSON-RPC packet occurred. 18 | /// 19 | ParseError, 20 | 21 | /// 22 | /// The instance was disposed. 23 | /// 24 | LocallyDisposed, 25 | 26 | /// 27 | /// The underlying transport was closed by the remote party. 28 | /// 29 | RemotePartyTerminated, 30 | 31 | /// 32 | /// A fatal exception was thrown in a local method that was requested by the remote party. 33 | /// 34 | /// 35 | /// Exceptions thrown by locally dispatched method calls are not considered fatal by default. 36 | /// The method may be overridden 37 | /// by an application in order to enable a JSON-RPC server to throw an exception that can terminate 38 | /// the entire connection. 39 | /// 40 | FatalException, 41 | 42 | /// 43 | /// An extensibility point was leveraged locally and broke the contract. 44 | /// 45 | LocalContractViolation, 46 | 47 | /// 48 | /// The remote party violated the JSON-RPC protocol. 49 | /// 50 | RemoteProtocolViolation, 51 | } 52 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/ExceptionProcessing.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using StreamJsonRpc.Protocol; 5 | 6 | namespace StreamJsonRpc; 7 | 8 | /// 9 | /// Enumerates the exception handling behaviors that are built into the class. 10 | /// 11 | /// 12 | public enum ExceptionProcessing 13 | { 14 | /// 15 | /// Exceptions thrown by the server are serialized as the simple class 16 | /// and the default error code is . 17 | /// The client experiences a whose property 18 | /// is the deserialized . 19 | /// 20 | CommonErrorData, 21 | 22 | /// 23 | /// Exceptions thrown by the server are serialized via the mechanism and captures more detail, 24 | /// using the error code . 25 | /// These are deserialized with the original exception types as inner exceptions of the thrown at the client. 26 | /// The is also set to an instance of that resembles the inner exception tree. 27 | /// 28 | ISerializable, 29 | } 30 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/Exceptions/BadRpcHeaderException.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace StreamJsonRpc; 5 | 6 | /// 7 | /// An exception thrown when a deserialized message has a bad header. 8 | /// 9 | /// 10 | [Serializable] 11 | public class BadRpcHeaderException : RemoteRpcException 12 | { 13 | /// 14 | /// Initializes a new instance of the class. 15 | /// 16 | /// The message. 17 | public BadRpcHeaderException(string? message) 18 | : base(message) 19 | { 20 | } 21 | 22 | /// 23 | /// Initializes a new instance of the class. 24 | /// 25 | /// The message. 26 | /// The inner exception. 27 | public BadRpcHeaderException(string? message, Exception? innerException) 28 | : base(message, innerException) 29 | { 30 | } 31 | 32 | /// 33 | /// Initializes a new instance of the class. 34 | /// 35 | /// Serialization info. 36 | /// Streaming context. 37 | #if NET8_0_OR_GREATER 38 | [Obsolete] 39 | #endif 40 | protected BadRpcHeaderException( 41 | System.Runtime.Serialization.SerializationInfo info, 42 | System.Runtime.Serialization.StreamingContext context) 43 | : base(info, context) 44 | { 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/Exceptions/ConnectionLostException.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace StreamJsonRpc; 5 | 6 | /// 7 | /// An exception used to fault a returned from a request 8 | /// when the request could not be completed or the response cannot be received because the connection dropped. 9 | /// 10 | [System.Serializable] 11 | public class ConnectionLostException : RemoteRpcException 12 | { 13 | /// 14 | /// Initializes a new instance of the class. 15 | /// 16 | public ConnectionLostException() 17 | : this(Resources.ConnectionDropped) 18 | { 19 | } 20 | 21 | /// 22 | /// Initializes a new instance of the class. 23 | /// 24 | /// The message that describes the error. 25 | public ConnectionLostException(string? message) 26 | : base(message) 27 | { 28 | } 29 | 30 | /// 31 | /// Initializes a new instance of the class. 32 | /// 33 | /// The error message that explains the reason for the exception. 34 | /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. 35 | public ConnectionLostException(string? message, Exception? innerException) 36 | : base(message, innerException) 37 | { 38 | } 39 | 40 | /// 41 | /// Initializes a new instance of the class. 42 | /// 43 | /// Serialization info. 44 | /// Streaming context. 45 | #if NET8_0_OR_GREATER 46 | [Obsolete] 47 | #endif 48 | protected ConnectionLostException( 49 | System.Runtime.Serialization.SerializationInfo info, 50 | System.Runtime.Serialization.StreamingContext context) 51 | : base(info, context) 52 | { 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/Exceptions/LocalRpcException.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using StreamJsonRpc.Protocol; 5 | 6 | namespace StreamJsonRpc; 7 | 8 | /// 9 | /// An exception that may be thrown within a locally invoked server method, and carries with it data that influences the JSON-RPC error message's error object. 10 | /// 11 | public class LocalRpcException : Exception 12 | { 13 | /// 14 | /// Initializes a new instance of the class. 15 | /// 16 | public LocalRpcException() 17 | { 18 | } 19 | 20 | /// 21 | /// Initializes a new instance of the class. 22 | /// 23 | /// The error message that explains the reason for the exception. 24 | public LocalRpcException(string? message) 25 | : base(message) 26 | { 27 | } 28 | 29 | /// 30 | /// Initializes a new instance of the class. 31 | /// 32 | /// The error message that explains the reason for the exception. 33 | /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. 34 | public LocalRpcException(string? message, Exception? inner) 35 | : base(message, inner) 36 | { 37 | } 38 | 39 | /// 40 | /// Gets or sets the value for the error.data property. 41 | /// 42 | public object? ErrorData { get; set; } 43 | 44 | /// 45 | /// Gets or sets the value for the error.code property. 46 | /// 47 | /// 48 | /// The default value is set to a special general error code: . 49 | /// This may be set to a more meaningful error code for the application that allows the client to programatically respond to the error condition. 50 | /// Application-defined values should avoid the [-32768, -32000] range, which is reserved for the JSON-RPC protocol itself. 51 | /// 52 | public int ErrorCode { get; set; } = (int)JsonRpcErrorCode.InvocationError; 53 | } 54 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/Exceptions/RemoteSerializationException.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using StreamJsonRpc.Protocol; 5 | 6 | namespace StreamJsonRpc; 7 | 8 | /// 9 | /// An exception thrown from back to the client from various request methods when the server failed to serialize the response. 10 | /// 11 | /// 12 | /// This exception comes from the error code. 13 | /// 14 | [Serializable] 15 | public class RemoteSerializationException : RemoteRpcException 16 | { 17 | /// 18 | /// Initializes a new instance of the class. 19 | /// 20 | /// 21 | public RemoteSerializationException(string? message, object? errorData, object? deserializedErrorData) 22 | : base(message) 23 | { 24 | this.ErrorCode = JsonRpcErrorCode.ResponseSerializationFailure; 25 | this.ErrorData = errorData; 26 | this.DeserializedErrorData = deserializedErrorData; 27 | } 28 | 29 | /// 30 | /// Initializes a new instance of the class. 31 | /// 32 | /// Serialization info. 33 | /// Streaming context. 34 | #if NET8_0_OR_GREATER 35 | [Obsolete] 36 | #endif 37 | protected RemoteSerializationException(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) 38 | : base(serializationInfo, streamingContext) 39 | { 40 | throw new NotImplementedException(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/Exceptions/UnexpectedEmptyEnumerableResponseException.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace StreamJsonRpc; 5 | 6 | internal class UnexpectedEmptyEnumerableResponseException : RemoteRpcException 7 | { 8 | /// 9 | /// Initializes a new instance of the class. 10 | /// 11 | /// 12 | public UnexpectedEmptyEnumerableResponseException(string? message) 13 | : base(message) 14 | { 15 | } 16 | 17 | /// 18 | /// Initializes a new instance of the class. 19 | /// 20 | /// 21 | public UnexpectedEmptyEnumerableResponseException(string? message, Exception? innerException) 22 | : base(message, innerException) 23 | { 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/Exceptions/UnrecognizedJsonRpcMessageException.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Runtime.Serialization; 5 | 6 | namespace StreamJsonRpc; 7 | 8 | /// 9 | /// An exception thrown when an incoming JSON-RPC message could not be recognized as conforming to any known JSON-RPC message. 10 | /// 11 | [Serializable] 12 | public class UnrecognizedJsonRpcMessageException : RemoteRpcException 13 | { 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | public UnrecognizedJsonRpcMessageException() 18 | : base(Resources.UnrecognizableMessage) 19 | { 20 | } 21 | 22 | /// 23 | /// Initializes a new instance of the class. 24 | /// 25 | /// The error message that explains the reason for the exception. 26 | public UnrecognizedJsonRpcMessageException(string? message) 27 | : base(message) 28 | { 29 | } 30 | 31 | /// 32 | /// Initializes a new instance of the class. 33 | /// 34 | /// The error message that explains the reason for the exception. 35 | /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. 36 | public UnrecognizedJsonRpcMessageException(string? message, Exception? innerException) 37 | : base(message, innerException) 38 | { 39 | } 40 | 41 | /// 42 | /// Initializes a new instance of the class. 43 | /// 44 | /// Serialization info. 45 | /// Streaming context. 46 | #if NET8_0_OR_GREATER 47 | [Obsolete] 48 | #endif 49 | protected UnrecognizedJsonRpcMessageException(SerializationInfo info, StreamingContext context) 50 | : base(info, context) 51 | { 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/Hex.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Runtime.InteropServices; 5 | 6 | namespace StreamJsonRpc; 7 | 8 | internal static class Hex 9 | { 10 | private static readonly byte[] HexBytes = "0123456789abcdef"u8.ToArray(); 11 | private static readonly byte[] ReverseHexDigits = BuildReverseHexDigits(); 12 | 13 | internal static void Encode(ReadOnlySpan src, ref Span dest) 14 | { 15 | Span bytes = MemoryMarshal.Cast(dest); 16 | 17 | // Inspired by http://stackoverflow.com/questions/623104/c-byte-to-hex-string/3974535#3974535 18 | int lengthInNibbles = src.Length * 2; 19 | 20 | for (int i = 0; i < (lengthInNibbles & -2); i++) 21 | { 22 | int index0 = +i >> 1; 23 | var b = (byte)(src[index0] >> 4); 24 | bytes[(2 * i) + 1] = 0; 25 | bytes[2 * i++] = HexBytes[b]; 26 | 27 | b = (byte)(src[index0] & 0x0F); 28 | bytes[(2 * i) + 1] = 0; 29 | bytes[2 * i] = HexBytes[b]; 30 | } 31 | 32 | dest = dest.Slice(lengthInNibbles); 33 | } 34 | 35 | internal static void Decode(ReadOnlySpan value, Span bytes) 36 | { 37 | for (int i = 0; i < value.Length; i++) 38 | { 39 | int c1 = ReverseHexDigits[value[i++] - '0'] << 4; 40 | int c2 = ReverseHexDigits[value[i] - '0']; 41 | 42 | bytes[i >> 1] = (byte)(c1 + c2); 43 | } 44 | } 45 | 46 | private static byte[] BuildReverseHexDigits() 47 | { 48 | var bytes = new byte['f' - '0' + 1]; 49 | 50 | for (int i = 0; i < 10; i++) 51 | { 52 | bytes[i] = (byte)i; 53 | } 54 | 55 | for (int i = 10; i < 16; i++) 56 | { 57 | bytes[i + 'a' - '0' - 0x0a] = (byte)i; 58 | bytes[i + 'A' - '0' - 0x0a] = (byte)i; 59 | } 60 | 61 | return bytes; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/IActivityTracingStrategy.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using StreamJsonRpc.Protocol; 5 | 6 | namespace StreamJsonRpc; 7 | 8 | /// 9 | /// Synchronizes contextual activities between an RPC client and server 10 | /// consistent with the W3C Trace Context specification. 11 | /// 12 | /// 13 | /// Implementations are required to be thread-safe. 14 | /// 15 | public interface IActivityTracingStrategy 16 | { 17 | /// 18 | /// Applies a contextual activity to an outbound RPC request. 19 | /// 20 | /// The outbound RPC request. 21 | /// 22 | /// This method may be invoked regardless of whether a contextual activity exists. 23 | /// 24 | void ApplyOutboundActivity(JsonRpcRequest request); 25 | 26 | /// 27 | /// Applies an activity described in an incoming RPC request to the current context so the dispatched method can inherit it. 28 | /// 29 | /// The inbound RPC request. 30 | /// An optional disposable object that can revert the effects taken by this method at the conclusion of the dispatched RPC server method. 31 | /// 32 | /// This method may be invoked regardless of whether an activity is identified in the incoming request. 33 | /// The may be referenced by the returned value if necessary. 34 | /// 35 | IDisposable? ApplyInboundActivity(JsonRpcRequest request); 36 | } 37 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/IJsonRpcAsyncMessageFormatter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Buffers; 5 | using System.IO.Pipelines; 6 | using StreamJsonRpc.Protocol; 7 | 8 | namespace StreamJsonRpc; 9 | 10 | /// 11 | /// An interface that offers serialization to an and asynchronous deserialization. 12 | /// 13 | public interface IJsonRpcAsyncMessageFormatter : IJsonRpcMessageFormatter 14 | { 15 | /// 16 | /// Deserializes a . 17 | /// 18 | /// The reader to deserialize from. 19 | /// A cancellation token. 20 | /// The deserialized . 21 | ValueTask DeserializeAsync(PipeReader reader, CancellationToken cancellationToken); 22 | } 23 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/IJsonRpcAsyncMessageTextFormatter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Buffers; 5 | using System.IO.Pipelines; 6 | using System.Text; 7 | using StreamJsonRpc.Protocol; 8 | 9 | namespace StreamJsonRpc; 10 | 11 | /// 12 | /// An interface that offers serialization to an and asynchronous deserialization 13 | /// and formats messages as JSON (text). 14 | /// 15 | public interface IJsonRpcAsyncMessageTextFormatter : IJsonRpcAsyncMessageFormatter, IJsonRpcMessageTextFormatter 16 | { 17 | /// 18 | /// Deserializes a sequence of bytes to a . 19 | /// 20 | /// The reader to deserialize from. 21 | /// The encoding to read the bytes from with. Must not be null. 22 | /// A cancellation token. 23 | /// The deserialized message. 24 | ValueTask DeserializeAsync(PipeReader reader, Encoding encoding, CancellationToken cancellationToken); 25 | } 26 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/IJsonRpcClientProxy.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace StreamJsonRpc; 5 | 6 | /// 7 | /// Implemented by dynamically generated proxies returned from and its overloads 8 | /// to provide access to additional JSON-RPC functionality. 9 | /// 10 | public interface IJsonRpcClientProxy : IDisposable 11 | { 12 | /// 13 | /// Gets the instance behind this proxy. 14 | /// 15 | JsonRpc JsonRpc { get; } 16 | } 17 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/IJsonRpcClientProxyInternal.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace StreamJsonRpc; 5 | 6 | /// 7 | /// An interface implemented by dynamic proxies generated by . 8 | /// 9 | internal interface IJsonRpcClientProxyInternal : IJsonRpcClientProxy, IDisposableObservable 10 | { 11 | /// 12 | /// Occurs when a method is invoked on the proxy but before it is sent over RPC. 13 | /// 14 | /// 15 | /// The event arg is the CLR name of the method being invoked. 16 | /// Exceptions thrown from the handler propagate directly to the caller of the proxy and prevent the RPC request from being transmitted. 17 | /// 18 | event EventHandler CallingMethod; 19 | 20 | /// 21 | /// Occurs after a method is invoke and transmission over RPC has been queued. 22 | /// 23 | /// 24 | /// The event arg is the CLR name of the method that was invoked. 25 | /// Exceptions thrown from the handler propagate directly to the caller of the proxy, although the RPC request was already transmitted. 26 | /// 27 | event EventHandler CalledMethod; 28 | 29 | /// 30 | /// Gets the handle of the object that is being marshaled over RPC for which this is a proxy, if applicable. 31 | /// 32 | long? MarshaledObjectHandle { get; } 33 | } 34 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/IJsonRpcInstanceContainer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace StreamJsonRpc; 5 | 6 | /// 7 | /// Interface optionally implemented by implementations that need a reference to their owner class. 8 | /// 9 | public interface IJsonRpcInstanceContainer 10 | { 11 | /// 12 | /// Sets the instance. 13 | /// 14 | /// May be thrown when set more than once. 15 | JsonRpc Rpc { set; } 16 | } 17 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/IJsonRpcMessageFactory.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using StreamJsonRpc.Protocol; 5 | 6 | namespace StreamJsonRpc; 7 | 8 | /// 9 | /// An interface that allows instances to act as a factory for -derived types. 10 | /// 11 | public interface IJsonRpcMessageFactory 12 | { 13 | /// 14 | /// Creates an instance of suitable for transmission over the . 15 | /// 16 | /// An instance of . 17 | JsonRpcRequest CreateRequestMessage(); 18 | 19 | /// 20 | /// Creates an instance of suitable for transmission over the . 21 | /// 22 | /// An instance of . 23 | JsonRpcError CreateErrorMessage(); 24 | 25 | /// 26 | /// Creates an instance of suitable for transmission over the . 27 | /// 28 | /// An instance of . 29 | JsonRpcResult CreateResultMessage(); 30 | } 31 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/IJsonRpcMessageFormatter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Buffers; 5 | using StreamJsonRpc.Protocol; 6 | using StreamJsonRpc.Reflection; 7 | 8 | namespace StreamJsonRpc; 9 | 10 | /// 11 | /// An interface that offers serialization to and from a sequence of bytes. 12 | /// 13 | public interface IJsonRpcMessageFormatter 14 | { 15 | /// 16 | /// Deserializes a . 17 | /// 18 | /// A sequence of bytes to deserialize. 19 | /// The deserialized . 20 | JsonRpcMessage Deserialize(ReadOnlySequence contentBuffer); 21 | 22 | /// 23 | /// Serializes a . 24 | /// 25 | /// The receiver of the serialized bytes. 26 | /// The message to serialize. 27 | void Serialize(IBufferWriter bufferWriter, JsonRpcMessage message); 28 | 29 | /// 30 | /// Gets a JSON representation for a given message for tracing purposes. 31 | /// 32 | /// The message to be traced. 33 | /// Any object whose method will produce a human-readable JSON string, suitable for tracing. 34 | [Obsolete("Tracing is now done via the " + nameof(IJsonRpcTracingCallbacks) + " and " + nameof(IJsonRpcFormatterTracingCallbacks) + " interfaces. Formatters may throw NotSupportedException from this method.")] 35 | object GetJsonText(JsonRpcMessage message); 36 | } 37 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/IJsonRpcMessageTextFormatter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Buffers; 5 | using System.Text; 6 | using StreamJsonRpc.Protocol; 7 | 8 | namespace StreamJsonRpc; 9 | 10 | /// 11 | /// An that formats messages as JSON (text). 12 | /// 13 | public interface IJsonRpcMessageTextFormatter : IJsonRpcMessageFormatter 14 | { 15 | /// 16 | /// Gets or sets the encoding used for serialization for methods that do not take an explicit . 17 | /// 18 | /// Never null. 19 | /// Thrown at an attempt to set the value to null. 20 | Encoding Encoding { get; set; } 21 | 22 | /// 23 | /// Deserializes a sequence of bytes to a . 24 | /// 25 | /// The bytes to deserialize. 26 | /// The encoding to read the bytes in with. Must not be null. 27 | /// The deserialized message. 28 | JsonRpcMessage Deserialize(ReadOnlySequence contentBuffer, Encoding encoding); 29 | } 30 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/IRpcMarshaledContext.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace StreamJsonRpc; 5 | 6 | /// 7 | /// Represents an object to be marshaled and provides check or influence its lifetime. 8 | /// 9 | /// The interface type whose methods are exposed for RPC invocation on the target object. 10 | /// 11 | /// When an object implementing this interface is disposed 12 | /// the marshaling relationship is immediately terminated. 13 | /// This releases resources allocated to facilitating the marshaling of the object 14 | /// and prevents any further invocations of the object by the remote party. 15 | /// If the underlying object implements then its 16 | /// method is also invoked. 17 | /// 18 | /// 19 | /// This type is an interface rather than a class so that users can enjoy covariance on the generic type parameter. 20 | /// 21 | internal interface IRpcMarshaledContext : IDisposableObservable 22 | where T : class 23 | { 24 | /// 25 | /// Occurs when the marshalling relationship is released, 26 | /// whether by a local call to 27 | /// or at the remote party's request. 28 | /// 29 | event EventHandler? Disposed; 30 | 31 | /// 32 | /// Gets the marshalable proxy that should be used in the RPC message. 33 | /// 34 | T Proxy { get; } 35 | 36 | /// 37 | /// Gets the to associate with this object when it becomes a RPC target. 38 | /// 39 | JsonRpcTargetOptions JsonRpcTargetOptions { get; } 40 | } 41 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/OptProf.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | IBC 5 | Common7\IDE\PublicAssemblies\StreamJsonRpc.2.x\$(TargetFileName) 6 | /ExeConfig:"%VisualStudio.InstallationUnderTest.Path%\Common7\IDE\vsn.exe" 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Resources; 5 | 6 | [assembly: NeutralResourcesLanguage("en-US")] 7 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/Protocol/Constants.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable SA1303 // Const field names should begin with upper-case letter 2 | 3 | namespace StreamJsonRpc.Protocol; 4 | 5 | internal static class Constants 6 | { 7 | internal const string jsonrpc = "jsonrpc"; 8 | internal const string id = "id"; 9 | 10 | internal static class Request 11 | { 12 | internal const string method = "method"; 13 | internal const string @params = "params"; 14 | internal const string traceparent = "traceparent"; 15 | internal const string tracestate = "tracestate"; 16 | 17 | internal static bool IsPropertyReserved(string propertyName) 18 | { 19 | Requires.NotNull(propertyName, nameof(propertyName)); 20 | 21 | return 22 | propertyName.Equals(jsonrpc, StringComparison.Ordinal) || 23 | propertyName.Equals(method, StringComparison.Ordinal) || 24 | propertyName.Equals(@params, StringComparison.Ordinal) || 25 | propertyName.Equals(traceparent, StringComparison.Ordinal) || 26 | propertyName.Equals(tracestate, StringComparison.Ordinal) || 27 | propertyName.Equals(id, StringComparison.Ordinal); 28 | } 29 | } 30 | 31 | internal static class Result 32 | { 33 | internal const string result = "result"; 34 | 35 | internal static bool IsPropertyReserved(string propertyName) 36 | { 37 | Requires.NotNull(propertyName, nameof(propertyName)); 38 | 39 | return 40 | propertyName.Equals(jsonrpc, StringComparison.Ordinal) || 41 | propertyName.Equals(result, StringComparison.Ordinal) || 42 | propertyName.Equals(id, StringComparison.Ordinal); 43 | } 44 | } 45 | 46 | internal static class Error 47 | { 48 | internal const string error = "error"; 49 | 50 | internal static bool IsPropertyReserved(string propertyName) 51 | { 52 | Requires.NotNull(propertyName, nameof(propertyName)); 53 | 54 | return 55 | propertyName.Equals(jsonrpc, StringComparison.Ordinal) || 56 | propertyName.Equals(error, StringComparison.Ordinal) || 57 | propertyName.Equals(id, StringComparison.Ordinal); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/Protocol/IJsonRpcMessageWithId.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace StreamJsonRpc.Protocol; 5 | 6 | /// 7 | /// An interface found on JSON-RPC protocol messages that contain an id field. 8 | /// 9 | public interface IJsonRpcMessageWithId 10 | { 11 | /// 12 | /// Gets or sets the ID on a message. 13 | /// 14 | RequestId RequestId { get; set; } 15 | } 16 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/README.md: -------------------------------------------------------------------------------- 1 | # StreamJsonRpc 2 | 3 | StreamJsonRpc is a cross-platform, .NET portable library that implements the 4 | [JSON-RPC][JSONRPC] wire protocol. 5 | 6 | It works over [Stream](https://docs.microsoft.com/dotnet/api/system.io.stream), [WebSocket](https://docs.microsoft.com/dotnet/api/system.net.websockets.websocket), or System.IO.Pipelines pipes, independent of the underlying transport. 7 | 8 | Bonus features beyond the JSON-RPC spec include: 9 | 10 | 1. Request cancellation 11 | 1. .NET Events as notifications 12 | 1. Dynamic client proxy generation 13 | 1. Support for [compact binary serialization](https://microsoft.github.io/vs-streamjsonrpc/docs/extensibility.html) via MessagePack 14 | 1. Pluggable architecture for custom message handling and formatting. 15 | 16 | Learn about the use cases for JSON-RPC and how to use this library from our [documentation](https://microsoft.github.io/vs-streamjsonrpc/docs/getting-started.html). 17 | 18 | ## Compatibility 19 | 20 | This library has been tested with and is compatible with the following other 21 | JSON-RPC libraries: 22 | 23 | * [json-rpc-peer][json-rpc-peer] (npm) 24 | * [vscode-jsonrpc][vscode-jsonrpc] (npm) 25 | 26 | [JSONRPC]: https://www.jsonrpc.org/ 27 | [json-rpc-peer]: https://www.npmjs.com/package/json-rpc-peer 28 | [vscode-jsonrpc]: https://www.npmjs.com/package/vscode-jsonrpc 29 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/Reflection/CodeGenHelpers.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.ComponentModel; 5 | 6 | namespace StreamJsonRpc.Reflection; 7 | 8 | /// 9 | /// Helper methods for dynamically generated proxies to invoke. 10 | /// This type is only public because mono does not support IgnoresAccessChecksToAttribute. Do not call directly. 11 | /// 12 | [EditorBrowsable(EditorBrowsableState.Never)] 13 | [Obsolete("This class is only for invoking from dynamically generated code.")] 14 | public static class CodeGenHelpers 15 | { 16 | #pragma warning disable VSTHRD200 // Use "Async" suffix in names of methods that return an awaitable type. 17 | /// 18 | public static IAsyncEnumerable CreateAsyncEnumerableProxy(Task> enumerableTask, CancellationToken defaultCancellationToken) => new ProxyGeneration.AsyncEnumerableProxy(enumerableTask, defaultCancellationToken); 19 | #pragma warning restore VSTHRD200 // Use "Async" suffix in names of methods that return an awaitable type. 20 | } 21 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/Reflection/IJsonRpcFormatterCallbacks.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace StreamJsonRpc.Reflection; 5 | 6 | /// 7 | /// Implemented by to expose callbacks allowing an to perform resource cleanup. 8 | /// 9 | public interface IJsonRpcFormatterCallbacks 10 | { 11 | /// 12 | /// Occurs when aborts the transmission of an outbound request (that was not a notification). 13 | /// 14 | /// 15 | /// This usually occurs because of an exception during serialization or transmission, possibly due to cancellation. 16 | /// 17 | event EventHandler RequestTransmissionAborted; 18 | 19 | /// 20 | /// Occurs when receives a response to a previously sent request. 21 | /// 22 | event EventHandler ResponseReceived; 23 | 24 | /// 25 | /// Occurs when transmits a response message. 26 | /// 27 | event EventHandler ResponseSent; 28 | } 29 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/Reflection/IJsonRpcFormatterState.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace StreamJsonRpc.Reflection; 5 | 6 | /// 7 | /// An interface implemented by instances 8 | /// to support some formatter extensions such as . 9 | /// 10 | public interface IJsonRpcFormatterState 11 | { 12 | /// 13 | /// Gets the id of the request or response currently being serialized. 14 | /// 15 | public RequestId SerializingMessageWithId { get; } 16 | 17 | /// 18 | /// Gets the ID of the response currently being deserialized. 19 | /// 20 | public RequestId DeserializingMessageWithId { get; } 21 | 22 | /// 23 | /// Gets a value indicating whether a is being serialized. 24 | /// 25 | /// 26 | /// A response is being serialized if this property's value is while is non-empty. 27 | /// 28 | public bool SerializingRequest { get; } 29 | } 30 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/Reflection/IJsonRpcFormatterTracingCallbacks.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Buffers; 5 | using StreamJsonRpc.Protocol; 6 | 7 | namespace StreamJsonRpc.Reflection; 8 | 9 | /// 10 | /// Optionally implemented by a when it needs the fully serialized sequence in order to trace the JSON representation of the message. 11 | /// 12 | public interface IJsonRpcFormatterTracingCallbacks 13 | { 14 | /// 15 | /// Invites the formatter to call with the JSON representation of the message just serialized.. 16 | /// 17 | /// The message that was just serialized. 18 | /// The encoded copy of the message, as it recently came from the method. 19 | void OnSerializationComplete(JsonRpcMessage message, ReadOnlySequence encodedMessage); 20 | } 21 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/Reflection/IJsonRpcMessageBufferManager.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using StreamJsonRpc.Protocol; 5 | 6 | namespace StreamJsonRpc.Reflection; 7 | 8 | /// 9 | /// An interface that may be found on an object to request notification of when 10 | /// message deserialization is completed so buffers can be released or safely recycled. 11 | /// 12 | public interface IJsonRpcMessageBufferManager 13 | { 14 | /// 15 | /// Notifies that it is safe to free buffers held to deserialize the payload for a message because all deserialization attempts are completed. 16 | /// 17 | /// The message whose deserialization is done. 18 | /// 19 | /// Implementations are guaranteed to be called at least once for each message when deserialization is completed. 20 | /// This method will be invoked before the next request to . 21 | /// 22 | void DeserializationComplete(JsonRpcMessage message); 23 | } 24 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/Reflection/IJsonRpcTracingCallbacks.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using StreamJsonRpc.Protocol; 5 | 6 | namespace StreamJsonRpc.Reflection; 7 | 8 | /// 9 | /// An interface implemented by for implementations to use to facilitate message tracing. 10 | /// 11 | public interface IJsonRpcTracingCallbacks 12 | { 13 | /// 14 | /// Occurs when the has serialized a message for transmission. 15 | /// 16 | /// The JSON-RPC message. 17 | /// The encoded form of the message. Calling on this should produce the JSON-RPC text of the message. 18 | void OnMessageSerialized(JsonRpcMessage message, object encodedMessage); 19 | 20 | /// 21 | /// Occurs when the has deserialized an incoming message. 22 | /// 23 | /// The JSON-RPC message. 24 | /// The encoded form of the message. Calling on this should produce the JSON-RPC text of the message. 25 | void OnMessageDeserialized(JsonRpcMessage message, object encodedMessage); 26 | } 27 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/Reflection/JsonRpcIgnoreAttribute.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace StreamJsonRpc; 5 | 6 | /// 7 | /// Attribute which identifies methods that should not be invokable over RPC. 8 | /// 9 | /// 10 | /// When adding an RPC target object via or other APIs, 11 | /// all public methods on the object default to being exposed to invocation by the client. 12 | /// When is set, even more methods are exposed. 13 | /// Applying this attribute to any method will ensure it can never be invoked directly by the RPC client. 14 | /// If and are applied to the same method, 15 | /// an exception will be thrown at the time of adding the object as an RPC target. 16 | /// 17 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] 18 | public class JsonRpcIgnoreAttribute : Attribute 19 | { 20 | } 21 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/Reflection/JsonRpcMessageEventArgs.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using StreamJsonRpc.Protocol; 5 | 6 | namespace StreamJsonRpc.Reflection; 7 | 8 | /// 9 | /// Carries the from request or response messages. 10 | /// 11 | public class JsonRpcMessageEventArgs : EventArgs 12 | { 13 | /// 14 | /// Initializes a new instance of the class. 15 | /// 16 | /// The ID from the request or response that the event is regarding. 17 | public JsonRpcMessageEventArgs(RequestId requestId) 18 | { 19 | Requires.Argument(!requestId.IsEmpty, nameof(requestId), "Non-default ID required."); 20 | this.RequestId = requestId; 21 | } 22 | 23 | /// 24 | /// Initializes a new instance of the class. 25 | /// 26 | /// The message the event is regarding. 27 | internal JsonRpcMessageEventArgs(IJsonRpcMessageWithId message) 28 | { 29 | Requires.NotNull(message, nameof(message)); 30 | Requires.Argument(!message.RequestId.IsEmpty, nameof(message), "Non-default ID required."); 31 | this.RequestId = message.RequestId; 32 | } 33 | 34 | /// 35 | /// Gets the id on the request, result or error. 36 | /// 37 | public RequestId RequestId { get; private set; } 38 | } 39 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/Reflection/JsonRpcResponseEventArgs.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using StreamJsonRpc.Protocol; 5 | 6 | namespace StreamJsonRpc.Reflection; 7 | 8 | /// 9 | /// Carries the and success status of response messages. 10 | /// 11 | public class JsonRpcResponseEventArgs : JsonRpcMessageEventArgs 12 | { 13 | /// 14 | /// Initializes a new instance of the class. 15 | /// 16 | /// The ID from the request or response that the event is regarding. 17 | /// A flag indicating whether the response is a result (as opposed to an error). 18 | public JsonRpcResponseEventArgs(RequestId requestId, bool isSuccessfulResponse) 19 | : base(requestId) 20 | { 21 | this.IsSuccessfulResponse = isSuccessfulResponse; 22 | } 23 | 24 | /// 25 | /// Initializes a new instance of the class. 26 | /// 27 | /// The message the event is regarding. 28 | internal JsonRpcResponseEventArgs(IJsonRpcMessageWithId message) 29 | : this(message.RequestId, message is JsonRpcResult) 30 | { 31 | } 32 | 33 | /// 34 | /// Gets a value indicating whether the response is a result (as opposed to an error). 35 | /// 36 | public bool IsSuccessfulResponse { get; } 37 | } 38 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/Reflection/MethodSignatureAndTarget.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Diagnostics; 5 | using System.Diagnostics.CodeAnalysis; 6 | using System.Reflection; 7 | using System.Runtime.CompilerServices; 8 | 9 | namespace StreamJsonRpc; 10 | 11 | [DebuggerDisplay("{" + nameof(DebuggerDisplay) + ",nq}")] 12 | internal struct MethodSignatureAndTarget : IEquatable 13 | { 14 | internal MethodSignatureAndTarget(MethodInfo method, object? target, JsonRpcMethodAttribute? attribute, SynchronizationContext? perMethodSynchronizationContext) 15 | { 16 | Requires.NotNull(method, nameof(method)); 17 | 18 | this.Signature = new MethodSignature(method, attribute); 19 | this.Target = target; 20 | this.SynchronizationContext = perMethodSynchronizationContext; 21 | } 22 | 23 | internal MethodSignatureAndTarget(MethodSignature signature, object? target, SynchronizationContext? perMethodSynchronizationContext) 24 | { 25 | this.Signature = signature; 26 | this.Target = target; 27 | this.SynchronizationContext = perMethodSynchronizationContext; 28 | } 29 | 30 | internal MethodSignature Signature { get; } 31 | 32 | internal object? Target { get; } 33 | 34 | internal SynchronizationContext? SynchronizationContext { get; } 35 | 36 | [ExcludeFromCodeCoverage] 37 | private string DebuggerDisplay => this.ToString(); 38 | 39 | /// 40 | public override bool Equals(object? obj) 41 | { 42 | return obj is MethodSignatureAndTarget other 43 | && this.Equals(other); 44 | } 45 | 46 | /// 47 | public bool Equals(MethodSignatureAndTarget other) 48 | { 49 | return this.Signature.Equals(other.Signature) 50 | && object.ReferenceEquals(this.Target, other.Target); 51 | } 52 | 53 | /// 54 | public override int GetHashCode() 55 | { 56 | return this.Signature.GetHashCode() + (this.Target is not null ? RuntimeHelpers.GetHashCode(this.Target) : 0); 57 | } 58 | 59 | /// 60 | public override string ToString() => $"{this.Signature} ({this.Target})"; 61 | } 62 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/Reflection/RpcMarshalableAttribute.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace StreamJsonRpc; 5 | 6 | /// 7 | /// Designates an interface that is used in an RPC contract to marshal the object so the receiver can invoke remote methods on it instead of serializing the object to send its data to the remote end. 8 | /// 9 | /// 10 | /// Learn more about marshable interfaces. 11 | /// 12 | [AttributeUsage(AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] 13 | public class RpcMarshalableAttribute : Attribute 14 | { 15 | /// 16 | /// Gets a value indicating whether the marshaled object is only allowed in requests 17 | /// and may only be invoked by the receiver until the response is sent. 18 | /// 19 | /// 20 | /// Objects marshaled via an interface attributed with this property set to true may only be used as RPC method parameters. 21 | /// They will not be allowed as return values from RPC methods. 22 | /// While the receiver may dispose of the proxy they receive, this disposal will not propagate to the sender, 23 | /// and their originating object will not be disposed of. 24 | /// The original object owner retains ownership of the lifetime of the object after the RPC call. 25 | /// 26 | public bool CallScopedLifetime { get; init; } 27 | } 28 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/RequestIdJsonConverter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Newtonsoft.Json; 5 | 6 | namespace StreamJsonRpc; 7 | 8 | #pragma warning disable CA1812 9 | internal class RequestIdJsonConverter : JsonConverter 10 | #pragma warning restore CA1812 11 | { 12 | public override RequestId ReadJson(JsonReader reader, Type objectType, RequestId existingValue, bool hasExistingValue, JsonSerializer serializer) 13 | { 14 | switch (reader.TokenType) 15 | { 16 | case JsonToken.Integer: return new RequestId(reader.Value is int i ? i : (long)reader.Value!); 17 | case JsonToken.String: return new RequestId((string?)reader.Value); 18 | case JsonToken.Null: return RequestId.Null; 19 | default: throw new JsonSerializationException("Unexpected token type for request ID: " + reader.TokenType); 20 | } 21 | } 22 | 23 | public override void WriteJson(JsonWriter writer, RequestId value, JsonSerializer serializer) 24 | { 25 | if (value.Number.HasValue) 26 | { 27 | writer.WriteValue(value.Number.Value); 28 | } 29 | else 30 | { 31 | writer.WriteValue(value.String); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/RpcMarshaledContext.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace StreamJsonRpc; 5 | 6 | /// 7 | internal class RpcMarshaledContext : IRpcMarshaledContext 8 | where T : class 9 | { 10 | /// 11 | /// Initializes a new instance of the class. 12 | /// 13 | /// The value that should be used in the object graph to be sent over RPC, to trigger marshaling. 14 | /// The to use when adding this object as an RPC target. 15 | internal RpcMarshaledContext(T value, JsonRpcTargetOptions options) 16 | { 17 | // We shouldn't reach this point with a proxy. 18 | Requires.Argument(value is not IJsonRpcClientProxyInternal, nameof(value), "Cannot marshal a proxy."); 19 | 20 | this.Proxy = value; 21 | this.JsonRpcTargetOptions = options; 22 | } 23 | 24 | /// 25 | public event EventHandler? Disposed; 26 | 27 | /// 28 | public T Proxy { get; private set; } 29 | 30 | /// 31 | public bool IsDisposed { get; private set; } 32 | 33 | /// 34 | public JsonRpcTargetOptions JsonRpcTargetOptions { get; } 35 | 36 | /// 37 | /// Immediately terminates the marshaling relationship. 38 | /// This releases resources allocated to facilitating the marshaling of the object 39 | /// and prevents any further invocations of the object by the remote party. 40 | /// If the underlying object implements then its 41 | /// method is also invoked. 42 | /// 43 | public void Dispose() 44 | { 45 | throw new NotImplementedException(); 46 | #pragma warning disable CS0162 // Unreachable code detected 47 | this.IsDisposed = true; 48 | this.Disposed?.Invoke(this, EventArgs.Empty); 49 | #pragma warning restore CS0162 // Unreachable code detected 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/SharedUtilities.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace StreamJsonRpc; 5 | 6 | /// 7 | /// Utilities that are source-shared between the library and its tests. 8 | /// 9 | internal static class SharedUtilities 10 | { 11 | /// 12 | /// The various modes that can be used to test the class. 13 | /// 14 | internal enum EventSourceTestMode 15 | { 16 | /// 17 | /// ETW events are not forced on. 18 | /// 19 | None, 20 | 21 | /// 22 | /// ETW events are forced on and exceptions are swallowed as they would be in production. 23 | /// 24 | EmulateProduction, 25 | 26 | /// 27 | /// ETW events are forced on and exceptions are not swallowed, allowing tests to detect errors in ETW logging. 28 | /// 29 | DoNotSwallowExceptions, 30 | } 31 | 32 | internal static EventSourceTestMode GetEventSourceTestMode() => Environment.GetEnvironmentVariable("StreamJsonRpc_TestWithEventSource") switch 33 | { 34 | "1" => EventSourceTestMode.EmulateProduction, 35 | "2" => EventSourceTestMode.DoNotSwallowExceptions, 36 | _ => EventSourceTestMode.None, 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/StreamJsonRpc.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netstandard2.0;netstandard2.1;net8.0 4 | prompt 5 | 4 6 | true 7 | A cross-platform .NETStandard library that implements the JSON-RPC wire protocol and can use System.IO.Stream, System.IO.Pipelines or WebSocket so you can use it with any transport. 8 | visualstudio stream json rpc jsonrpc 9 | 10 | $(NoWarn);SYSLIB0050 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/StreamJsonRpc/net8.0/PublicAPI.Unshipped.txt: -------------------------------------------------------------------------------- 1 | StreamJsonRpc.JsonRpc.Attach(System.ReadOnlySpan interfaceTypes, StreamJsonRpc.JsonRpcProxyOptions? options) -> object! -------------------------------------------------------------------------------- /src/StreamJsonRpc/netstandard2.0/PublicAPI.Unshipped.txt: -------------------------------------------------------------------------------- 1 | StreamJsonRpc.JsonRpc.Attach(System.ReadOnlySpan interfaceTypes, StreamJsonRpc.JsonRpcProxyOptions? options) -> object! -------------------------------------------------------------------------------- /src/StreamJsonRpc/netstandard2.1/PublicAPI.Unshipped.txt: -------------------------------------------------------------------------------- 1 | StreamJsonRpc.JsonRpc.Attach(System.ReadOnlySpan interfaceTypes, StreamJsonRpc.JsonRpcProxyOptions? options) -> object! -------------------------------------------------------------------------------- /src/VSInsertionMetadata/ProfilingInputs.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/VSInsertionMetadata/StreamJsonRpc.VSInsertionMetadata.proj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0 4 | true 5 | $(RepoRootPath)bin\Packages\$(Configuration)\VSRepo\ 6 | false 7 | false 8 | Contains metadata for insertion into VS. 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 3 | "settings": { 4 | "documentationRules": { 5 | "companyName": "Microsoft Corporation", 6 | "copyrightText": "Copyright (c) {companyName}. All rights reserved.\nLicensed under the {licenseName} license. See {licenseFile} file in the project root for full license information.", 7 | "variables": { 8 | "licenseName": "MIT", 9 | "licenseFile": "LICENSE" 10 | }, 11 | "fileNamingConvention": "stylecop", 12 | "xmlHeader": false 13 | }, 14 | "namingRules": { 15 | "tupleElementNameCasing": "PascalCase" 16 | }, 17 | "orderingRules": { 18 | "usingDirectivesPlacement": "outsideNamespace" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # SA1600: Elements should be documented 4 | dotnet_diagnostic.SA1600.severity = silent 5 | 6 | # SA1601: Partial elements should be documented 7 | dotnet_diagnostic.SA1601.severity = silent 8 | 9 | # SA1602: Enumeration items should be documented 10 | dotnet_diagnostic.SA1602.severity = silent 11 | 12 | # SA1615: Element return value should be documented 13 | dotnet_diagnostic.SA1615.severity = silent 14 | 15 | dotnet_diagnostic.VSTHRD001.severity = none 16 | dotnet_diagnostic.VSTHRD002.severity = silent 17 | dotnet_diagnostic.VSTHRD003.severity = none 18 | dotnet_diagnostic.VSTHRD012.severity = none 19 | 20 | # VSTHRD103: Call async methods when in an async method 21 | dotnet_diagnostic.VSTHRD103.severity = silent 22 | 23 | # VSTHRD111: Use .ConfigureAwait(bool) 24 | dotnet_diagnostic.VSTHRD111.severity = none 25 | 26 | # VSTHRD200: Use Async suffix for async methods 27 | dotnet_diagnostic.VSTHRD200.severity = silent 28 | 29 | # CA1014: Mark assemblies with CLSCompliant 30 | dotnet_diagnostic.CA1014.severity = none 31 | 32 | # CA1050: Declare types in namespaces 33 | dotnet_diagnostic.CA1050.severity = none 34 | 35 | # CA1303: Do not pass literals as localized parameters 36 | dotnet_diagnostic.CA1303.severity = none 37 | 38 | # CS1591: Missing XML comment for publicly visible type or member 39 | dotnet_diagnostic.CS1591.severity = silent 40 | 41 | # CA1707: Identifiers should not contain underscores 42 | dotnet_diagnostic.CA1707.severity = silent 43 | 44 | # CA1062: Validate arguments of public methods 45 | dotnet_diagnostic.CA1062.severity = suggestion 46 | 47 | # CA1063: Implement IDisposable Correctly 48 | dotnet_diagnostic.CA1063.severity = silent 49 | 50 | # CA1816: Dispose methods should call SuppressFinalize 51 | dotnet_diagnostic.CA1816.severity = silent 52 | 53 | # CA2007: Consider calling ConfigureAwait on the awaited task 54 | dotnet_diagnostic.CA2007.severity = none 55 | 56 | # SA1401: Fields should be private 57 | dotnet_diagnostic.SA1401.severity = silent 58 | 59 | # SA1133: Do not combine attributes 60 | dotnet_diagnostic.SA1133.severity = silent 61 | -------------------------------------------------------------------------------- /test/Benchmarks/.gitignore: -------------------------------------------------------------------------------- 1 | BenchmarkDotNet.Artifacts/ 2 | -------------------------------------------------------------------------------- /test/Benchmarks/Benchmarks.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0;net472 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/Benchmarks/InvokeBenchmarks.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.IO.Pipelines; 5 | using BenchmarkDotNet.Attributes; 6 | using Microsoft; 7 | using Nerdbank.Streams; 8 | using StreamJsonRpc; 9 | 10 | namespace Benchmarks; 11 | 12 | [MemoryDiagnoser] 13 | public class InvokeBenchmarks 14 | { 15 | private JsonRpc clientRpc = null!; 16 | private JsonRpc serverRpc = null!; 17 | 18 | [Params("JSON", "MessagePack")] 19 | public string Formatter { get; set; } = null!; 20 | 21 | [GlobalSetup] 22 | public void Setup() 23 | { 24 | (IDuplexPipe, IDuplexPipe) duplex = FullDuplexStream.CreatePipePair(); 25 | this.clientRpc = new JsonRpc(CreateHandler(duplex.Item1)); 26 | this.clientRpc.StartListening(); 27 | 28 | this.serverRpc = new JsonRpc(CreateHandler(duplex.Item2)); 29 | this.serverRpc.AddLocalRpcTarget(new Server()); 30 | this.serverRpc.StartListening(); 31 | 32 | IJsonRpcMessageHandler CreateHandler(IDuplexPipe pipe) 33 | { 34 | return this.Formatter switch 35 | { 36 | "JSON" => new HeaderDelimitedMessageHandler(pipe, new JsonMessageFormatter()), 37 | "MessagePack" => new LengthHeaderMessageHandler(pipe, new MessagePackFormatter()), 38 | _ => throw Assumes.NotReachable(), 39 | }; 40 | } 41 | } 42 | 43 | [Benchmark] 44 | public Task InvokeAsync_NoArgs() => this.clientRpc.InvokeAsync(nameof(Server.NoOp), Array.Empty()); 45 | 46 | private class Server 47 | { 48 | public void NoOp() 49 | { 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/Benchmarks/NotifyBenchmarks.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using BenchmarkDotNet.Attributes; 5 | using Microsoft; 6 | using StreamJsonRpc; 7 | 8 | namespace Benchmarks; 9 | 10 | [MemoryDiagnoser] 11 | public class NotifyBenchmarks 12 | { 13 | private JsonRpc clientRpc = null!; 14 | 15 | [Params("JSON", "MessagePack")] 16 | public string Formatter { get; set; } = null!; 17 | 18 | [GlobalSetup] 19 | public void Setup() 20 | { 21 | this.clientRpc = new JsonRpc(CreateHandler(Stream.Null)); 22 | 23 | IJsonRpcMessageHandler CreateHandler(Stream pipe) 24 | { 25 | return this.Formatter switch 26 | { 27 | "JSON" => new HeaderDelimitedMessageHandler(pipe, new JsonMessageFormatter()), 28 | "MessagePack" => new LengthHeaderMessageHandler(pipe, pipe, new MessagePackFormatter()), 29 | _ => throw Assumes.NotReachable(), 30 | }; 31 | } 32 | } 33 | 34 | [Benchmark] 35 | public Task NotifyAsync_NoArgs() => this.clientRpc.NotifyAsync("NoOp", Array.Empty()); 36 | } 37 | -------------------------------------------------------------------------------- /test/Benchmarks/Program.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using BenchmarkDotNet.Configs; 5 | using BenchmarkDotNet.Reports; 6 | using BenchmarkDotNet.Running; 7 | 8 | namespace Benchmarks; 9 | 10 | internal static class Program 11 | { 12 | private static async Task Main(string[] args) 13 | { 14 | // Allow a special "manual" argument for convenient perfview.exe-monitored runs for GC pressure analysis. 15 | if (args.Length == 1 && args[0] == "manual") 16 | { 17 | var b = new InvokeBenchmarks { Formatter = "MessagePack" }; 18 | b.Setup(); 19 | await b.InvokeAsync_NoArgs(); 20 | 21 | await Task.Delay(2000); 22 | 23 | for (int i = 0; i < 1000; i++) 24 | { 25 | await b.InvokeAsync_NoArgs(); 26 | } 27 | } 28 | else 29 | { 30 | IConfig? config = null; 31 | #if DEBUG 32 | config = new DebugInProcessConfig(); 33 | #endif 34 | IEnumerable? summaries = BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, config); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/Benchmarks/ShortLivedConnectionBenchmarks.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.IO.Pipelines; 5 | using BenchmarkDotNet.Attributes; 6 | using Microsoft; 7 | using Nerdbank.Streams; 8 | using StreamJsonRpc; 9 | 10 | namespace Benchmarks; 11 | 12 | [MemoryDiagnoser] 13 | public class ShortLivedConnectionBenchmarks 14 | { 15 | private const int Iterations = 1000; 16 | 17 | [Params("JSON", "MessagePack")] 18 | public string Formatter { get; set; } = null!; 19 | 20 | [Benchmark(OperationsPerInvoke = Iterations)] 21 | public async Task CreateConnectionAndInvokeOnce() 22 | { 23 | for (int i = 0; i < Iterations; i++) 24 | { 25 | (IDuplexPipe, IDuplexPipe) duplex = FullDuplexStream.CreatePipePair(); 26 | using var clientRpc = new JsonRpc(CreateHandler(duplex.Item1)); 27 | clientRpc.StartListening(); 28 | 29 | using var serverRpc = new JsonRpc(CreateHandler(duplex.Item2)); 30 | serverRpc.AddLocalRpcTarget(new Server()); 31 | serverRpc.StartListening(); 32 | 33 | await clientRpc.InvokeAsync(nameof(Server.NoOp), Array.Empty()); 34 | } 35 | 36 | IJsonRpcMessageHandler CreateHandler(IDuplexPipe pipe) 37 | { 38 | return this.Formatter switch 39 | { 40 | "JSON" => new HeaderDelimitedMessageHandler(pipe, new JsonMessageFormatter()), 41 | "MessagePack" => new LengthHeaderMessageHandler(pipe, new MessagePackFormatter()), 42 | _ => throw Assumes.NotReachable(), 43 | }; 44 | } 45 | } 46 | 47 | private class Server 48 | { 49 | public void NoOp() 50 | { 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /test/Benchmarks/run.cmd: -------------------------------------------------------------------------------- 1 | @pushd "%~dp0\" 2 | dotnet run -f net8.0 -c release -- --runtimes net472 net8.0 %* 3 | @popd 4 | -------------------------------------------------------------------------------- /test/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | false 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests.ExternalAssembly/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("StreamJsonRpc.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] 4 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests.ExternalAssembly/IInternalGenericInterface.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace StreamJsonRpc.Tests.ExternalAssembly 5 | { 6 | internal interface IInternalGenericInterface 7 | { 8 | Task GetOptionsAsync(InternalStruct id, CancellationToken cancellationToken); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests.ExternalAssembly/IPublicNestedInInternalInterface.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma warning disable SA1649 // File name should match first type name 5 | 6 | namespace StreamJsonRpc.Tests.ExternalAssembly; 7 | 8 | internal interface IInternal 9 | { 10 | public interface IPublicNestedInInternalInterface 11 | { 12 | Task SubtractAsync(int a, int b); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests.ExternalAssembly/ISomeInternalProxyInterface.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace StreamJsonRpc.Tests.ExternalAssembly; 5 | 6 | internal interface ISomeInternalProxyInterface 7 | { 8 | Task SubtractAsync(int a, int b); 9 | } 10 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests.ExternalAssembly/InternalStruct.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace StreamJsonRpc.Tests.ExternalAssembly; 5 | 6 | internal struct InternalStruct 7 | { 8 | } 9 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests.ExternalAssembly/SomeOtherInternalType.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace StreamJsonRpc.Tests.ExternalAssembly; 5 | 6 | internal class SomeOtherInternalType 7 | { 8 | } 9 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests.ExternalAssembly/StreamJsonRpc.Tests.ExternalAssembly.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | true 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # Allow tests to use `var` even when the type isn't apparent 4 | csharp_style_var_elsewhere = false:silent 5 | 6 | # SA1600: Elements should be documented 7 | dotnet_diagnostic.SA1600.severity = none 8 | 9 | # SA1133: Do not combine attributes 10 | dotnet_diagnostic.SA1133.severity = none 11 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/AsyncEnumerableJsonTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | public class AsyncEnumerableJsonTests : AsyncEnumerableTests 5 | { 6 | public AsyncEnumerableJsonTests(ITestOutputHelper logger) 7 | : base(logger) 8 | { 9 | } 10 | 11 | protected override void InitializeFormattersAndHandlers() 12 | { 13 | this.serverMessageFormatter = new JsonMessageFormatter(); 14 | this.clientMessageFormatter = new JsonMessageFormatter(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/AsyncEnumerableJsonTypeHandlingTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Newtonsoft.Json; 5 | 6 | public class AsyncEnumerableJsonTypeHandlingTests : AsyncEnumerableJsonTests 7 | { 8 | public AsyncEnumerableJsonTypeHandlingTests(ITestOutputHelper logger) 9 | : base(logger) 10 | { 11 | } 12 | 13 | protected override void InitializeFormattersAndHandlers() 14 | { 15 | this.serverMessageFormatter = new JsonMessageFormatter 16 | { 17 | JsonSerializer = 18 | { 19 | TypeNameHandling = TypeNameHandling.Objects, 20 | TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple, 21 | }, 22 | }; 23 | 24 | this.clientMessageFormatter = new JsonMessageFormatter 25 | { 26 | JsonSerializer = 27 | { 28 | TypeNameHandling = TypeNameHandling.Objects, 29 | TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple, 30 | }, 31 | }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/AsyncEnumerableMessagePackTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | public class AsyncEnumerableMessagePackTests : AsyncEnumerableTests 5 | { 6 | public AsyncEnumerableMessagePackTests(ITestOutputHelper logger) 7 | : base(logger) 8 | { 9 | } 10 | 11 | protected override void InitializeFormattersAndHandlers() 12 | { 13 | this.serverMessageFormatter = new MessagePackFormatter(); 14 | this.clientMessageFormatter = new MessagePackFormatter(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/AsyncEnumerableSystemTextJsonTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | public class AsyncEnumerableSystemTextJsonTests : AsyncEnumerableTests 5 | { 6 | public AsyncEnumerableSystemTextJsonTests(ITestOutputHelper logger) 7 | : base(logger) 8 | { 9 | } 10 | 11 | protected override void InitializeFormattersAndHandlers() 12 | { 13 | this.serverMessageFormatter = new SystemTextJsonFormatter(); 14 | this.clientMessageFormatter = new SystemTextJsonFormatter(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/CommonErrorDataTests.cs: -------------------------------------------------------------------------------- 1 | public class CommonErrorDataTests : TestBase 2 | { 3 | public CommonErrorDataTests(ITestOutputHelper logger) 4 | : base(logger) 5 | { 6 | } 7 | 8 | [Fact] 9 | public void Ctor_CopyFrom_Null() 10 | { 11 | Assert.Throws(() => new CommonErrorData(null!)); 12 | } 13 | 14 | [Fact] 15 | public void Ctor_CopyFrom_NeverThrown() 16 | { 17 | var template = new CustomException(5, "msg"); 18 | var errorData = new CommonErrorData(template); 19 | Assert.Equal("msg", template.Message); 20 | Assert.Equal(template.HResult, errorData.HResult); 21 | Assert.Null(errorData.Inner); 22 | Assert.Equal(typeof(CustomException).FullName, errorData.TypeName); 23 | Assert.Null(errorData.StackTrace); 24 | } 25 | 26 | [Fact] 27 | public void Ctor_CopyFrom_ThrownStackTrace() 28 | { 29 | try 30 | { 31 | throw new CustomException(5, "msg"); 32 | } 33 | catch (Exception template) 34 | { 35 | var errorData = new CommonErrorData(template); 36 | Assert.Equal("msg", template.Message); 37 | Assert.Equal(template.HResult, errorData.HResult); 38 | Assert.Null(errorData.Inner); 39 | Assert.Equal(typeof(CustomException).FullName, errorData.TypeName); 40 | Assert.NotNull(errorData.StackTrace); 41 | this.Logger.WriteLine(errorData.StackTrace); 42 | } 43 | } 44 | 45 | [Fact] 46 | public void Ctor_CopyFrom_InnerExceptions() 47 | { 48 | var template = new InvalidOperationException("outer", new InvalidCastException("inner")); 49 | var errorData = new CommonErrorData(template); 50 | 51 | Assert.Equal(template.GetType().FullName, errorData.TypeName); 52 | Assert.Equal(template.Message, errorData.Message); 53 | Assert.Equal(template.InnerException!.GetType().FullName, errorData.Inner?.TypeName); 54 | Assert.Equal(template.InnerException.Message, errorData.Inner?.Message); 55 | } 56 | 57 | private class CustomException : Exception 58 | { 59 | internal CustomException(int hresult, string message) 60 | : base(message) 61 | { 62 | this.HResult = hresult; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/CommonMethodNameTransformsTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using StreamJsonRpc; 5 | using Xunit; 6 | 7 | public class CommonMethodNameTransformsTests 8 | { 9 | [Fact] 10 | public void CamelCase() 11 | { 12 | Assert.Equal("fireOne", CommonMethodNameTransforms.CamelCase("FireOne")); 13 | Assert.Equal("fireOne", CommonMethodNameTransforms.CamelCase("fireOne")); 14 | Assert.Equal("fireOneAndTwo", CommonMethodNameTransforms.CamelCase("FIREOneAndTwo")); 15 | Assert.Equal("fire", CommonMethodNameTransforms.CamelCase("FIRE")); 16 | Assert.Throws(() => CommonMethodNameTransforms.CamelCase(null!)); 17 | Assert.Equal(string.Empty, CommonMethodNameTransforms.CamelCase(string.Empty)); 18 | } 19 | 20 | [Fact] 21 | public void Prefix() 22 | { 23 | Assert.Equal("FooDo", CommonMethodNameTransforms.Prepend("Foo")("Do")); 24 | Assert.Equal("Foo.Do", CommonMethodNameTransforms.Prepend("Foo.")("Do")); 25 | Assert.Equal("Foo.Bar/Do", CommonMethodNameTransforms.Prepend("Foo.Bar/")("Do")); 26 | Assert.Equal("Foo.Bar.Do", CommonMethodNameTransforms.Prepend("Foo.Bar.")("Do")); 27 | Assert.Throws(() => CommonMethodNameTransforms.Prepend(null!)); 28 | Assert.Equal("Do", CommonMethodNameTransforms.Prepend(string.Empty)("Do")); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/CustomCancellationStrategyJsonTypeHandlingTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Text; 5 | using Newtonsoft.Json; 6 | 7 | public class CustomCancellationStrategyJsonTypeHandlingTests : CustomCancellationStrategyTests 8 | { 9 | public CustomCancellationStrategyJsonTypeHandlingTests(ITestOutputHelper logger) 10 | : base(logger) 11 | { 12 | } 13 | 14 | protected override void InitializeFormattersAndHandlers() 15 | { 16 | this.serverMessageFormatter = new JsonMessageFormatter(new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)) 17 | { 18 | JsonSerializer = 19 | { 20 | TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Objects, 21 | TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple, 22 | }, 23 | }; 24 | 25 | this.clientMessageFormatter = new JsonMessageFormatter(new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)) 26 | { 27 | JsonSerializer = 28 | { 29 | TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Objects, 30 | TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple, 31 | }, 32 | }; 33 | 34 | this.serverMessageHandler = new HeaderDelimitedMessageHandler(this.serverStream, this.serverStream, this.serverMessageFormatter); 35 | this.clientMessageHandler = new HeaderDelimitedMessageHandler(this.clientStream, this.clientStream, this.clientMessageFormatter); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/DirectMessageHandler.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.VisualStudio.Threading; 5 | using Newtonsoft.Json.Linq; 6 | using StreamJsonRpc; 7 | using StreamJsonRpc.Protocol; 8 | 9 | public class DirectMessageHandler : MessageHandlerBase 10 | { 11 | public DirectMessageHandler() 12 | : base(new JsonMessageFormatter()) 13 | { 14 | } 15 | 16 | public override bool CanRead => true; 17 | 18 | public override bool CanWrite => true; 19 | 20 | public new JsonMessageFormatter Formatter => (JsonMessageFormatter)base.Formatter; 21 | 22 | internal AsyncQueue MessagesToRead { get; } = new AsyncQueue(); 23 | 24 | internal AsyncQueue WrittenMessages { get; } = new AsyncQueue(); 25 | 26 | protected override async ValueTask ReadCoreAsync(CancellationToken cancellationToken) 27 | { 28 | return this.Formatter.Deserialize(await this.MessagesToRead.DequeueAsync(cancellationToken)); 29 | } 30 | 31 | protected override ValueTask WriteCoreAsync(JsonRpcMessage content, CancellationToken cancellationToken) 32 | { 33 | this.WrittenMessages.Enqueue(this.Formatter.Serialize(content)); 34 | return default; 35 | } 36 | 37 | protected override ValueTask FlushAsync(CancellationToken cancellationToken) => default; 38 | } 39 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/DisposableProxyJsonTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Newtonsoft.Json; 5 | 6 | public class DisposableProxyJsonTests : DisposableProxyTests 7 | { 8 | public DisposableProxyJsonTests(ITestOutputHelper logger) 9 | : base(logger) 10 | { 11 | } 12 | 13 | protected override Type FormatterExceptionType => typeof(JsonSerializationException); 14 | 15 | protected override IJsonRpcMessageFormatter CreateFormatter() => new JsonMessageFormatter(); 16 | } 17 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/DisposableProxyMessagePackTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using MessagePack; 5 | 6 | public class DisposableProxyMessagePackTests : DisposableProxyTests 7 | { 8 | public DisposableProxyMessagePackTests(ITestOutputHelper logger) 9 | : base(logger) 10 | { 11 | } 12 | 13 | protected override Type FormatterExceptionType => typeof(MessagePackSerializationException); 14 | 15 | protected override IJsonRpcMessageFormatter CreateFormatter() => new MessagePackFormatter(); 16 | } 17 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/DisposableProxySystemTextJsonTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Text.Json; 5 | 6 | public class DisposableProxySystemTextJsonTests : DisposableProxyTests 7 | { 8 | public DisposableProxySystemTextJsonTests(ITestOutputHelper logger) 9 | : base(logger) 10 | { 11 | } 12 | 13 | protected override Type FormatterExceptionType => typeof(JsonException); 14 | 15 | protected override IJsonRpcMessageFormatter CreateFormatter() => new SystemTextJsonFormatter(); 16 | } 17 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/DuplexPipeMarshalingJsonTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | public class DuplexPipeMarshalingJsonTests : DuplexPipeMarshalingTests 5 | { 6 | public DuplexPipeMarshalingJsonTests(ITestOutputHelper logger) 7 | : base(logger) 8 | { 9 | } 10 | 11 | protected override void InitializeFormattersAndHandlers() 12 | { 13 | this.serverMessageFormatter = new JsonMessageFormatter { MultiplexingStream = this.serverMx }; 14 | this.clientMessageFormatter = new JsonMessageFormatter { MultiplexingStream = this.clientMx }; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/DuplexPipeMarshalingMessagePackTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | public class DuplexPipeMarshalingMessagePackTests : DuplexPipeMarshalingTests 5 | { 6 | public DuplexPipeMarshalingMessagePackTests(ITestOutputHelper logger) 7 | : base(logger) 8 | { 9 | } 10 | 11 | protected override void InitializeFormattersAndHandlers() 12 | { 13 | this.serverMessageFormatter = new MessagePackFormatter { MultiplexingStream = this.serverMx }; 14 | this.clientMessageFormatter = new MessagePackFormatter { MultiplexingStream = this.clientMx }; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/DuplexPipeMarshalingSystemTextJsonTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | public class DuplexPipeMarshalingSystemTextJsonTests : DuplexPipeMarshalingTests 5 | { 6 | public DuplexPipeMarshalingSystemTextJsonTests(ITestOutputHelper logger) 7 | : base(logger) 8 | { 9 | } 10 | 11 | protected override void InitializeFormattersAndHandlers() 12 | { 13 | this.serverMessageFormatter = new SystemTextJsonFormatter { MultiplexingStream = this.serverMx }; 14 | this.clientMessageFormatter = new SystemTextJsonFormatter { MultiplexingStream = this.clientMx }; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/ExceptionSettingsTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | public class ExceptionSettingsTests : TestBase 5 | { 6 | public ExceptionSettingsTests(ITestOutputHelper logger) 7 | : base(logger) 8 | { 9 | } 10 | 11 | [Fact] 12 | public void RecursionLimit() 13 | { 14 | Assert.Equal(int.MaxValue, ExceptionSettings.TrustedData.RecursionLimit); 15 | Assert.Equal(50, ExceptionSettings.UntrustedData.RecursionLimit); 16 | } 17 | 18 | [Fact] 19 | public void CanDeserialize() 20 | { 21 | Assert.True(ExceptionSettings.TrustedData.CanDeserialize(typeof(AppDomain))); 22 | Assert.False(ExceptionSettings.UntrustedData.CanDeserialize(typeof(AppDomain))); 23 | 24 | Assert.True((ExceptionSettings.TrustedData with { RecursionLimit = 3 }).CanDeserialize(typeof(AppDomain))); 25 | Assert.False((ExceptionSettings.UntrustedData with { RecursionLimit = 3 }).CanDeserialize(typeof(AppDomain))); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/Exceptions/RemoteInvocationExceptionTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | public class RemoteInvocationExceptionTests : TestBase 5 | { 6 | private const string SomeMessage = "test message"; 7 | private static readonly Exception SomeInnerException = new Exception(); 8 | 9 | public RemoteInvocationExceptionTests(ITestOutputHelper logger) 10 | : base(logger) 11 | { 12 | } 13 | 14 | [Fact] 15 | public void Ctor_Message_Code_Data() 16 | { 17 | var data = new CommonErrorData(); 18 | var ex = new RemoteInvocationException(SomeMessage, 123, data); 19 | Assert.Equal(SomeMessage, ex.Message); 20 | Assert.Equal(123, ex.ErrorCode); 21 | Assert.Same(data, ex.ErrorData); 22 | Assert.Null(ex.DeserializedErrorData); 23 | } 24 | 25 | [Fact] 26 | public void Ctor_Message_Code_Data_DeserializedData() 27 | { 28 | var data = new CommonErrorData(); 29 | var deserializedData = new CommonErrorData(); 30 | var ex = new RemoteInvocationException(SomeMessage, 123, data, deserializedData); 31 | Assert.Equal(SomeMessage, ex.Message); 32 | Assert.Equal(123, ex.ErrorCode); 33 | Assert.Same(data, ex.ErrorData); 34 | Assert.Same(deserializedData, ex.DeserializedErrorData); 35 | } 36 | 37 | [Fact] 38 | public void Serializable() 39 | { 40 | var data = new CommonErrorData(); 41 | var deserializedData = new CommonErrorData(); 42 | var original = new RemoteInvocationException(SomeMessage, 123, data, deserializedData); 43 | var deserialized = BinaryFormatterRoundtrip(original); 44 | Assert.Equal(original.Message, deserialized.Message); 45 | Assert.Equal(original.ErrorCode, deserialized.ErrorCode); 46 | Assert.Null(deserialized.ErrorData); 47 | Assert.Null(deserialized.DeserializedErrorData); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/InteropTestBase.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Newtonsoft.Json; 5 | using Newtonsoft.Json.Linq; 6 | 7 | public class InteropTestBase : TestBase 8 | { 9 | protected readonly DirectMessageHandler messageHandler; 10 | 11 | public InteropTestBase(ITestOutputHelper logger) 12 | : base(logger) 13 | { 14 | this.messageHandler = new DirectMessageHandler(); 15 | } 16 | 17 | protected void UseTypeHandling() 18 | { 19 | this.messageHandler.Formatter.JsonSerializer.TypeNameHandling = TypeNameHandling.Objects; 20 | this.messageHandler.Formatter.JsonSerializer.TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple; 21 | } 22 | 23 | protected ValueTask RequestAsync(object request) 24 | { 25 | this.Send(request); 26 | return this.ReceiveAsync(); 27 | } 28 | 29 | protected void Send(dynamic message) 30 | { 31 | Requires.NotNull(message, nameof(message)); 32 | 33 | var json = JToken.FromObject(message, new JsonSerializer()); 34 | this.messageHandler.MessagesToRead.Enqueue(json); 35 | } 36 | 37 | protected async ValueTask ReceiveAsync() 38 | { 39 | JToken json = await this.messageHandler.WrittenMessages.DequeueAsync(this.TimeoutToken); 40 | return json; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/JsonRpcClient20InteropJsonTypeHandlingTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | public class JsonRpcClient20InteropJsonTypeHandlingTests : JsonRpcClient20InteropTests 5 | { 6 | public JsonRpcClient20InteropJsonTypeHandlingTests(ITestOutputHelper logger) 7 | : base(logger) 8 | { 9 | this.UseTypeHandling(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/JsonRpcCustomRequestIdTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Diagnostics; 5 | using Microsoft.VisualStudio.Threading; 6 | using Newtonsoft.Json.Linq; 7 | 8 | public class JsonRpcCustomRequestIdTests : InteropTestBase 9 | { 10 | private JsonRpcWithStringIds clientRpc; 11 | 12 | public JsonRpcCustomRequestIdTests(ITestOutputHelper logger) 13 | : base(logger) 14 | { 15 | this.clientRpc = new JsonRpcWithStringIds(this.messageHandler) 16 | { 17 | TraceSource = 18 | { 19 | Switch = { Level = SourceLevels.Verbose }, 20 | Listeners = { new XunitTraceListener(logger) }, 21 | }, 22 | }; 23 | this.clientRpc.StartListening(); 24 | } 25 | 26 | [Fact] 27 | public async Task ClientSendsUniqueIdAsString() 28 | { 29 | var invokeTask = this.clientRpc.InvokeWithCancellationAsync("test", cancellationToken: this.TimeoutToken); 30 | JToken request = await this.ReceiveAsync(); 31 | Assert.Equal(JTokenType.String, request["id"]?.Type); 32 | string? idAsString = request.Value("id"); 33 | Assert.StartsWith("prefix-", idAsString); 34 | this.Send(new 35 | { 36 | jsonrpc = "2.0", 37 | id = idAsString, 38 | result = "pass", 39 | }); 40 | 41 | string result = await invokeTask.WithCancellation(this.TimeoutToken); 42 | Assert.Equal("pass", result); 43 | } 44 | 45 | private class JsonRpcWithStringIds : JsonRpc 46 | { 47 | public JsonRpcWithStringIds(IJsonRpcMessageHandler handler) 48 | : base(handler) 49 | { 50 | } 51 | 52 | protected override RequestId CreateNewRequestId() 53 | { 54 | return new RequestId("prefix-" + base.CreateNewRequestId().Number); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/JsonRpcEnumerableSettingsTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using StreamJsonRpc; 5 | using Xunit; 6 | 7 | public class JsonRpcEnumerableSettingsTests 8 | { 9 | private JsonRpcEnumerableSettings settings = new JsonRpcEnumerableSettings(); 10 | 11 | [Fact] 12 | public void MinBatchSize_Default() 13 | { 14 | Assert.Equal(1, this.settings.MinBatchSize); 15 | } 16 | 17 | [Fact] 18 | public void MaxReadAhead_Default() 19 | { 20 | Assert.Equal(0, this.settings.MaxReadAhead); 21 | } 22 | 23 | [Fact] 24 | public void Prefetch_Default() 25 | { 26 | Assert.Equal(0, this.settings.Prefetch); 27 | } 28 | 29 | [Fact] 30 | public void MinBatchSize_AcceptsOnlyPositiveIntegers() 31 | { 32 | this.settings.MinBatchSize = 1; 33 | this.settings.MinBatchSize = 10; 34 | Assert.Throws(() => this.settings.MinBatchSize = 0); 35 | Assert.Throws(() => this.settings.MinBatchSize = -1); 36 | } 37 | 38 | [Fact] 39 | public void Prefetch_AcceptsOnlyNonNegativeIntegers() 40 | { 41 | this.settings.Prefetch = 10; 42 | this.settings.Prefetch = 0; 43 | Assert.Throws(() => this.settings.Prefetch = -1); 44 | } 45 | 46 | [Fact] 47 | public void MaxReadAhead_AcceptsOnlyNonNegativeIntegers() 48 | { 49 | this.settings.MaxReadAhead = 10; 50 | this.settings.MaxReadAhead = 0; 51 | Assert.Throws(() => this.settings.MaxReadAhead = -1); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/JsonRpcRemoteTargetJsonMessageFormatterTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using StreamJsonRpc; 5 | 6 | public class JsonRpcRemoteTargetJsonMessageFormatterTests : JsonRpcRemoteTargetTests 7 | { 8 | public JsonRpcRemoteTargetJsonMessageFormatterTests(ITestOutputHelper logger) 9 | : base(logger) 10 | { 11 | } 12 | 13 | protected override IJsonRpcMessageFormatter CreateFormatter() => new JsonMessageFormatter(); 14 | } 15 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/JsonRpcRemoteTargetMessagePackFormatterTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | /* This test class enables a set of tests for a scenario that doesn't work on the MessagePackFormatter today. 5 | 6 | using StreamJsonRpc; 7 | 8 | public class JsonRpcRemoteTargetMessagePackFormatterTests : JsonRpcRemoteTargetTests 9 | { 10 | public JsonRpcRemoteTargetMessagePackFormatterTests(ITestOutputHelper logger) 11 | : base(logger) 12 | { 13 | } 14 | 15 | protected override IJsonRpcMessageFormatter CreateFormatter() => new MessagePackFormatter(); 16 | 17 | protected override IJsonRpcMessageHandler CreateHandler(Stream sending, Stream receiving) => new LengthHeaderMessageHandler(sending, receiving, this.CreateFormatter()); 18 | } 19 | */ 20 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/JsonRpcRemoteTargetSystemTextJsonFormatterTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | /* This test class enables a set of tests for a scenario that doesn't work on the SystemTextJsonFormatter today. 5 | 6 | using StreamJsonRpc; 7 | 8 | public class JsonRpcRemoteTargetSystemTextJsonFormatterTests : JsonRpcRemoteTargetTests 9 | { 10 | public JsonRpcRemoteTargetSystemTextJsonFormatterTests(ITestOutputHelper logger) 11 | : base(logger) 12 | { 13 | } 14 | 15 | protected override IJsonRpcMessageFormatter CreateFormatter() => new SystemTextJsonFormatter(); 16 | } 17 | 18 | */ 19 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/JsonRpcTargetOptionsTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using StreamJsonRpc; 5 | using Xunit; 6 | 7 | public class JsonRpcTargetOptionsTests 8 | { 9 | [Fact] 10 | public void CopyConstructor() 11 | { 12 | JsonRpcTargetOptions options = new(); 13 | options.AllowNonPublicInvocation = !options.AllowNonPublicInvocation; 14 | options.ClientRequiresNamedArguments = !options.ClientRequiresNamedArguments; 15 | options.DisposeOnDisconnect = !options.DisposeOnDisconnect; 16 | options.EventNameTransform = s => s; 17 | options.MethodNameTransform = s => s; 18 | options.NotifyClientOfEvents = !options.NotifyClientOfEvents; 19 | options.UseSingleObjectParameterDeserialization = !options.UseSingleObjectParameterDeserialization; 20 | 21 | JsonRpcTargetOptions copy = new(options); 22 | Assert.Equal(options.AllowNonPublicInvocation, copy.AllowNonPublicInvocation); 23 | Assert.Equal(options.ClientRequiresNamedArguments, copy.ClientRequiresNamedArguments); 24 | Assert.Equal(options.DisposeOnDisconnect, copy.DisposeOnDisconnect); 25 | Assert.Equal(options.EventNameTransform, copy.EventNameTransform); 26 | Assert.Equal(options.MethodNameTransform, copy.MethodNameTransform); 27 | Assert.Equal(options.NotifyClientOfEvents, copy.NotifyClientOfEvents); 28 | Assert.Equal(options.UseSingleObjectParameterDeserialization, copy.UseSingleObjectParameterDeserialization); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/LocalRpcExceptionTests.cs: -------------------------------------------------------------------------------- 1 | public class LocalRpcExceptionTests : TestBase 2 | { 3 | public LocalRpcExceptionTests(ITestOutputHelper logger) 4 | : base(logger) 5 | { 6 | } 7 | 8 | [Fact] 9 | public void ErrorCode() 10 | { 11 | var ex = new LocalRpcException() { ErrorCode = 1 }; 12 | Assert.Equal(1, ex.ErrorCode); 13 | } 14 | 15 | [Fact] 16 | public void ErrorData_AnonymousType() 17 | { 18 | var ex = new LocalRpcException() { ErrorData = new { myError = 5 } }; 19 | dynamic error = ex.ErrorData; 20 | Assert.Equal(5, (int)error.myError); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/MarshalableProxyJsonTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Newtonsoft.Json; 5 | 6 | public class MarshalableProxyJsonTests : MarshalableProxyTests 7 | { 8 | public MarshalableProxyJsonTests(ITestOutputHelper logger) 9 | : base(logger) 10 | { 11 | } 12 | 13 | protected override Type FormatterExceptionType => typeof(JsonSerializationException); 14 | 15 | protected override IJsonRpcMessageFormatter CreateFormatter() => new JsonMessageFormatter(); 16 | } 17 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/MarshalableProxyMessagePackTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using MessagePack; 5 | 6 | public class MarshalableProxyMessagePackTests : MarshalableProxyTests 7 | { 8 | public MarshalableProxyMessagePackTests(ITestOutputHelper logger) 9 | : base(logger) 10 | { 11 | } 12 | 13 | protected override Type FormatterExceptionType => typeof(MessagePackSerializationException); 14 | 15 | protected override IJsonRpcMessageFormatter CreateFormatter() => new MessagePackFormatter(); 16 | } 17 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/MarshalableProxySystemTextJsonTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Text.Json; 5 | 6 | public class MarshalableProxySystemTextJsonTests : MarshalableProxyTests 7 | { 8 | public MarshalableProxySystemTextJsonTests(ITestOutputHelper logger) 9 | : base(logger) 10 | { 11 | } 12 | 13 | protected override Type FormatterExceptionType => typeof(JsonException); 14 | 15 | protected override IJsonRpcMessageFormatter CreateFormatter() => new SystemTextJsonFormatter(); 16 | } 17 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/ObserverMarshalingJsonTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | public class ObserverMarshalingJsonTests : ObserverMarshalingTests 5 | { 6 | public ObserverMarshalingJsonTests(ITestOutputHelper logger) 7 | : base(logger) 8 | { 9 | } 10 | 11 | protected override IJsonRpcMessageFormatter CreateFormatter() => new JsonMessageFormatter(); 12 | } 13 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/ObserverMarshalingMessagePackTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | public class ObserverMarshalingMessagePackTests : ObserverMarshalingTests 5 | { 6 | public ObserverMarshalingMessagePackTests(ITestOutputHelper logger) 7 | : base(logger) 8 | { 9 | } 10 | 11 | protected override IJsonRpcMessageFormatter CreateFormatter() => new MessagePackFormatter(); 12 | } 13 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/ObserverMarshalingSystemTextJsonTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | public class ObserverMarshalingSystemTextJsonTests : ObserverMarshalingTests 5 | { 6 | public ObserverMarshalingSystemTextJsonTests(ITestOutputHelper logger) 7 | : base(logger) 8 | { 9 | } 10 | 11 | protected override IJsonRpcMessageFormatter CreateFormatter() => new SystemTextJsonFormatter(); 12 | } 13 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/TargetObjectEventsJsonTests.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | public class TargetObjectEventsJsonTests : TargetObjectEventsTests 4 | { 5 | public TargetObjectEventsJsonTests(ITestOutputHelper logger) 6 | : base(logger) 7 | { 8 | } 9 | 10 | protected override void InitializeFormattersAndHandlers() 11 | { 12 | var clientFormatter = new JsonMessageFormatter 13 | { 14 | JsonSerializer = 15 | { 16 | Converters = 17 | { 18 | new IFruitConverter(), 19 | }, 20 | }, 21 | }; 22 | 23 | var serverFormatter = new JsonMessageFormatter 24 | { 25 | JsonSerializer = 26 | { 27 | Converters = 28 | { 29 | new IFruitConverter(), 30 | }, 31 | }, 32 | }; 33 | 34 | this.serverMessageHandler = new HeaderDelimitedMessageHandler(this.serverStream, serverFormatter); 35 | this.clientMessageHandler = new HeaderDelimitedMessageHandler(this.clientStream, clientFormatter); 36 | } 37 | 38 | private class IFruitConverter : JsonConverter 39 | { 40 | public override IFruit? ReadJson(JsonReader reader, Type objectType, IFruit? existingValue, bool hasExistingValue, JsonSerializer serializer) 41 | { 42 | if (reader.TokenType == JsonToken.Null) 43 | { 44 | return null; 45 | } 46 | 47 | Assumes.True(reader.Read()); 48 | Assumes.True((string?)reader.Value == nameof(IFruit.Name)); 49 | Assumes.True(reader.Read()); 50 | string name = (string)reader.Value; 51 | return new Fruit(name); 52 | } 53 | 54 | public override void WriteJson(JsonWriter writer, IFruit? value, JsonSerializer serializer) 55 | { 56 | if (value is null) 57 | { 58 | writer.WriteNull(); 59 | return; 60 | } 61 | 62 | writer.WriteStartObject(); 63 | writer.WritePropertyName(nameof(IFruit.Name)); 64 | writer.WriteValue(value.Name); 65 | writer.WriteEndObject(); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/TargetObjectEventsMessagePackTests.cs: -------------------------------------------------------------------------------- 1 | using MessagePack; 2 | using MessagePack.Formatters; 3 | using MessagePack.Resolvers; 4 | 5 | public class TargetObjectEventsMessagePackTests : TargetObjectEventsTests 6 | { 7 | public TargetObjectEventsMessagePackTests(ITestOutputHelper logger) 8 | : base(logger) 9 | { 10 | } 11 | 12 | protected override void InitializeFormattersAndHandlers() 13 | { 14 | var serverMessageFormatter = new MessagePackFormatter(); 15 | var clientMessageFormatter = new MessagePackFormatter(); 16 | 17 | var options = MessagePackFormatter.DefaultUserDataSerializationOptions 18 | .WithResolver(CompositeResolver.Create( 19 | new IMessagePackFormatter[] { }, 20 | new IFormatterResolver[] { StandardResolverAllowPrivate.Instance })); 21 | serverMessageFormatter.SetMessagePackSerializerOptions(options); 22 | clientMessageFormatter.SetMessagePackSerializerOptions(options); 23 | 24 | this.serverMessageHandler = new LengthHeaderMessageHandler(this.serverStream, this.serverStream, serverMessageFormatter); 25 | this.clientMessageHandler = new LengthHeaderMessageHandler(this.clientStream, this.clientStream, clientMessageFormatter); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/Usings.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | global using Microsoft; 5 | global using StreamJsonRpc; 6 | global using StreamJsonRpc.Protocol; 7 | global using Xunit; 8 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/WebSocketMessageHandlerMessagePackTests.cs: -------------------------------------------------------------------------------- 1 | public class WebSocketMessageHandlerMessagePackTests : WebSocketMessageHandlerTests 2 | { 3 | public WebSocketMessageHandlerMessagePackTests(ITestOutputHelper logger) 4 | : base(new MessagePackFormatter(), logger) 5 | { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/WebSocketMessageHandlerSystemTextJsonTests.cs: -------------------------------------------------------------------------------- 1 | public class WebSocketMessageHandlerSystemTextJsonTests : WebSocketMessageHandlerTests 2 | { 3 | public WebSocketMessageHandlerSystemTextJsonTests(ITestOutputHelper logger) 4 | : base(new SystemTextJsonFormatter(), logger) 5 | { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/XunitTraceListener.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Text; 3 | 4 | internal class XunitTraceListener : TraceListener 5 | { 6 | private readonly ITestOutputHelper logger; 7 | private readonly StringBuilder lineInProgress = new StringBuilder(); 8 | private bool disposed; 9 | 10 | internal XunitTraceListener(ITestOutputHelper logger) 11 | { 12 | this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); 13 | } 14 | 15 | public override bool IsThreadSafe => false; 16 | 17 | public override void Write(string? message) => this.lineInProgress.Append(message); 18 | 19 | public override void WriteLine(string? message) 20 | { 21 | if (!this.disposed) 22 | { 23 | this.lineInProgress.Append(message); 24 | this.logger.WriteLine(this.lineInProgress.ToString()); 25 | this.lineInProgress.Clear(); 26 | } 27 | } 28 | 29 | protected override void Dispose(bool disposing) 30 | { 31 | this.disposed = true; 32 | base.Dispose(disposing); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/StreamJsonRpc.Tests/xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", 3 | "shadowCopy": false 4 | } 5 | -------------------------------------------------------------------------------- /tools/Check-DotNetRuntime.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Checks whether a given .NET Core runtime is installed. 4 | #> 5 | [CmdletBinding()] 6 | Param ( 7 | [Parameter()] 8 | [ValidateSet('Microsoft.AspNetCore.App','Microsoft.NETCore.App')] 9 | [string]$Runtime='Microsoft.NETCore.App', 10 | [Parameter(Mandatory=$true)] 11 | [Version]$Version 12 | ) 13 | 14 | $dotnet = Get-Command dotnet -ErrorAction SilentlyContinue 15 | if (!$dotnet) { 16 | # Nothing is installed. 17 | Write-Output $false 18 | exit 1 19 | } 20 | 21 | Function IsVersionMatch { 22 | Param( 23 | [Parameter()] 24 | $actualVersion 25 | ) 26 | return $actualVersion -and 27 | $Version.Major -eq $actualVersion.Major -and 28 | $Version.Minor -eq $actualVersion.Minor -and 29 | (($Version.Build -eq -1) -or ($Version.Build -eq $actualVersion.Build)) -and 30 | (($Version.Revision -eq -1) -or ($Version.Revision -eq $actualVersion.Revision)) 31 | } 32 | 33 | $installedRuntimes = dotnet --list-runtimes |? { $_.Split()[0] -ieq $Runtime } |% { $v = $null; [Version]::tryparse($_.Split()[1], [ref] $v); $v } 34 | $matchingRuntimes = $installedRuntimes |? { IsVersionMatch -actualVersion $_ } 35 | if (!$matchingRuntimes) { 36 | Write-Output $false 37 | exit 1 38 | } 39 | 40 | Write-Output $true 41 | exit 0 42 | -------------------------------------------------------------------------------- /tools/Check-DotNetSdk.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Checks whether the .NET Core SDK required by this repo is installed. 4 | #> 5 | [CmdletBinding()] 6 | Param ( 7 | ) 8 | 9 | $dotnet = Get-Command dotnet -ErrorAction SilentlyContinue 10 | if (!$dotnet) { 11 | # Nothing is installed. 12 | Write-Output $false 13 | exit 1 14 | } 15 | 16 | # We need to set the current directory so dotnet considers the SDK required by our global.json file. 17 | Push-Location "$PSScriptRoot\.." 18 | try { 19 | dotnet -h 2>&1 | Out-Null 20 | if (($LASTEXITCODE -eq 129) -or # On Linux 21 | ($LASTEXITCODE -eq -2147450751) # On Windows 22 | ) { 23 | # These exit codes indicate no matching SDK exists. 24 | Write-Output $false 25 | exit 2 26 | } 27 | 28 | # The required SDK is already installed! 29 | Write-Output $true 30 | exit 0 31 | } catch { 32 | # I don't know why, but on some build agents (e.g. MicroBuild), an exception is thrown from the `dotnet` invocation when a match is not found. 33 | Write-Output $false 34 | exit 3 35 | } finally { 36 | Pop-Location 37 | } 38 | -------------------------------------------------------------------------------- /tools/Convert-PDB.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Converts between Windows PDB and Portable PDB formats. 4 | .PARAMETER DllPath 5 | The path to the DLL whose PDB is to be converted. 6 | .PARAMETER PdbPath 7 | The path to the PDB to convert. May be omitted if the DLL was compiled on this machine and the PDB is still at its original path. 8 | .PARAMETER OutputPath 9 | The path of the output PDB to write. 10 | #> 11 | [CmdletBinding()] 12 | Param( 13 | [Parameter(Mandatory=$true,Position=0)] 14 | [string]$DllPath, 15 | [Parameter()] 16 | [string]$PdbPath, 17 | [Parameter(Mandatory=$true,Position=1)] 18 | [string]$OutputPath 19 | ) 20 | 21 | if ($IsMacOS -or $IsLinux) { 22 | Write-Error "This script only works on Windows" 23 | return 24 | } 25 | 26 | $version = '1.1.0-beta2-21101-01' 27 | $baseDir = "$PSScriptRoot/../obj/tools" 28 | $pdb2pdbpath = "$baseDir/Microsoft.DiaSymReader.Pdb2Pdb.$version/tools/Pdb2Pdb.exe" 29 | if (-not (Test-Path $pdb2pdbpath)) { 30 | if (-not (Test-Path $baseDir)) { New-Item -Type Directory -Path $baseDir | Out-Null } 31 | $baseDir = (Resolve-Path $baseDir).Path # Normalize it 32 | Write-Verbose "& (& $PSScriptRoot/Get-NuGetTool.ps1) install Microsoft.DiaSymReader.Pdb2Pdb -version $version -PackageSaveMode nuspec -OutputDirectory $baseDir -Source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json | Out-Null" 33 | & (& $PSScriptRoot/Get-NuGetTool.ps1) install Microsoft.DiaSymReader.Pdb2Pdb -version $version -PackageSaveMode nuspec -OutputDirectory $baseDir -Source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json | Out-Null 34 | } 35 | 36 | $args = $DllPath,'/out',$OutputPath,'/nowarn','0021' 37 | if ($PdbPath) { 38 | $args += '/pdb',$PdbPath 39 | } 40 | 41 | Write-Verbose "$pdb2pdbpath $args" 42 | & $pdb2pdbpath $args 43 | -------------------------------------------------------------------------------- /tools/Get-ArtifactsStagingDirectory.ps1: -------------------------------------------------------------------------------- 1 | Param( 2 | [switch]$CleanIfLocal 3 | ) 4 | if ($env:BUILD_ARTIFACTSTAGINGDIRECTORY) { 5 | $ArtifactStagingFolder = $env:BUILD_ARTIFACTSTAGINGDIRECTORY 6 | } elseif ($env:RUNNER_TEMP) { 7 | $ArtifactStagingFolder = Join-Path $env:RUNNER_TEMP _artifacts 8 | } else { 9 | $ArtifactStagingFolder = [System.IO.Path]::GetFullPath("$PSScriptRoot/../obj/_artifacts") 10 | if ($CleanIfLocal -and (Test-Path $ArtifactStagingFolder)) { 11 | Remove-Item $ArtifactStagingFolder -Recurse -Force 12 | } 13 | } 14 | 15 | $ArtifactStagingFolder 16 | -------------------------------------------------------------------------------- /tools/Get-LibTemplateBasis.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Returns the name of the well-known branch in the Library.Template repository upon which HEAD is based. 4 | #> 5 | [CmdletBinding(SupportsShouldProcess = $true)] 6 | Param( 7 | [switch]$ErrorIfNotRelated 8 | ) 9 | 10 | # This list should be sorted in order of decreasing specificity. 11 | $branchMarkers = @( 12 | @{ commit = 'fd0a7b25ccf030bbd16880cca6efe009d5b1fffc'; branch = 'microbuild' }; 13 | @{ commit = '05f49ce799c1f9cc696d53eea89699d80f59f833'; branch = 'main' }; 14 | ) 15 | 16 | foreach ($entry in $branchMarkers) { 17 | if (git rev-list HEAD | Select-String -Pattern $entry.commit) { 18 | return $entry.branch 19 | } 20 | } 21 | 22 | if ($ErrorIfNotRelated) { 23 | Write-Error "Library.Template has not been previously merged with this repo. Please review https://github.com/AArnott/Library.Template/tree/main?tab=readme-ov-file#readme for instructions." 24 | exit 1 25 | } 26 | -------------------------------------------------------------------------------- /tools/Get-NuGetTool.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Downloads the NuGet.exe tool and returns the path to it. 4 | .PARAMETER NuGetVersion 5 | The version of the NuGet tool to acquire. 6 | #> 7 | Param( 8 | [Parameter()] 9 | [string]$NuGetVersion='6.12.2' 10 | ) 11 | 12 | $toolsPath = & "$PSScriptRoot\Get-TempToolsPath.ps1" 13 | $binaryToolsPath = Join-Path $toolsPath $NuGetVersion 14 | if (!(Test-Path $binaryToolsPath)) { $null = mkdir $binaryToolsPath } 15 | $nugetPath = Join-Path $binaryToolsPath nuget.exe 16 | 17 | if (!(Test-Path $nugetPath)) { 18 | Write-Host "Downloading nuget.exe $NuGetVersion..." -ForegroundColor Yellow 19 | (New-Object System.Net.WebClient).DownloadFile("https://dist.nuget.org/win-x86-commandline/v$NuGetVersion/NuGet.exe", $nugetPath) 20 | } 21 | 22 | return (Resolve-Path $nugetPath).Path 23 | -------------------------------------------------------------------------------- /tools/Get-ProcDump.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Downloads 32-bit and 64-bit procdump executables and returns the path to where they were installed. 4 | #> 5 | $version = '0.0.1' 6 | $baseDir = "$PSScriptRoot\..\obj\tools" 7 | $procDumpToolPath = "$baseDir\procdump.$version\bin" 8 | if (-not (Test-Path $procDumpToolPath)) { 9 | if (-not (Test-Path $baseDir)) { New-Item -Type Directory -Path $baseDir | Out-Null } 10 | $baseDir = (Resolve-Path $baseDir).Path # Normalize it 11 | & (& $PSScriptRoot\Get-NuGetTool.ps1) install procdump -version $version -PackageSaveMode nuspec -OutputDirectory $baseDir -Source https://api.nuget.org/v3/index.json | Out-Null 12 | } 13 | 14 | (Resolve-Path $procDumpToolPath).Path 15 | -------------------------------------------------------------------------------- /tools/Get-SymbolFiles.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Collect the list of PDBs built in this repo. 4 | .PARAMETER Path 5 | The directory to recursively search for PDBs. 6 | .PARAMETER Tests 7 | A switch indicating to find PDBs only for test binaries instead of only for shipping shipping binaries. 8 | #> 9 | [CmdletBinding()] 10 | param ( 11 | [parameter(Mandatory=$true)] 12 | [string]$Path, 13 | [switch]$Tests 14 | ) 15 | 16 | $ActivityName = "Collecting symbols from $Path" 17 | Write-Progress -Activity $ActivityName -CurrentOperation "Discovery PDB files" 18 | $PDBs = Get-ChildItem -rec "$Path/*.pdb" 19 | 20 | # Filter PDBs to product OR test related. 21 | $testregex = "unittest|tests|\.test\.|Benchmarks" 22 | 23 | Write-Progress -Activity $ActivityName -CurrentOperation "De-duplicating symbols" 24 | $PDBsByHash = @{} 25 | $i = 0 26 | $PDBs |% { 27 | Write-Progress -Activity $ActivityName -CurrentOperation "De-duplicating symbols" -PercentComplete (100 * $i / $PDBs.Length) 28 | $hash = Get-FileHash $_ 29 | $i++ 30 | Add-Member -InputObject $_ -MemberType NoteProperty -Name Hash -Value $hash.Hash 31 | Write-Output $_ 32 | } | Sort-Object CreationTime |% { 33 | # De-dupe based on hash. Prefer the first match so we take the first built copy. 34 | if (-not $PDBsByHash.ContainsKey($_.Hash)) { 35 | $PDBsByHash.Add($_.Hash, $_.FullName) 36 | Write-Output $_ 37 | } 38 | } |? { 39 | if ($Tests) { 40 | $_.FullName -match $testregex 41 | } else { 42 | $_.FullName -notmatch $testregex 43 | } 44 | } |% { 45 | # Collect the DLLs/EXEs as well. 46 | $rootName = "$($_.Directory)/$($_.BaseName)" 47 | if ($rootName.EndsWith('.ni')) { 48 | $rootName = $rootName.Substring(0, $rootName.Length - 3) 49 | } 50 | 51 | $dllPath = "$rootName.dll" 52 | $exePath = "$rootName.exe" 53 | if (Test-Path $dllPath) { 54 | $BinaryImagePath = $dllPath 55 | } elseif (Test-Path $exePath) { 56 | $BinaryImagePath = $exePath 57 | } else { 58 | Write-Warning "`"$_`" found with no matching binary file." 59 | $BinaryImagePath = $null 60 | } 61 | 62 | if ($BinaryImagePath) { 63 | Write-Output $BinaryImagePath 64 | Write-Output $_.FullName 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tools/Get-TempToolsPath.ps1: -------------------------------------------------------------------------------- 1 | if ($env:AGENT_TEMPDIRECTORY) { 2 | $path = "$env:AGENT_TEMPDIRECTORY\$env:BUILD_BUILDID" 3 | } elseif ($env:localappdata) { 4 | $path = "$env:localappdata\gitrepos\tools" 5 | } else { 6 | $path = "$PSScriptRoot\..\obj\tools" 7 | } 8 | 9 | if (!(Test-Path $path)) { 10 | New-Item -ItemType Directory -Path $Path | Out-Null 11 | } 12 | 13 | (Resolve-Path $path).Path 14 | -------------------------------------------------------------------------------- /tools/Prepare-Legacy-Symbols.ps1: -------------------------------------------------------------------------------- 1 | Param( 2 | [string]$Path 3 | ) 4 | 5 | $ArtifactStagingFolder = & "$PSScriptRoot/Get-ArtifactsStagingDirectory.ps1" 6 | $ArtifactStagingFolder += '/symbols-legacy' 7 | robocopy $Path $ArtifactStagingFolder /mir /njh /njs /ndl /nfl 8 | $WindowsPdbSubDirName = 'symstore' 9 | 10 | Get-ChildItem "$ArtifactStagingFolder\*.pdb" -Recurse |% { 11 | $dllPath = "$($_.Directory)/$($_.BaseName).dll" 12 | $exePath = "$($_.Directory)/$($_.BaseName).exe" 13 | if (Test-Path $dllPath) { 14 | $BinaryImagePath = $dllPath 15 | } elseif (Test-Path $exePath) { 16 | $BinaryImagePath = $exePath 17 | } else { 18 | Write-Warning "`"$_`" found with no matching binary file." 19 | $BinaryImagePath = $null 20 | } 21 | 22 | if ($BinaryImagePath) { 23 | # Convert the PDB to legacy Windows PDBs 24 | Write-Host "Converting PDB for $_" -ForegroundColor DarkGray 25 | $WindowsPdbDir = "$($_.Directory.FullName)\$WindowsPdbSubDirName" 26 | if (!(Test-Path $WindowsPdbDir)) { mkdir $WindowsPdbDir | Out-Null } 27 | $legacyPdbPath = "$WindowsPdbDir\$($_.BaseName).pdb" 28 | & "$PSScriptRoot\Convert-PDB.ps1" -DllPath $BinaryImagePath -PdbPath $_ -OutputPath $legacyPdbPath 29 | if ($LASTEXITCODE -ne 0) { 30 | Write-Warning "PDB conversion of `"$_`" failed." 31 | } 32 | 33 | Move-Item $legacyPdbPath $_ -Force 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tools/artifacts/APIScanInputs.ps1: -------------------------------------------------------------------------------- 1 | $inputs = & "$PSScriptRoot/symbols.ps1" 2 | 3 | if (!$inputs) { return } 4 | 5 | # Filter out specific files that target OS's that are not subject to APIScan. 6 | # Files that are subject but are not supported must be scanned and an SEL exception filed. 7 | $outputs = @{} 8 | $forbiddenSubPaths = @( 9 | , 'linux-*' 10 | , 'osx*' 11 | ) 12 | 13 | $inputs.GetEnumerator() | % { 14 | $list = $_.Value | ? { 15 | $path = $_.Replace('\', '/') 16 | return !($forbiddenSubPaths | ? { $path -like "*/$_/*" }) 17 | } 18 | $outputs[$_.Key] = $list 19 | } 20 | 21 | 22 | $outputs 23 | -------------------------------------------------------------------------------- /tools/artifacts/LocBin.ps1: -------------------------------------------------------------------------------- 1 | # Identify LCE files and the binary files they describe 2 | $BinRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..\bin") 3 | if (!(Test-Path $BinRoot)) { return } 4 | 5 | $FilesToCopy = @() 6 | $FilesToCopy += Get-ChildItem -Recurse -File -Path $BinRoot |? { $_.FullName -match '\\Localize\\' } 7 | 8 | Get-ChildItem -rec "$BinRoot\*.lce" -File | % { 9 | $FilesToCopy += $_ 10 | $FilesToCopy += $_.FullName.SubString(0, $_.FullName.Length - 4) 11 | } 12 | 13 | $FilesToCopy += Get-ChildItem -rec "$BinRoot\*.lcg" -File | % { [xml](Get-Content $_) } | % { $_.lcx.name } 14 | 15 | @{ 16 | "$BinRoot" = $FilesToCopy; 17 | } 18 | -------------------------------------------------------------------------------- /tools/artifacts/VSInsertion.ps1: -------------------------------------------------------------------------------- 1 | # This artifact captures everything needed to insert into VS (NuGet packages, insertion metadata, etc.) 2 | 3 | [CmdletBinding()] 4 | Param ( 5 | ) 6 | 7 | if ($IsMacOS -or $IsLinux) { 8 | # We only package up for insertions on Windows agents since they are where optprof can happen. 9 | Write-Verbose "Skipping VSInsertion artifact since we're not on Windows." 10 | return @{} 11 | } 12 | 13 | $RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") 14 | $BuildConfiguration = $env:BUILDCONFIGURATION 15 | if (!$BuildConfiguration) { 16 | $BuildConfiguration = 'Debug' 17 | } 18 | 19 | $PackagesRoot = "$RepoRoot/bin/Packages/$BuildConfiguration" 20 | $NuGetPackages = "$PackagesRoot/NuGet" 21 | $VsixPackages = "$PackagesRoot/Vsix" 22 | 23 | if (!(Test-Path $NuGetPackages)) { 24 | Write-Warning "Skipping because NuGet packages haven't been built yet." 25 | return @{} 26 | } 27 | 28 | $result = @{ 29 | "$NuGetPackages" = (Get-ChildItem $NuGetPackages -Recurse) 30 | } 31 | 32 | if (Test-Path $VsixPackages) { 33 | $result["$PackagesRoot"] += Get-ChildItem $VsixPackages -Recurse 34 | } 35 | 36 | if ($env:IsOptProf) { 37 | $VSRepoPackages = "$PackagesRoot/VSRepo" 38 | $result["$VSRepoPackages"] = (Get-ChildItem "$VSRepoPackages\*.VSInsertionMetadata.*.nupkg"); 39 | } 40 | 41 | $result 42 | -------------------------------------------------------------------------------- /tools/artifacts/Variables.ps1: -------------------------------------------------------------------------------- 1 | # This artifact captures all variables defined in the ..\variables folder. 2 | # It "snaps" the values of these variables where we can compute them during the build, 3 | # and otherwise captures the scripts to run later during an Azure Pipelines environment release. 4 | 5 | $RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot/../..") 6 | $ArtifactBasePath = "$RepoRoot/obj/_artifacts" 7 | $VariablesArtifactPath = Join-Path $ArtifactBasePath variables 8 | if (-not (Test-Path $VariablesArtifactPath)) { New-Item -ItemType Directory -Path $VariablesArtifactPath | Out-Null } 9 | 10 | # Copy variables, either by value if the value is calculable now, or by script 11 | Get-ChildItem "$PSScriptRoot/../variables" |% { 12 | $value = $null 13 | if (-not $_.BaseName.StartsWith('_')) { # Skip trying to interpret special scripts 14 | # First check the environment variables in case the variable was set in a queued build 15 | # Always use all caps for env var access because Azure Pipelines converts variables to upper-case for env vars, 16 | # and on non-Windows env vars are case sensitive. 17 | $envVarName = $_.BaseName.ToUpper() 18 | if (Test-Path env:$envVarName) { 19 | $value = Get-Content "env:$envVarName" 20 | } 21 | 22 | # If that didn't give us anything, try executing the script right now from its original position 23 | if (-not $value) { 24 | $value = & $_.FullName 25 | } 26 | 27 | if ($value) { 28 | # We got something, so wrap it with quotes so it's treated like a literal value. 29 | $value = "'$value'" 30 | } 31 | } 32 | 33 | # If that didn't get us anything, just copy the script itself 34 | if (-not $value) { 35 | $value = Get-Content -LiteralPath $_.FullName 36 | } 37 | 38 | Set-Content -LiteralPath "$VariablesArtifactPath/$($_.Name)" -Value $value 39 | } 40 | 41 | @{ 42 | "$VariablesArtifactPath" = (Get-ChildItem $VariablesArtifactPath -Recurse); 43 | } 44 | -------------------------------------------------------------------------------- /tools/artifacts/build_logs.ps1: -------------------------------------------------------------------------------- 1 | $ArtifactStagingFolder = & "$PSScriptRoot/../Get-ArtifactsStagingDirectory.ps1" 2 | 3 | if (!(Test-Path $ArtifactStagingFolder/build_logs)) { return } 4 | 5 | @{ 6 | "$ArtifactStagingFolder/build_logs" = (Get-ChildItem -Recurse "$ArtifactStagingFolder/build_logs") 7 | } 8 | -------------------------------------------------------------------------------- /tools/artifacts/coverageResults.ps1: -------------------------------------------------------------------------------- 1 | $RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") 2 | 3 | $coverageFiles = @(Get-ChildItem "$RepoRoot/test/*.cobertura.xml" -Recurse | Where {$_.FullName -notlike "*/In/*" -and $_.FullName -notlike "*\In\*" }) 4 | 5 | # Prepare code coverage reports for merging on another machine 6 | $repoRoot = $env:SYSTEM_DEFAULTWORKINGDIRECTORY 7 | if (!$repoRoot) { $repoRoot = $env:GITHUB_WORKSPACE } 8 | if ($repoRoot) { 9 | Write-Host "Substituting $repoRoot with `"{reporoot}`"" 10 | $coverageFiles |% { 11 | $content = Get-Content -LiteralPath $_ |% { $_ -Replace [regex]::Escape($repoRoot), "{reporoot}" } 12 | Set-Content -LiteralPath $_ -Value $content -Encoding UTF8 13 | } 14 | } else { 15 | Write-Warning "coverageResults: Cloud build not detected. Machine-neutral token replacement skipped." 16 | } 17 | 18 | if (!((Test-Path $RepoRoot\bin) -and (Test-Path $RepoRoot\obj))) { return } 19 | 20 | @{ 21 | $RepoRoot = ( 22 | $coverageFiles + 23 | (Get-ChildItem "$RepoRoot\obj\*.cs" -Recurse) 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /tools/artifacts/deployables.ps1: -------------------------------------------------------------------------------- 1 | $RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") 2 | $BuildConfiguration = $env:BUILDCONFIGURATION 3 | if (!$BuildConfiguration) { 4 | $BuildConfiguration = 'Debug' 5 | } 6 | 7 | $PackagesRoot = "$RepoRoot/bin/Packages/$BuildConfiguration" 8 | 9 | if (!(Test-Path $PackagesRoot)) { return } 10 | 11 | @{ 12 | "$PackagesRoot" = (Get-ChildItem $PackagesRoot -Recurse) 13 | } 14 | -------------------------------------------------------------------------------- /tools/artifacts/projectAssetsJson.ps1: -------------------------------------------------------------------------------- 1 | $ObjRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..\obj") 2 | 3 | if (!(Test-Path $ObjRoot)) { return } 4 | 5 | @{ 6 | "$ObjRoot" = ( 7 | (Get-ChildItem "$ObjRoot\project.assets.json" -Recurse) 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /tools/artifacts/symbols.ps1: -------------------------------------------------------------------------------- 1 | $BinPath = [System.IO.Path]::GetFullPath("$PSScriptRoot/../../bin") 2 | if (!(Test-Path $BinPath)) { return } 3 | $symbolfiles = & "$PSScriptRoot/../Get-SymbolFiles.ps1" -Path $BinPath | Get-Unique 4 | 5 | @{ 6 | "$BinPath" = $SymbolFiles; 7 | } 8 | -------------------------------------------------------------------------------- /tools/artifacts/testResults.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | Param( 3 | ) 4 | 5 | $result = @{} 6 | 7 | $testRoot = Resolve-Path "$PSScriptRoot\..\..\test" 8 | $result[$testRoot] = (Get-ChildItem "$testRoot\TestResults" -Recurse -Directory | Get-ChildItem -Recurse -File) 9 | 10 | $artifactStaging = & "$PSScriptRoot/../Get-ArtifactsStagingDirectory.ps1" 11 | $testlogsPath = Join-Path $artifactStaging "test_logs" 12 | if (Test-Path $testlogsPath) { 13 | $result[$testlogsPath] = Get-ChildItem $testlogsPath -Recurse; 14 | } 15 | 16 | $result 17 | -------------------------------------------------------------------------------- /tools/artifacts/test_symbols.ps1: -------------------------------------------------------------------------------- 1 | $BinPath = [System.IO.Path]::GetFullPath("$PSScriptRoot/../../bin") 2 | if (!(Test-Path $BinPath)) { return } 3 | $symbolfiles = & "$PSScriptRoot/../Get-SymbolFiles.ps1" -Path $BinPath -Tests | Get-Unique 4 | 5 | @{ 6 | "$BinPath" = $SymbolFiles; 7 | } 8 | -------------------------------------------------------------------------------- /tools/publish-CodeCov.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Uploads code coverage to codecov.io 4 | .PARAMETER CodeCovToken 5 | Code coverage token to use 6 | .PARAMETER PathToCodeCoverage 7 | Path to root of code coverage files 8 | .PARAMETER Name 9 | Name to upload with codecoverge 10 | .PARAMETER Flags 11 | Flags to upload with codecoverge 12 | #> 13 | [CmdletBinding()] 14 | Param ( 15 | [Parameter(Mandatory=$true)] 16 | [string]$CodeCovToken, 17 | [Parameter(Mandatory=$true)] 18 | [string]$PathToCodeCoverage, 19 | [string]$Name, 20 | [string]$Flags 21 | ) 22 | 23 | $RepoRoot = (Resolve-Path "$PSScriptRoot/..").Path 24 | 25 | Get-ChildItem -Recurse -LiteralPath $PathToCodeCoverage -Filter "*.cobertura.xml" | % { 26 | $relativeFilePath = Resolve-Path -relative $_.FullName 27 | 28 | Write-Host "Uploading: $relativeFilePath" -ForegroundColor Yellow 29 | & (& "$PSScriptRoot/Get-CodeCovTool.ps1") -t $CodeCovToken -f $relativeFilePath -R $RepoRoot -F $Flags -n $Name 30 | } 31 | -------------------------------------------------------------------------------- /tools/variables/BusinessGroupName.ps1: -------------------------------------------------------------------------------- 1 | 'Visual Studio - VS Core' 2 | -------------------------------------------------------------------------------- /tools/variables/DotNetSdkVersion.ps1: -------------------------------------------------------------------------------- 1 | $globalJson = Get-Content -LiteralPath "$PSScriptRoot\..\..\global.json" | ConvertFrom-Json 2 | $globalJson.sdk.version 3 | -------------------------------------------------------------------------------- /tools/variables/FailsOnMonoFilter.ps1: -------------------------------------------------------------------------------- 1 | # This is a string to append to a net472 test run so that appropriate tests get skipped 2 | if ($IsMacOS -or $IsLinux) { 3 | '&FailsOnMono!=true' # net472 test run will be on mono, so skip tests known to fail there. 4 | } else { 5 | '&FailsOnMono!=x' # Do not filter out any test 6 | } 7 | -------------------------------------------------------------------------------- /tools/variables/InsertJsonValues.ps1: -------------------------------------------------------------------------------- 1 | $vstsDropNames = & "$PSScriptRoot\VstsDropNames.ps1" 2 | $BuildConfiguration = $env:BUILDCONFIGURATION 3 | if (!$BuildConfiguration) { 4 | $BuildConfiguration = 'Debug' 5 | } 6 | 7 | $BasePath = "$PSScriptRoot\..\..\bin\Packages\$BuildConfiguration\Vsix" 8 | 9 | if (Test-Path $BasePath) { 10 | $vsmanFiles = @() 11 | Get-ChildItem $BasePath *.vsman -Recurse -File |% { 12 | $version = (Get-Content $_.FullName | ConvertFrom-Json).info.buildVersion 13 | $fn = $_.Name 14 | $vsmanFiles += "$fn{$version}=https://vsdrop.corp.microsoft.com/file/v1/$vstsDropNames;$fn" 15 | } 16 | 17 | [string]::join(',',$vsmanFiles) 18 | } 19 | -------------------------------------------------------------------------------- /tools/variables/InsertPropsValues.ps1: -------------------------------------------------------------------------------- 1 | $InsertedPkgs = (& "$PSScriptRoot\..\artifacts\VSInsertion.ps1") 2 | 3 | $icv=@() 4 | foreach ($kvp in $InsertedPkgs.GetEnumerator()) { 5 | $kvp.Value |% { 6 | if ($_.Name -match "^(.*?)\.(\d+\.\d+\.\d+(?:\.\d+)?(?:-.*?)?)(?:\.symbols)?\.nupkg$") { 7 | $id = $Matches[1] 8 | $version = $Matches[2] 9 | $icv += "$id=$version" 10 | } 11 | } 12 | } 13 | 14 | Write-Output ([string]::join(',',$icv)) 15 | -------------------------------------------------------------------------------- /tools/variables/InsertTargetBranch.ps1: -------------------------------------------------------------------------------- 1 | # This is the default branch of the VS repo that we will use to insert into VS. 2 | 'main' 3 | -------------------------------------------------------------------------------- /tools/variables/InsertVersionsValues.ps1: -------------------------------------------------------------------------------- 1 | $MacroName = 'StreamJsonRpcVersion' 2 | $SampleProject = "$PSScriptRoot\..\..\src\StreamJsonRpc" 3 | [string]::join(',',(@{ 4 | ($MacroName) = & { (dotnet nbgv get-version --project $SampleProject --format json | ConvertFrom-Json).AssemblyVersion }; 5 | }.GetEnumerator() |% { "$($_.key)=$($_.value)" })) 6 | -------------------------------------------------------------------------------- /tools/variables/LocLanguages.ps1: -------------------------------------------------------------------------------- 1 | ## For faster PR/CI builds localize only for 2 languages, ENU and JPN provide good enough coverage 2 | if ($env:BUILD_REASON -eq 'PullRequest') { 3 | 'ENU,JPN' 4 | } else { 5 | 'VS' 6 | } 7 | -------------------------------------------------------------------------------- /tools/variables/ProfilingInputsDropName.ps1: -------------------------------------------------------------------------------- 1 | if ($env:SYSTEM_TEAMPROJECT) { 2 | "ProfilingInputs/$env:SYSTEM_TEAMPROJECT/$env:BUILD_REPOSITORY_NAME/$env:BUILD_SOURCEBRANCHNAME/$env:BUILD_BUILDID" 3 | } else { 4 | Write-Warning "No Azure Pipelines build detected. No Azure Pipelines drop name will be computed." 5 | } 6 | -------------------------------------------------------------------------------- /tools/variables/ShouldSkipOptimize.ps1: -------------------------------------------------------------------------------- 1 | 'false' 2 | -------------------------------------------------------------------------------- /tools/variables/SymbolsFeatureName.ps1: -------------------------------------------------------------------------------- 1 | 'vs-streamjsonrpc' 2 | -------------------------------------------------------------------------------- /tools/variables/VstsDropNames.ps1: -------------------------------------------------------------------------------- 1 | "Products/$env:SYSTEM_TEAMPROJECT/$env:BUILD_REPOSITORY_NAME/$env:BUILD_SOURCEBRANCHNAME/$env:BUILD_BUILDID" 2 | -------------------------------------------------------------------------------- /tools/variables/_all.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | 3 | <# 4 | .SYNOPSIS 5 | This script returns a hashtable of build variables that should be set 6 | at the start of a build or release definition's execution. 7 | #> 8 | 9 | [CmdletBinding(SupportsShouldProcess = $true)] 10 | param ( 11 | ) 12 | 13 | $vars = @{} 14 | 15 | Get-ChildItem "$PSScriptRoot\*.ps1" -Exclude "_*" |% { 16 | Write-Host "Computing $($_.BaseName) variable" 17 | $vars[$_.BaseName] = & $_ 18 | } 19 | 20 | $vars 21 | -------------------------------------------------------------------------------- /tools/variables/_define.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This script translates the variables returned by the _all.ps1 script 4 | into commands that instruct Azure Pipelines to actually set those variables for other pipeline tasks to consume. 5 | 6 | The build or release definition may have set these variables to override 7 | what the build would do. So only set them if they have not already been set. 8 | #> 9 | 10 | [CmdletBinding()] 11 | param ( 12 | ) 13 | 14 | (& "$PSScriptRoot\_all.ps1").GetEnumerator() |% { 15 | # Always use ALL CAPS for env var names since Azure Pipelines converts variable names to all caps and on non-Windows OS, env vars are case sensitive. 16 | $keyCaps = $_.Key.ToUpper() 17 | if ((Test-Path "env:$keyCaps") -and (Get-Content "env:$keyCaps")) { 18 | Write-Host "Skipping setting $keyCaps because variable is already set to '$(Get-Content env:$keyCaps)'." -ForegroundColor Cyan 19 | } else { 20 | Write-Host "$keyCaps=$($_.Value)" -ForegroundColor Yellow 21 | if ($env:TF_BUILD) { 22 | # Create two variables: the first that can be used by its simple name and accessible only within this job. 23 | Write-Host "##vso[task.setvariable variable=$keyCaps]$($_.Value)" 24 | # and the second that works across jobs and stages but must be fully qualified when referenced. 25 | Write-Host "##vso[task.setvariable variable=$keyCaps;isOutput=true]$($_.Value)" 26 | } elseif ($env:GITHUB_ACTIONS) { 27 | Add-Content -LiteralPath $env:GITHUB_ENV -Value "$keyCaps=$($_.Value)" 28 | } 29 | Set-Item -LiteralPath "env:$keyCaps" -Value $_.Value 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /version.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", 3 | "version": "2.23-alpha", 4 | "publicReleaseRefSpec": [ 5 | "^refs/heads/main$", 6 | "^refs/heads/v\\d+(?:.\\d+)?$" 7 | ], 8 | "cloudBuild": { 9 | "setVersionVariables": false 10 | } 11 | } 12 | --------------------------------------------------------------------------------