remove src/csharp

pull/29225/head
Jan Tattermusch 3 years ago
parent 207bcfdc31
commit 97f2b6d707
  1. 31
      src/csharp/.editorconfig
  2. 17
      src/csharp/.gitignore
  3. 410
      src/csharp/BUILD-INTEGRATION.md
  4. 4
      src/csharp/Directory.Build.props
  5. 9
      src/csharp/Directory.Build.targets
  6. 3
      src/csharp/Grpc.Auth/.gitignore
  7. 111
      src/csharp/Grpc.Auth/GoogleAuthInterceptors.cs
  8. 81
      src/csharp/Grpc.Auth/GoogleGrpcCredentials.cs
  9. 44
      src/csharp/Grpc.Auth/Grpc.Auth.csproj
  10. 29
      src/csharp/Grpc.Auth/Properties/AssemblyInfo.cs
  11. 2
      src/csharp/Grpc.Core.Api/.gitignore
  12. 69
      src/csharp/Grpc.Core.Api/AsyncAuthInterceptor.cs
  13. 98
      src/csharp/Grpc.Core.Api/AsyncCallState.cs
  14. 168
      src/csharp/Grpc.Core.Api/AsyncClientStreamingCall.cs
  15. 145
      src/csharp/Grpc.Core.Api/AsyncDuplexStreamingCall.cs
  16. 126
      src/csharp/Grpc.Core.Api/AsyncServerStreamingCall.cs
  17. 50
      src/csharp/Grpc.Core.Api/AsyncStreamReaderExtensions.cs
  18. 150
      src/csharp/Grpc.Core.Api/AsyncUnaryCall.cs
  19. 112
      src/csharp/Grpc.Core.Api/AuthContext.cs
  20. 109
      src/csharp/Grpc.Core.Api/AuthProperty.cs
  21. 59
      src/csharp/Grpc.Core.Api/BindServiceMethodAttribute.cs
  22. 90
      src/csharp/Grpc.Core.Api/CallCredentials.cs
  23. 39
      src/csharp/Grpc.Core.Api/CallCredentialsConfiguratorBase.cs
  24. 45
      src/csharp/Grpc.Core.Api/CallFlags.cs
  25. 67
      src/csharp/Grpc.Core.Api/CallInvoker.cs
  26. 230
      src/csharp/Grpc.Core.Api/CallOptions.cs
  27. 82
      src/csharp/Grpc.Core.Api/ChannelBase.cs
  28. 134
      src/csharp/Grpc.Core.Api/ChannelCredentials.cs
  29. 44
      src/csharp/Grpc.Core.Api/ChannelCredentialsConfiguratorBase.cs
  30. 231
      src/csharp/Grpc.Core.Api/ClientBase.cs
  31. 59
      src/csharp/Grpc.Core.Api/ContextPropagationOptions.cs
  32. 35
      src/csharp/Grpc.Core.Api/ContextPropagationToken.cs
  33. 66
      src/csharp/Grpc.Core.Api/DeserializationContext.cs
  34. 36
      src/csharp/Grpc.Core.Api/Grpc.Core.Api.csproj
  35. 66
      src/csharp/Grpc.Core.Api/IAsyncStreamReader.cs
  36. 68
      src/csharp/Grpc.Core.Api/IAsyncStreamWriter.cs
  37. 38
      src/csharp/Grpc.Core.Api/IClientStreamWriter.cs
  38. 34
      src/csharp/Grpc.Core.Api/IServerStreamWriter.cs
  39. 143
      src/csharp/Grpc.Core.Api/Interceptors/CallInvokerExtensions.cs
  40. 87
      src/csharp/Grpc.Core.Api/Interceptors/ChannelExtensions.cs
  41. 64
      src/csharp/Grpc.Core.Api/Interceptors/ClientInterceptorContext.cs
  42. 96
      src/csharp/Grpc.Core.Api/Interceptors/InterceptingCallInvoker.cs
  43. 405
      src/csharp/Grpc.Core.Api/Interceptors/Interceptor.cs
  44. 151
      src/csharp/Grpc.Core.Api/Internal/CodeAnalysisAttributes.cs
  45. 60
      src/csharp/Grpc.Core.Api/Internal/UnimplementedCallInvoker.cs
  46. 68
      src/csharp/Grpc.Core.Api/KeyCertificatePair.cs
  47. 138
      src/csharp/Grpc.Core.Api/Marshaller.cs
  48. 497
      src/csharp/Grpc.Core.Api/Metadata.cs
  49. 176
      src/csharp/Grpc.Core.Api/Method.cs
  50. 55
      src/csharp/Grpc.Core.Api/Properties/AssemblyInfo.cs
  51. 109
      src/csharp/Grpc.Core.Api/RpcException.cs
  52. 69
      src/csharp/Grpc.Core.Api/SerializationContext.cs
  53. 163
      src/csharp/Grpc.Core.Api/ServerCallContext.cs
  54. 58
      src/csharp/Grpc.Core.Api/ServerMethods.cs
  55. 161
      src/csharp/Grpc.Core.Api/ServerServiceDefinition.cs
  56. 98
      src/csharp/Grpc.Core.Api/ServiceBinderBase.cs
  57. 122
      src/csharp/Grpc.Core.Api/SslCredentials.cs
  58. 95
      src/csharp/Grpc.Core.Api/Status.cs
  59. 125
      src/csharp/Grpc.Core.Api/StatusCode.cs
  60. 54
      src/csharp/Grpc.Core.Api/Utils/EncodingExtensions.cs
  61. 105
      src/csharp/Grpc.Core.Api/Utils/GrpcPreconditions.cs
  62. 48
      src/csharp/Grpc.Core.Api/VerifyPeerContext.cs
  63. 23
      src/csharp/Grpc.Core.Api/Version.cs
  64. 43
      src/csharp/Grpc.Core.Api/VersionInfo.cs
  65. 74
      src/csharp/Grpc.Core.Api/WriteOptions.cs
  66. 2
      src/csharp/Grpc.Core.NativeDebug/.gitignore
  67. 53
      src/csharp/Grpc.Core.NativeDebug/Grpc.Core.NativeDebug.csproj
  68. 2
      src/csharp/Grpc.Core.Testing/.gitignore
  69. 43
      src/csharp/Grpc.Core.Testing/Grpc.Core.Testing.csproj
  70. 29
      src/csharp/Grpc.Core.Testing/Properties/AssemblyInfo.cs
  71. 10
      src/csharp/Grpc.Core.Testing/Settings.StyleCop
  72. 76
      src/csharp/Grpc.Core.Testing/TestCalls.cs
  73. 107
      src/csharp/Grpc.Core.Testing/TestServerCallContext.cs
  74. 3
      src/csharp/Grpc.Core.Tests/.gitignore
  75. 77
      src/csharp/Grpc.Core.Tests/AppDomainUnloadTest.cs
  76. 71
      src/csharp/Grpc.Core.Tests/AuthContextTest.cs
  77. 67
      src/csharp/Grpc.Core.Tests/AuthPropertyTest.cs
  78. 48
      src/csharp/Grpc.Core.Tests/CallAfterShutdownTest.cs
  79. 193
      src/csharp/Grpc.Core.Tests/CallCancellationTest.cs
  80. 50
      src/csharp/Grpc.Core.Tests/CallCredentialsTest.cs
  81. 98
      src/csharp/Grpc.Core.Tests/CallOptionsTest.cs
  82. 90
      src/csharp/Grpc.Core.Tests/ChannelConnectivityTest.cs
  83. 67
      src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs
  84. 90
      src/csharp/Grpc.Core.Tests/ChannelOptionsTest.cs
  85. 127
      src/csharp/Grpc.Core.Tests/ChannelTest.cs
  86. 410
      src/csharp/Grpc.Core.Tests/ClientServerTest.cs
  87. 153
      src/csharp/Grpc.Core.Tests/CompressionTest.cs
  88. 181
      src/csharp/Grpc.Core.Tests/ContextPropagationTest.cs
  89. 121
      src/csharp/Grpc.Core.Tests/ContextualMarshallerTest.cs
  90. 50
      src/csharp/Grpc.Core.Tests/FakeCredentials.cs
  91. 30
      src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
  92. 94
      src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs
  93. 82
      src/csharp/Grpc.Core.Tests/HalfcloseTest.cs
  94. 228
      src/csharp/Grpc.Core.Tests/Interceptors/ClientInterceptorTest.cs
  95. 173
      src/csharp/Grpc.Core.Tests/Interceptors/ServerInterceptorTest.cs
  96. 194
      src/csharp/Grpc.Core.Tests/Internal/AsyncCallServerTest.cs
  97. 82
      src/csharp/Grpc.Core.Tests/Internal/AsyncCallStateTest.cs
  98. 766
      src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs
  99. 60
      src/csharp/Grpc.Core.Tests/Internal/ChannelArgsSafeHandleTest.cs
  100. 40
      src/csharp/Grpc.Core.Tests/Internal/CompletionQueueEventTest.cs
  101. Some files were not shown because too many files have changed in this diff Show More

@ -1,31 +0,0 @@
root = true
[**]
charset = utf-8 ; Implies no BOM.
end_of_line = LF
indent_style = space
indent_size = 4
insert_final_newline = true
tab_width = 4
; https://docs.microsoft.com/visualstudio/ide/editorconfig-code-style-settings-reference
[*.cs]
dotnet_sort_system_directives_first = true
csharp_new_line_before_open_brace = accessors, anonymous_methods, control_blocks, events, indexers, local_functions, methods, properties, types
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_indent_case_contents = true
csharp_indent_switch_labels = true
csharp_space_after_cast = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_around_binary_operators = before_and_after
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_preserve_single_line_statements = true
csharp_preserve_single_line_blocks = true

@ -1,17 +0,0 @@
*.userprefs
*.user
*.lock.json
/*.suo
/*.sdf
/.vs/
bin/
obj/
*.nupkg
StyleCop.Cache
/packages/
/protoc_plugins/
test-results/
TestResult.xml
coverage_results.xml
/TestResults

@ -1,410 +0,0 @@
Protocol Buffers/gRPC Codegen Integration Into .NET Build
=================================================
With Grpc.Tools package version 1.17 we made it easier to compile .proto files
in your project using the `dotnet build` command, Visual Studio, or command-line
MSBuild. You need to configure the .csproj project according to the way you want
to integrate Protocol Buffer files into your build.
There is also a Reference section at the end of the file.
Common scenarios
----------------
### I just want to compile .proto files into my library
This is the approach taken by the examples in the `csharp/examples` directory.
Protoc output files (for example, `Helloworld.cs` and `HelloworldGrpc.cs`
compiled from `helloworld.proto`) are placed among *object* and other temporary
files of your project, and automatically provided as inputs to the C# compiler.
As with other automatically generated .cs files, they are included in the source
and symbols NuGet package, if you build one.
Simply reference your .proto files in a `<Protobuf>` item group. The following
example will add all .proto files in a project and all its subdirectories
(excluding special directories such as `bin` and `obj`):
```xml
<ItemGroup>
<Protobuf Include="**/*.proto" />
</ItemGroup>
```
You must add a reference to the NuGet packages Grpc.Tools and Grpc (the latter
is a meta-package, in turn referencing Grpc.Core and Google.Protobuf packages).
It is **very important** to mark Grpc.Tools as a development-only dependency, so
that the *users* of your library do not fetch the tools package:
* "dotnet SDK" .csproj (Visual Studio, `dotnet new`): Add an attribute
`PrivateAssets="All"` to the Grpc.Tools package reference. See an example in the
[Greeter.csproj](../../examples/csharp/Helloworld/Greeter/Greeter.csproj#L10)
example project in this repository. If adding a package reference in Visual
Studio, edit the project file and add this attribute. [This is a bug in NuGet
client](https://github.com/NuGet/Home/issues/4125).
* "Classic" .csproj with `packages.config` (Visual Studio before 2017, old versions of Mono):
This is handled automatically by NuGet after you set the `developmentDependency="true"`
attribute on the `<package>` tag in `packages.config`.
If building a NuGet package from your library with the nuget command line tool
from a .nuspec file, then the spec file may (and probably should) reference the
Grpc metapackage, but **do not add a reference to Grpc.Tools** to it. "dotnet SDK"
projects handle this automatically when called from `dotnet pack` by excluding
any packages with private assets, such as thus marked Grpc.Tools.
#### Per-file options that can be set in Visual Studio
For a "dotnet SDK" project, you have more control of some frequently used options.
**You may need to open and close Visual Studio** for this form to appear in the
properties window after adding a reference to Grpc.Tools package (we do not know
whether this is a bug or by design, but it looks like a bug):
![Properties in an SDK project](doc/integration.md-fig.2-sdk.png)
You can also change options of multiple files at once by selecting them in the
Project Explorer together.
For a "classic" project, you can only add .proto files with all options set to
default (if you find it necessary to modify these options, then hand-edit the
.csproj file). Click on the "show all files" button, add files to project, then
change file type of the .proto files to "Protobuf" in the Properties window
drop-down. This menu item will appear after you import the Grpc.Tools package:
![Properties in a classic project](doc/integration.md-fig.1-classic.png)
See the Reference section at end of this file for options that can be set
per-file by modifying the source .csproj directly.
#### My .proto files are in a directory outside the project
Refer to the example files
[RouteGuide.csproj](../../examples/csharp/RouteGuide/RouteGuide/RouteGuide.csproj#L58-L60)
and [Greeter.csproj](../../examples/csharp/Helloworld/Greeter/Greeter.csproj#L11)
in this repository. For the files to show up in Visual Studio properly, add a
`Link` attribute with just a filename to the `<Protobuf>` item. This will be the
display name of the file. In the `Include` attribute, specify the complete path
to file. A relative path is based off the project directory.
Or, if using Visual Studio, add files _as links_ from outside directory. In the
Add Files dialog, there is a little [down arrow near the Open
button](https://stackoverflow.com/a/9770061). Click on it, and choose "Add as
link". If you do not select this option, Visual Studio will copy files to the
project directory instead.
#### My .proto files have same filename in different folders
Starting from Grpc.Tools version 2.31, protocol buffers compilation preserves original folder structure for generated files. Eg.
- `../ProjectFolder/Protos/v2/http.proto`
- `../ProjectFolder/Protos/v3/http.proto`
Will result in:
- `../ProjectFolder/obj/CONFIGURATION/FRAMEWORK/Protos/v2/Greet.cs`
- `../ProjectFolder/obj/CONFIGURATION/FRAMEWORK/Protos/v2/GreetGrpc.cs`
- `../ProjectFolder/obj/CONFIGURATION/FRAMEWORK/Protos/v3/Greet.cs`
- `../ProjectFolder/obj/CONFIGURATION/FRAMEWORK/Protos/v3/GreetGrpc.cs`
This feature resolves problems we have faced in large projects. Moreover, There is now also a project-wide new option Protobuf_ProtoRoot to define the fallback ProtoRoot. If the ProtoRoot is set, this also reduces the amount of problems that lead to duplicates. Eg.
```xml
<ItemGroup>
<Protobuf Include="Protos\v2\greet.proto" ProtoRoot="Protos" />
</ItemGroup>
```
Before Grpc.Tools version 2.31 all .proto files were compiled into `obj` directory, flattening relative paths. For proto files with duplicated names it cause following errors `NETSDK1022 Duplicate 'Compile' items were included. [...]` or `MSB3105 [...] Duplicate items are not supported by the "Sources" parameter`. The workaround for this problem was introducing relative paths in your `obj` folder, by manipulating output path. Eg.
```xml
<ItemGroup>
<Protobuf Include="Protos/v2/http.proto" OutputDir="$(Protobuf_OutputPath)%(RelativeDir)" />
<Protobuf Include="Protos/v3/http.proto" OutputDir="$(Protobuf_OutputPath)%(RelativeDir)" />
</ItemGroup>
```
__Note, this was a workaround approach, we recommend updating Grpc.Tools to the latest version.__
### I just want to generate proto and gRPC C# sources from my .proto files (no C# compile)
Suppose you want to place generated files right beside each respective source
.proto file. Create a .csproj library file in the common root of your .proto
tree, and add a reference to Grpc.Tools package (this works in Windows too, `$`
below stands for a command prompt in either platform):
```
/myproject/myprotofiles$ dotnet new classlib
. . .
Restoring packages for /myproject/myprotofiles/myprotofiles.csproj...
. . .
/myproject/myprotofiles$ rm *.cs <-- remove all *.cs files from template;
C:\myproject\myprotofiles> del *.cs /y <-- on Windows, use the del command instead.
/myproject/myprotofiles$ dotnet add package Grpc.Tools
```
(the latter command also accepts an optional `--version X.Y` switch for a
specific version of package, should you need one). Next open the generated
.csproj file in a text editor.
Since you are not building a package, you may not worry about adding
`PrivateAssets="All"` attribute, but it will not hurt, in case you are
repurposing the project at some time later. The important part is (1) tell the
gRPC tools to select the whole directory of files; (2) order placement of each
output besides its source, and (3) not compile the generated .cs files. Add the
following stanza under the `<Project>` xml node:
```xml
<ItemGroup>
<Protobuf Include="**/*.proto" OutputDir="%(RelativeDir)" CompileOutputs="false" />
</ItemGroup>
```
The `Include` tells the build system to recursively examine project directory
and its subdirectories (`**`) include all files matching the wildcard `*.proto`.
You can instead selectively include your files or selectively exclude files from
the glob pattern; [MSBuild documentation explains
that](https://docs.microsoft.com/visualstudio/msbuild/msbuild-items). The
`OutputDir="%(RelativeDir)"` orders the output directory for each .cs file be
same as the corresponding .proto directory. Finally, `CompileOutputs="false"`
prevents compiling the generated files into an assembly.
Note that an empty assembly is still generated, but you should ignore it. As
with any build system, it is used to detect out-of-date dependencies and
recompile them.
#### I am getting a warning about a missing expected file!
When we are preparing compile, there is no way to know whether a given proto
file will produce a *Grpc.cs output or not. If the proto file has a `service`
clause, it will; otherwise, it won't, but the build script cannot know that in
advance. When we are treating generated .cs files as temporary, this is ok, but
when generating them for you, creating empty files is probably not. You need to
tell the compiler which files should be compiled with gRPC services, and which
only contain protobuffer message definitions.
One option is just ignore the warning. Another is quench it by setting the
property `Protobuf_NoWarnMissingExpected` to `true`:
```xml
<PropertyGroup>
<Protobuf_NoWarnMissingExpected>true</Protobuf_NoWarnMissingExpected>
</PropertyGroup>
```
For a small to medium projects this is sufficient. But because of a missing
output dependency, the corresponding .proto file will be recompiled on every
build. If your project is large, or if other large builds depend on generated
files, and are also needlessly recompiled, you'll want to prevent these rebuilds
when files have not in fact changed, as follows:
##### Explicitly tell protoc for which files it should use the gRPC plugin
You need to set the `Protobuf` item property `GrpcServices` to `None` for those
.proto inputs which do not have a `service` declared (or, optionally, those
which do but you do not want a service/client stub for). The default value for
the `GrpcServices` is `Both` (both client and server stub are generated). This
is easy enough to do with glob patterns if your files are laid out in
directories according to their service use, for example:
```xml
<ItemGroup>
<Protobuf Include="**/*.proto" OutputDir="%(RelativeDir)"
CompileOutputs="false" GrpcServices="None" />
<Protobuf Update="**/hello/*.proto;**/bye/*.proto" GrpcServices="Both" />
</ItemGroup>
```
In this sample, all .proto files are compiled with `GrpcServices="None"`, except
for .proto files in subdirectories on any tree level named `hello/` and `bye`,
which will take `GrpcServices="Both"` Note the use of the `Update` attribute
instead of `Include`. If you write `Include` by mistake, the files will be added
to compile *twice*, once with, and once without GrpcServices. Pay attention not
to do that!
Another example would be the use of globbing if your service .proto files are
named according to a pattern, for example `*_services.proto`. In this case, The
`Update` attribute can be written as `Update="**/*_service.proto"`, to set the
attribute `GrpcServices="Both"` only on these files.
But what if no patterns work, and you cannot sort a large set of .proto file
into those containing a service and those not? As a last resort,
##### Force creating empty .cs files for missing outputs.
Naturally, this results in a dirtier compiler output tree, but you may clean it
using other ways (for example, by not copying zero-length .cs files to their
final destination). Remember, though, that the files are still important to keep
in their output locations to prevent needless recompilation. You may force
generating empty files by setting the property `Protobuf_TouchMissingExpected`
to `true`:
```xml
<PropertyGroup>
<Protobuf_TouchMissingExpected>true</Protobuf_TouchMissingExpected>
</PropertyGroup>
```
#### But I do not use gRPC at all, I need only protobuffer messages compiled
Set `GrpcServices="None"` on all proto files:
```xml
<ItemGroup>
<Protobuf Include="**/*.proto" OutputDir="%(RelativeDir)"
CompileOutputs="false" GrpcServices="None" />
</ItemGroup>
```
#### That's good so far, but I do not want the `bin` and `obj` directories in my tree
You may create the project in a subdirectory of the root of your files, such as,
for example, `.build`. In this case, you want to refer to the proto files
relative to that `.build/` directory as
```xml
<ItemGroup>
<Protobuf Include="../**/*.proto" ProtoRoot=".."
OutputDir="%(RelativeDir)" CompileOutputs="false" />
</ItemGroup>
```
Pay attention to the `ProtoRoot` property. It needs to be set to the directory
where `import` declarations in the .proto files are looking for files, since the
project root is no longer the same as the proto root.
Alternatively, you may place the project in a directory *above* your proto root,
and refer to the files with a subdirectory name:
```xml
<ItemGroup>
<Protobuf Include="proto_root/**/*.proto" ProtoRoot="proto_root"
OutputDir="%(RelativeDir)" CompileOutputs="false" />
</ItemGroup>
```
### Alas, this all is nice, but my scenario is more complex, -OR-
### I'll investigate that when I have time. I just want to run protoc as I did before.
One option is examine our [.targets and .props files](Grpc.Tools/build/) and see
if you can create your own build sequence from the provided targets so that it
fits your needs. Also please open an issue (and tag @kkm000 in it!) with your
scenario. We'll try to support it if it appears general enough.
But if you just want to run `protoc` using MsBuild `<Exec>` task, as you
probably did before the version 1.17 of Grpc.Tools, we have a few build
variables that point to resolved names of tools and common protoc imports.
You'll have to roll your own dependency checking (or go with a full
recompilation each time, if that works for you), but at the very least each
version of the Tools package will point to the correct location of the files,
and resolve the compiler and plugin executables appropriate for the host system.
These property variables are:
* `Protobuf_ProtocFullPath` points to the full path and filename of protoc executable, e. g.,
"C:\Users\kkm\.nuget\packages\grpc.tools\1.17.0\build\native\bin\windows\protoc.exe".
* `gRPC_PluginFullPath` points to the full path and filename of gRPC plugin, such as
"C:\Users\kkm\.nuget\packages\grpc.tools\1.17.0\build\native\bin\windows\grpc_csharp_plugin.exe"
* `Protobuf_StandardImportsPath` points to the standard proto import directory, for example,
"C:\Users\kkm\.nuget\packages\grpc.tools\1.17.0\build\native\include". This is
the directory where a declaration such as `import "google/protobuf/wrappers.proto";`
in a proto file would find its target.
Use MSBuild property expansion syntax `$(VariableName)` in your protoc command
line to substitute these variables, for instance,
```xml
<Target Name="MyProtoCompile">
<PropertyGroup>
<ProtoCCommand>$(Protobuf_ProtocFullPath) --plugin=protoc-gen-grpc=$(gRPC_PluginFullPath) -I $(Protobuf_StandardImportsPath) ....rest of your command.... </ProtoCCommand>
</PropertyGroup>
<Message Importance="high" Text="$(ProtoCCommand)" />
<Exec Command="$(ProtoCCommand)" />
</Target>
```
Also make sure *not* to include any file names to the `Protobuf` item
collection, otherwise they will be compiled by default. If, by any chance, you
used that name for your build scripting, you must rename it.
### What about C++ projects?
This is in the works. Currently, the same variables as above are set to point to
the protoc binary, C++ gRPC plugin and the standard imports, but nothing else.
Do not use the `Protobuf` item collection name so that your project remains
future-proof. We'll use it for C++ projects too.
Reference
---------
### Protobuf item metadata reference
The following metadata are recognized on the `<Protobuf>` items.
| Name | Default | Value | Synopsis |
|----------------|-----------|----------------------|----------------------------------|
| Access | `public` | `public`, `internal` | Generated class access |
| AdditionalProtocArguments | | arbitrary cmdline arguments | Extra command line flags passed to `protoc` command |
| ProtoCompile | `true` | `true`, `false` | Pass files to protoc? |
| ProtoRoot | See notes | A directory | Common root for set of files |
| CompileOutputs | `true` | `true`, `false` | C#-compile generated files? |
| OutputDir | See notes | A directory | Directory for generated C# files with protobuf messages |
| OutputOptions | | arbitrary options | Extra options passed to C# codegen as `--csharp_opt=opt1,opt2` |
| GrpcOutputDir | See notes | A directory | Directory for generated gRPC stubs |
| GrpcOutputOptions | | arbitrary options | Extra options passed to gRPC codegen as `--grpc_opt=opt1,opt2` |
| GrpcServices | `both` | `none`, `client`, `server`, `both` | Generated gRPC stubs |
__Notes__
* __ProtoRoot__
For files _inside_ the project cone, `ProtoRoot` is set by default to the
project directory. For every file _outside_ of the project directory, the value
is set to this file's containing directory name, individually per file. If you
include a subtree of proto files that lies outside of the project directory, you
need to set this metadatum. There is an example in this file above. The path in
this variable is relative to the project directory.
* __OutputDir__
The default value for this metadatum is the value of the property
`Protobuf_OutputPath`. This property, in turn, unless you set it in your
project, will be set to the value of the standard MSBuild property
`IntermediateOutputPath`, which points to the location of compilation object
outputs, such as "obj/Release/netstandard1.5/". The path in this property is
considered relative to the project directory.
* __GrpcOutputDir__
Unless explicitly set, will follow `OutputDir` for any given file.
* __Access__
Sets generated class access on _both_ generated message and gRPC stub classes.
* __AdditionalProtocArguments__
Pass additional commandline arguments to the `protoc` command being invoked.
Normally this option should not be used, but it exists for scenarios when you need to pass
otherwise unsupported (e.g. experimental) flags to protocol buffer compiler.
* __GrpcOutputOptions__
Pass additional options to the `grpc_csharp_plugin` in form of the `--grpc_opt` flag.
Normally this option should not be used as it's values are already controlled by "Access"
and "GrpcServices" metadata, but it might be useful in situations where you want
to explicitly pass some otherwise unsupported (e.g. experimental) options to the
`grpc_csharp_plugin`.
`grpc_csharp_plugin` command line options
---------
Under the hood, the `Grpc.Tools` build integration invokes the `protoc` and `grpc_csharp_plugin` binaries
to perform code generation. Here is an overview of the available `grpc_csharp_plugin` options:
| Name | Default | Synopsis |
|---------------- |-----------|----------------------------------------------------------|
| no_client | off | Don't generate the client stub |
| no_server | off | Don't generate the server-side stub |
| internal_access | off | Generate classes with "internal" visibility |
Note that the protocol buffer compiler has a special commandline syntax for plugin options.
Example:
```
protoc --plugin=protoc-gen-grpc=grpc_csharp_plugin --csharp_out=OUT_DIR \
--grpc_out=OUT_DIR --grpc_opt=lite_client,no_server \
-I INCLUDE_DIR foo.proto
```

@ -1,4 +0,0 @@
<Project>
<Import Project="build\dependencies.props" />
<Import Project="build\common.props" />
</Project>

@ -1,9 +0,0 @@
<Project>
<!-- Needed for the net45 build to work on Unix. See https://github.com/dotnet/designs/pull/33 -->
<ItemGroup>
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

@ -1,3 +0,0 @@
bin
obj
*.nupkg

@ -1,111 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System.Threading;
using System.Threading.Tasks;
using Google.Apis.Auth.OAuth2;
using Grpc.Core;
using Grpc.Core.Utils;
namespace Grpc.Auth
{
/// <summary>
/// Factory methods to create authorization interceptors for Google credentials.
/// <seealso cref="GoogleGrpcCredentials"/>
/// </summary>
public static class GoogleAuthInterceptors
{
private const string AuthorizationHeader = "Authorization";
private const string Schema = "Bearer";
/// <summary>
/// Creates an <see cref="AsyncAuthInterceptor"/> that will obtain access token from any credential type that implements
/// <c>ITokenAccess</c>. (e.g. <c>GoogleCredential</c>).
/// </summary>
/// <param name="credential">The credential to use to obtain access tokens.</param>
/// <returns>The interceptor.</returns>
public static AsyncAuthInterceptor FromCredential(ITokenAccess credential)
{
if (credential is ITokenAccessWithHeaders credentialWithHeaders)
{
return FromCredential(credentialWithHeaders);
}
return new AsyncAuthInterceptor(async (context, metadata) =>
{
var accessToken = await credential.GetAccessTokenForRequestAsync(context.ServiceUrl, CancellationToken.None).ConfigureAwait(false);
metadata.Add(CreateBearerTokenHeader(accessToken));
});
}
/// <summary>
/// Creates an <see cref="AsyncAuthInterceptor"/> that will obtain access token and associated information
/// from any credential type that implements <see cref="ITokenAccessWithHeaders"/>
/// </summary>
/// <param name="credential">The credential to use to obtain access tokens.</param>
/// <returns>The interceptor.</returns>
public static AsyncAuthInterceptor FromCredential(ITokenAccessWithHeaders credential)
{
return new AsyncAuthInterceptor(async (context, metadata) =>
{
AccessTokenWithHeaders tokenAndHeaders = await credential.GetAccessTokenWithHeadersForRequestAsync(context.ServiceUrl, CancellationToken.None).ConfigureAwait(false);
metadata.Add(CreateBearerTokenHeader(tokenAndHeaders.AccessToken));
foreach (var header in tokenAndHeaders.Headers)
{
foreach (var headerValue in header.Value)
{
metadata.Add(new Metadata.Entry(header.Key, headerValue));
}
}
});
}
/// <summary>
/// Creates an <see cref="AsyncAuthInterceptor"/> that will use given access token as authorization.
/// </summary>
/// <param name="accessToken">OAuth2 access token.</param>
/// <returns>The interceptor.</returns>
public static AsyncAuthInterceptor FromAccessToken(string accessToken)
{
GrpcPreconditions.CheckNotNull(accessToken);
return new AsyncAuthInterceptor((context, metadata) =>
{
metadata.Add(CreateBearerTokenHeader(accessToken));
return GetCompletedTask();
});
}
private static Metadata.Entry CreateBearerTokenHeader(string accessToken)
{
return new Metadata.Entry(AuthorizationHeader, Schema + " " + accessToken);
}
/// <summary>
/// Framework independent equivalent of <c>Task.CompletedTask</c>.
/// </summary>
private static Task GetCompletedTask()
{
#if NETSTANDARD
return Task.CompletedTask;
#else
return Task.FromResult<object>(null); // for .NET45, emulate the functionality
#endif
}
}
}

@ -1,81 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Threading;
using System.Threading.Tasks;
using Google.Apis.Auth.OAuth2;
using Grpc.Core;
using Grpc.Core.Utils;
namespace Grpc.Auth
{
/// <summary>
/// Factory/extension methods to create instances of <see cref="ChannelCredentials"/> and <see cref="CallCredentials"/> classes
/// based on credential objects originating from Google auth library.
/// </summary>
public static class GoogleGrpcCredentials
{
/// <summary>
/// Retrieves an instance of Google's Application Default Credentials using
/// <c>GoogleCredential.GetApplicationDefaultAsync()</c> and converts them
/// into a gRPC <see cref="ChannelCredentials"/> that use the default SSL credentials.
/// </summary>
/// <returns>The <c>ChannelCredentials</c> instance.</returns>
public static async Task<ChannelCredentials> GetApplicationDefaultAsync()
{
var googleCredential = await GoogleCredential.GetApplicationDefaultAsync().ConfigureAwait(false);
return googleCredential.ToChannelCredentials();
}
/// <summary>
/// Creates an instance of <see cref="CallCredentials"/> that will use given access token to authenticate
/// with a gRPC service.
/// </summary>
/// <param name="accessToken">OAuth2 access token.</param>
/// /// <returns>The <c>MetadataCredentials</c> instance.</returns>
public static CallCredentials FromAccessToken(string accessToken)
{
return CallCredentials.FromInterceptor(GoogleAuthInterceptors.FromAccessToken(accessToken));
}
/// <summary>
/// Converts a <c>ITokenAccess</c> (e.g. <c>GoogleCredential</c>) object
/// into a gRPC <see cref="CallCredentials"/> object.
/// </summary>
/// <param name="credential">The credential to use to obtain access tokens.</param>
/// <returns>The <c>CallCredentials</c> instance.</returns>
public static CallCredentials ToCallCredentials(this ITokenAccess credential)
{
return CallCredentials.FromInterceptor(GoogleAuthInterceptors.FromCredential(credential));
}
/// <summary>
/// Converts a <c>ITokenAccess</c> (e.g. <c>GoogleCredential</c>) object
/// into a gRPC <see cref="ChannelCredentials"/> object.
/// Default SSL credentials are used.
/// </summary>
/// <param name="googleCredential">The credential to use to obtain access tokens.</param>
/// <returns>>The <c>ChannelCredentials</c> instance.</returns>
public static ChannelCredentials ToChannelCredentials(this ITokenAccess googleCredential)
{
return ChannelCredentials.Create(new SslCredentials(), googleCredential.ToCallCredentials());
}
}
}

@ -1,44 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Authors>The gRPC Authors</Authors>
<Copyright>Copyright 2015 The gRPC Authors</Copyright>
<Description>gRPC C# Authentication Library</Description>
<PackageIcon>packageIcon.png</PackageIcon>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/grpc/grpc</PackageProjectUrl>
<PackageTags>gRPC RPC HTTP/2 Auth OAuth2</PackageTags>
<VersionPrefix>$(GrpcCsharpVersion)</VersionPrefix>
</PropertyGroup>
<PropertyGroup>
<TargetFrameworks>net45;netstandard1.5;netstandard2.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<Import Project="..\Grpc.Core\SourceLink.csproj.include" />
<ItemGroup>
<None Include="../packageIcon.png" Pack="true" PackagePath="\"/>
</ItemGroup>
<ItemGroup>
<Compile Include="..\Grpc.Core.Api\Version.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../Grpc.Core.Api/Grpc.Core.Api.csproj">
<PrivateAssets>None</PrivateAssets>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Google.Apis.Auth" Version="1.46.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
<Reference Include="System" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
</Project>

@ -1,29 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System.Reflection;
using System.Runtime.CompilerServices;
[assembly: AssemblyTitle("Grpc.Auth")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("Google Inc. All rights reserved.")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

@ -1,69 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
/// Asynchronous authentication interceptor for <see cref="CallCredentials"/>.
/// </summary>
/// <param name="context">The interceptor context.</param>
/// <param name="metadata">Metadata to populate with entries that will be added to outgoing call's headers.</param>
/// <returns></returns>
public delegate Task AsyncAuthInterceptor(AuthInterceptorContext context, Metadata metadata);
/// <summary>
/// Context for an RPC being intercepted by <see cref="AsyncAuthInterceptor"/>.
/// </summary>
public class AuthInterceptorContext
{
readonly string serviceUrl;
readonly string methodName;
/// <summary>
/// Initializes a new instance of <c>AuthInterceptorContext</c>.
/// </summary>
public AuthInterceptorContext(string serviceUrl, string methodName)
{
this.serviceUrl = GrpcPreconditions.CheckNotNull(serviceUrl);
this.methodName = GrpcPreconditions.CheckNotNull(methodName);
}
/// <summary>
/// The fully qualified service URL for the RPC being called.
/// </summary>
public string ServiceUrl
{
get { return serviceUrl; }
}
/// <summary>
/// The method name of the RPC being called.
/// </summary>
public string MethodName
{
get { return methodName; }
}
}
}

@ -1,98 +0,0 @@
#region Copyright notice and license
// Copyright 2019 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Threading.Tasks;
namespace Grpc.Core
{
/// <summary>
/// Provides an abstraction over the callback providers
/// used by AsyncUnaryCall, AsyncDuplexStreamingCall, etc
/// </summary>
internal struct AsyncCallState
{
readonly object responseHeadersAsync; // Task<Metadata> or Func<object, Task<Metadata>>
readonly object getStatusFunc; // Func<Status> or Func<object, Status>
readonly object getTrailersFunc; // Func<Metadata> or Func<object, Metadata>
readonly object disposeAction; // Action or Action<object>
readonly object? callbackState; // arg0 for the callbacks above, if needed
internal AsyncCallState(
Func<object, Task<Metadata>> responseHeadersAsync,
Func<object, Status> getStatusFunc,
Func<object, Metadata> getTrailersFunc,
Action<object> disposeAction,
object callbackState)
{
this.responseHeadersAsync = responseHeadersAsync;
this.getStatusFunc = getStatusFunc;
this.getTrailersFunc = getTrailersFunc;
this.disposeAction = disposeAction;
this.callbackState = callbackState;
}
internal AsyncCallState(
Task<Metadata> responseHeadersAsync,
Func<Status> getStatusFunc,
Func<Metadata> getTrailersFunc,
Action disposeAction)
{
this.responseHeadersAsync = responseHeadersAsync;
this.getStatusFunc = getStatusFunc;
this.getTrailersFunc = getTrailersFunc;
this.disposeAction = disposeAction;
this.callbackState = null;
}
internal Task<Metadata> ResponseHeadersAsync()
{
var withState = responseHeadersAsync as Func<object, Task<Metadata>>;
return withState != null ? withState(callbackState!)
: (Task<Metadata>)responseHeadersAsync;
}
internal Status GetStatus()
{
var withState = getStatusFunc as Func<object, Status>;
return withState != null ? withState(callbackState!)
: ((Func<Status>)getStatusFunc)();
}
internal Metadata GetTrailers()
{
var withState = getTrailersFunc as Func<object, Metadata>;
return withState != null ? withState(callbackState!)
: ((Func<Metadata>)getTrailersFunc)();
}
internal void Dispose()
{
var withState = disposeAction as Action<object>;
if (withState != null)
{
withState(callbackState!);
}
else
{
((Action)disposeAction)();
}
}
}
}

@ -1,168 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace Grpc.Core
{
/// <summary>
/// Return type for client streaming calls.
/// </summary>
/// <typeparam name="TRequest">Request message type for this call.</typeparam>
/// <typeparam name="TResponse">Response message type for this call.</typeparam>
public sealed class AsyncClientStreamingCall<TRequest, TResponse> : IDisposable
{
readonly IClientStreamWriter<TRequest> requestStream;
readonly Task<TResponse> responseAsync;
readonly AsyncCallState callState;
/// <summary>
/// Creates a new AsyncClientStreamingCall object with the specified properties.
/// </summary>
/// <param name="requestStream">Stream of request values.</param>
/// <param name="responseAsync">The response of the asynchronous call.</param>
/// <param name="responseHeadersAsync">Response headers of the asynchronous call.</param>
/// <param name="getStatusFunc">Delegate returning the status of the call.</param>
/// <param name="getTrailersFunc">Delegate returning the trailing metadata of the call.</param>
/// <param name="disposeAction">Delegate to invoke when Dispose is called on the call object.</param>
public AsyncClientStreamingCall(IClientStreamWriter<TRequest> requestStream,
Task<TResponse> responseAsync,
Task<Metadata> responseHeadersAsync,
Func<Status> getStatusFunc,
Func<Metadata> getTrailersFunc,
Action disposeAction)
{
this.requestStream = requestStream;
this.responseAsync = responseAsync;
this.callState = new AsyncCallState(responseHeadersAsync, getStatusFunc, getTrailersFunc, disposeAction);
}
/// <summary>
/// Creates a new AsyncClientStreamingCall object with the specified properties.
/// </summary>
/// <param name="requestStream">Stream of request values.</param>
/// <param name="responseAsync">The response of the asynchronous call.</param>
/// <param name="responseHeadersAsync">Response headers of the asynchronous call.</param>
/// <param name="getStatusFunc">Delegate returning the status of the call.</param>
/// <param name="getTrailersFunc">Delegate returning the trailing metadata of the call.</param>
/// <param name="disposeAction">Delegate to invoke when Dispose is called on the call object.</param>
/// <param name="state">State object for use with the callback parameters.</param>
public AsyncClientStreamingCall(IClientStreamWriter<TRequest> requestStream,
Task<TResponse> responseAsync,
Func<object, Task<Metadata>> responseHeadersAsync,
Func<object, Status> getStatusFunc,
Func<object, Metadata> getTrailersFunc,
Action<object> disposeAction,
object state)
{
this.requestStream = requestStream;
this.responseAsync = responseAsync;
this.callState = new AsyncCallState(responseHeadersAsync, getStatusFunc, getTrailersFunc, disposeAction, state);
}
/// <summary>
/// Asynchronous call result.
/// </summary>
public Task<TResponse> ResponseAsync
{
get
{
return this.responseAsync;
}
}
/// <summary>
/// Asynchronous access to response headers.
/// </summary>
public Task<Metadata> ResponseHeadersAsync
{
get
{
return callState.ResponseHeadersAsync();
}
}
/// <summary>
/// Async stream to send streaming requests.
/// </summary>
public IClientStreamWriter<TRequest> RequestStream
{
get
{
return requestStream;
}
}
/// <summary>
/// Gets an awaiter used to await this <see cref="AsyncClientStreamingCall{TRequest,TResponse}"/>.
/// </summary>
/// <returns>An awaiter instance.</returns>
/// <remarks>This method is intended for compiler use rather than use directly in code.</remarks>
public TaskAwaiter<TResponse> GetAwaiter()
{
return responseAsync.GetAwaiter();
}
/// <summary>
/// Configures an awaiter used to await this <see cref="AsyncClientStreamingCall{TRequest,TResponse}"/>.
/// </summary>
/// <param name="continueOnCapturedContext">
/// true to attempt to marshal the continuation back to the original context captured; otherwise, false.
/// </param>
/// <returns>An object used to await this task.</returns>
public ConfiguredTaskAwaitable<TResponse> ConfigureAwait(bool continueOnCapturedContext)
{
return responseAsync.ConfigureAwait(continueOnCapturedContext);
}
/// <summary>
/// Gets the call status if the call has already finished.
/// Throws InvalidOperationException otherwise.
/// </summary>
public Status GetStatus()
{
return callState.GetStatus();
}
/// <summary>
/// Gets the call trailing metadata if the call has already finished.
/// Throws InvalidOperationException otherwise.
/// </summary>
public Metadata GetTrailers()
{
return callState.GetTrailers();
}
/// <summary>
/// Provides means to cleanup after the call.
/// If the call has already finished normally (request stream has been completed and call result has been received), doesn't do anything.
/// Otherwise, requests cancellation of the call which should terminate all pending async operations associated with the call.
/// As a result, all resources being used by the call should be released eventually.
/// </summary>
/// <remarks>
/// Normally, there is no need for you to dispose the call unless you want to utilize the
/// "Cancel" semantics of invoking <c>Dispose</c>.
/// </remarks>
public void Dispose()
{
callState.Dispose();
}
}
}

@ -1,145 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Threading.Tasks;
namespace Grpc.Core
{
/// <summary>
/// Return type for bidirectional streaming calls.
/// </summary>
/// <typeparam name="TRequest">Request message type for this call.</typeparam>
/// <typeparam name="TResponse">Response message type for this call.</typeparam>
public sealed class AsyncDuplexStreamingCall<TRequest, TResponse> : IDisposable
{
readonly IClientStreamWriter<TRequest> requestStream;
readonly IAsyncStreamReader<TResponse> responseStream;
readonly AsyncCallState callState;
/// <summary>
/// Creates a new AsyncDuplexStreamingCall object with the specified properties.
/// </summary>
/// <param name="requestStream">Stream of request values.</param>
/// <param name="responseStream">Stream of response values.</param>
/// <param name="responseHeadersAsync">Response headers of the asynchronous call.</param>
/// <param name="getStatusFunc">Delegate returning the status of the call.</param>
/// <param name="getTrailersFunc">Delegate returning the trailing metadata of the call.</param>
/// <param name="disposeAction">Delegate to invoke when Dispose is called on the call object.</param>
public AsyncDuplexStreamingCall(IClientStreamWriter<TRequest> requestStream,
IAsyncStreamReader<TResponse> responseStream,
Task<Metadata> responseHeadersAsync,
Func<Status> getStatusFunc,
Func<Metadata> getTrailersFunc,
Action disposeAction)
{
this.requestStream = requestStream;
this.responseStream = responseStream;
this.callState = new AsyncCallState(responseHeadersAsync, getStatusFunc, getTrailersFunc, disposeAction);
}
/// <summary>
/// Creates a new AsyncDuplexStreamingCall object with the specified properties.
/// </summary>
/// <param name="requestStream">Stream of request values.</param>
/// <param name="responseStream">Stream of response values.</param>
/// <param name="responseHeadersAsync">Response headers of the asynchronous call.</param>
/// <param name="getStatusFunc">Delegate returning the status of the call.</param>
/// <param name="getTrailersFunc">Delegate returning the trailing metadata of the call.</param>
/// <param name="disposeAction">Delegate to invoke when Dispose is called on the call object.</param>
/// <param name="state">State object for use with the callback parameters.</param>
public AsyncDuplexStreamingCall(IClientStreamWriter<TRequest> requestStream,
IAsyncStreamReader<TResponse> responseStream,
Func<object, Task<Metadata>> responseHeadersAsync,
Func<object, Status> getStatusFunc,
Func<object, Metadata> getTrailersFunc,
Action<object> disposeAction,
object state)
{
this.requestStream = requestStream;
this.responseStream = responseStream;
this.callState = new AsyncCallState(responseHeadersAsync, getStatusFunc, getTrailersFunc, disposeAction, state);
}
/// <summary>
/// Async stream to read streaming responses.
/// </summary>
public IAsyncStreamReader<TResponse> ResponseStream
{
get
{
return responseStream;
}
}
/// <summary>
/// Async stream to send streaming requests.
/// </summary>
public IClientStreamWriter<TRequest> RequestStream
{
get
{
return requestStream;
}
}
/// <summary>
/// Asynchronous access to response headers.
/// </summary>
public Task<Metadata> ResponseHeadersAsync
{
get
{
return callState.ResponseHeadersAsync();
}
}
/// <summary>
/// Gets the call status if the call has already finished.
/// Throws InvalidOperationException otherwise.
/// </summary>
public Status GetStatus()
{
return callState.GetStatus();
}
/// <summary>
/// Gets the call trailing metadata if the call has already finished.
/// Throws InvalidOperationException otherwise.
/// </summary>
public Metadata GetTrailers()
{
return callState.GetTrailers();
}
/// <summary>
/// Provides means to cleanup after the call.
/// If the call has already finished normally (request stream has been completed and response stream has been fully read), doesn't do anything.
/// Otherwise, requests cancellation of the call which should terminate all pending async operations associated with the call.
/// As a result, all resources being used by the call should be released eventually.
/// </summary>
/// <remarks>
/// Normally, there is no need for you to dispose the call unless you want to utilize the
/// "Cancel" semantics of invoking <c>Dispose</c>.
/// </remarks>
public void Dispose()
{
callState.Dispose();
}
}
}

@ -1,126 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Threading.Tasks;
namespace Grpc.Core
{
/// <summary>
/// Return type for server streaming calls.
/// </summary>
/// <typeparam name="TResponse">Response message type for this call.</typeparam>
public sealed class AsyncServerStreamingCall<TResponse> : IDisposable
{
readonly IAsyncStreamReader<TResponse> responseStream;
readonly AsyncCallState callState;
/// <summary>
/// Creates a new AsyncDuplexStreamingCall object with the specified properties.
/// </summary>
/// <param name="responseStream">Stream of response values.</param>
/// <param name="responseHeadersAsync">Response headers of the asynchronous call.</param>
/// <param name="getStatusFunc">Delegate returning the status of the call.</param>
/// <param name="getTrailersFunc">Delegate returning the trailing metadata of the call.</param>
/// <param name="disposeAction">Delegate to invoke when Dispose is called on the call object.</param>
public AsyncServerStreamingCall(IAsyncStreamReader<TResponse> responseStream,
Task<Metadata> responseHeadersAsync,
Func<Status> getStatusFunc,
Func<Metadata> getTrailersFunc,
Action disposeAction)
{
this.responseStream = responseStream;
this.callState = new AsyncCallState(responseHeadersAsync, getStatusFunc, getTrailersFunc, disposeAction);
}
/// <summary>
/// Creates a new AsyncDuplexStreamingCall object with the specified properties.
/// </summary>
/// <param name="responseStream">Stream of response values.</param>
/// <param name="responseHeadersAsync">Response headers of the asynchronous call.</param>
/// <param name="getStatusFunc">Delegate returning the status of the call.</param>
/// <param name="getTrailersFunc">Delegate returning the trailing metadata of the call.</param>
/// <param name="disposeAction">Delegate to invoke when Dispose is called on the call object.</param>
/// <param name="state">State object for use with the callback parameters.</param>
public AsyncServerStreamingCall(IAsyncStreamReader<TResponse> responseStream,
Func<object, Task<Metadata>> responseHeadersAsync,
Func<object, Status> getStatusFunc,
Func<object, Metadata> getTrailersFunc,
Action<object> disposeAction,
object state)
{
this.responseStream = responseStream;
this.callState = new AsyncCallState(responseHeadersAsync, getStatusFunc, getTrailersFunc, disposeAction, state);
}
/// <summary>
/// Async stream to read streaming responses.
/// </summary>
public IAsyncStreamReader<TResponse> ResponseStream
{
get
{
return responseStream;
}
}
/// <summary>
/// Asynchronous access to response headers.
/// </summary>
public Task<Metadata> ResponseHeadersAsync
{
get
{
return callState.ResponseHeadersAsync();
}
}
/// <summary>
/// Gets the call status if the call has already finished.
/// Throws InvalidOperationException otherwise.
/// </summary>
public Status GetStatus()
{
return callState.GetStatus();
}
/// <summary>
/// Gets the call trailing metadata if the call has already finished.
/// Throws InvalidOperationException otherwise.
/// </summary>
public Metadata GetTrailers()
{
return callState.GetTrailers();
}
/// <summary>
/// Provides means to cleanup after the call.
/// If the call has already finished normally (response stream has been fully read), doesn't do anything.
/// Otherwise, requests cancellation of the call which should terminate all pending async operations associated with the call.
/// As a result, all resources being used by the call should be released eventually.
/// </summary>
/// <remarks>
/// Normally, there is no need for you to dispose the call unless you want to utilize the
/// "Cancel" semantics of invoking <c>Dispose</c>.
/// </remarks>
public void Dispose()
{
callState.Dispose();
}
}
}

@ -1,50 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Grpc.Core
{
/// <summary>
/// Extension methods for <see cref="IAsyncStreamReader{T}"/>.
/// </summary>
public static class AsyncStreamReaderExtensions
{
/// <summary>
/// Advances the stream reader to the next element in the sequence, returning the result asynchronously.
/// </summary>
/// <typeparam name="T">The message type.</typeparam>
/// <param name="streamReader">The stream reader.</param>
/// <returns>
/// Task containing the result of the operation: true if the reader was successfully advanced
/// to the next element; false if the reader has passed the end of the sequence.
/// </returns>
public static Task<bool> MoveNext<T>(this IAsyncStreamReader<T> streamReader)
where T : class
{
if (streamReader == null)
{
throw new ArgumentNullException(nameof(streamReader));
}
return streamReader.MoveNext(CancellationToken.None);
}
}
}

@ -1,150 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace Grpc.Core
{
/// <summary>
/// Return type for single request - single response call.
/// </summary>
/// <typeparam name="TResponse">Response message type for this call.</typeparam>
public sealed class AsyncUnaryCall<TResponse> : IDisposable
{
readonly Task<TResponse> responseAsync;
readonly AsyncCallState callState;
/// <summary>
/// Creates a new AsyncUnaryCall object with the specified properties.
/// </summary>
/// <param name="responseAsync">The response of the asynchronous call.</param>
/// <param name="responseHeadersAsync">Response headers of the asynchronous call.</param>
/// <param name="getStatusFunc">Delegate returning the status of the call.</param>
/// <param name="getTrailersFunc">Delegate returning the trailing metadata of the call.</param>
/// <param name="disposeAction">Delegate to invoke when Dispose is called on the call object.</param>
public AsyncUnaryCall(Task<TResponse> responseAsync,
Task<Metadata> responseHeadersAsync,
Func<Status> getStatusFunc,
Func<Metadata> getTrailersFunc,
Action disposeAction)
{
this.responseAsync = responseAsync;
this.callState = new AsyncCallState(responseHeadersAsync, getStatusFunc, getTrailersFunc, disposeAction);
}
/// <summary>
/// Creates a new AsyncUnaryCall object with the specified properties.
/// </summary>
/// <param name="responseAsync">The response of the asynchronous call.</param>
/// <param name="responseHeadersAsync">Response headers of the asynchronous call.</param>
/// <param name="getStatusFunc">Delegate returning the status of the call.</param>
/// <param name="getTrailersFunc">Delegate returning the trailing metadata of the call.</param>
/// <param name="disposeAction">Delegate to invoke when Dispose is called on the call object.</param>
/// <param name="state">State object for use with the callback parameters.</param>
public AsyncUnaryCall(Task<TResponse> responseAsync,
Func<object, Task<Metadata>> responseHeadersAsync,
Func<object, Status> getStatusFunc,
Func<object, Metadata> getTrailersFunc,
Action<object> disposeAction,
object state)
{
this.responseAsync = responseAsync;
callState = new AsyncCallState(responseHeadersAsync, getStatusFunc, getTrailersFunc, disposeAction, state);
}
/// <summary>
/// Asynchronous call result.
/// </summary>
public Task<TResponse> ResponseAsync
{
get
{
return this.responseAsync;
}
}
/// <summary>
/// Asynchronous access to response headers.
/// </summary>
public Task<Metadata> ResponseHeadersAsync
{
get
{
return callState.ResponseHeadersAsync();
}
}
/// <summary>
/// Gets an awaiter used to await this <see cref="AsyncUnaryCall{TResponse}"/>.
/// </summary>
/// <returns>An awaiter instance.</returns>
/// <remarks>This method is intended for compiler use rather than use directly in code.</remarks>
public TaskAwaiter<TResponse> GetAwaiter()
{
return responseAsync.GetAwaiter();
}
/// <summary>
/// Configures an awaiter used to await this <see cref="AsyncUnaryCall{TResponse}"/>.
/// </summary>
/// <param name="continueOnCapturedContext">
/// true to attempt to marshal the continuation back to the original context captured; otherwise, false.
/// </param>
/// <returns>An object used to await this task.</returns>
public ConfiguredTaskAwaitable<TResponse> ConfigureAwait(bool continueOnCapturedContext)
{
return responseAsync.ConfigureAwait(continueOnCapturedContext);
}
/// <summary>
/// Gets the call status if the call has already finished.
/// Throws InvalidOperationException otherwise.
/// </summary>
public Status GetStatus()
{
return callState.GetStatus();
}
/// <summary>
/// Gets the call trailing metadata if the call has already finished.
/// Throws InvalidOperationException otherwise.
/// </summary>
public Metadata GetTrailers()
{
return callState.GetTrailers();
}
/// <summary>
/// Provides means to cleanup after the call.
/// If the call has already finished normally (request stream has been completed and call result has been received), doesn't do anything.
/// Otherwise, requests cancellation of the call which should terminate all pending async operations associated with the call.
/// As a result, all resources being used by the call should be released eventually.
/// </summary>
/// <remarks>
/// Normally, there is no need for you to dispose the call unless you want to utilize the
/// "Cancel" semantics of invoking <c>Dispose</c>.
/// </remarks>
public void Dispose()
{
callState.Dispose();
}
}
}

@ -1,112 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
/// Authentication context for a call.
/// AuthContext is the only reliable source of truth when it comes to authenticating calls.
/// Using any other call/context properties for authentication purposes is wrong and inherently unsafe.
/// Note: experimental API that can change or be removed without any prior notice.
/// </summary>
public class AuthContext
{
string? peerIdentityPropertyName;
Dictionary<string, List<AuthProperty>> properties;
/// <summary>
/// Initializes a new instance of the <see cref="T:Grpc.Core.AuthContext"/> class.
/// </summary>
/// <param name="peerIdentityPropertyName">Peer identity property name.</param>
/// <param name="properties">Multimap of auth properties by name.</param>
public AuthContext(string? peerIdentityPropertyName, Dictionary<string, List<AuthProperty>> properties)
{
this.peerIdentityPropertyName = peerIdentityPropertyName;
this.properties = GrpcPreconditions.CheckNotNull(properties);
}
/// <summary>
/// Returns <c>true</c> if the peer is authenticated.
/// </summary>
public bool IsPeerAuthenticated
{
get
{
return peerIdentityPropertyName != null;
}
}
/// <summary>
/// Gets the name of the property that indicates the peer identity. Returns <c>null</c>
/// if the peer is not authenticated.
/// </summary>
public string? PeerIdentityPropertyName
{
get
{
return peerIdentityPropertyName;
}
}
/// <summary>
/// Gets properties that represent the peer identity (there can be more than one). Returns an empty collection
/// if the peer is not authenticated.
/// </summary>
public IEnumerable<AuthProperty> PeerIdentity
{
get
{
if (peerIdentityPropertyName == null)
{
return Enumerable.Empty<AuthProperty>();
}
return properties[peerIdentityPropertyName];
}
}
/// <summary>
/// Gets the auth properties of this context.
/// </summary>
public IEnumerable<AuthProperty> Properties
{
get
{
return properties.Values.SelectMany(v => v);
}
}
/// <summary>
/// Returns the auth properties with given name (there can be more than one).
/// If no properties of given name exist, an empty collection will be returned.
/// </summary>
public IEnumerable<AuthProperty> FindPropertiesByName(string propertyName)
{
List<AuthProperty> result;
if (!properties.TryGetValue(propertyName, out result))
{
return Enumerable.Empty<AuthProperty>();
}
return result;
}
}
}

@ -1,109 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Text;
using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
/// A property of an <see cref="AuthContext"/>.
/// Note: experimental API that can change or be removed without any prior notice.
/// </summary>
public class AuthProperty
{
static readonly Encoding EncodingUTF8 = System.Text.Encoding.UTF8;
string name;
byte[] valueBytes;
string? lazyValue;
private AuthProperty(string name, byte[] valueBytes)
{
this.name = GrpcPreconditions.CheckNotNull(name);
this.valueBytes = GrpcPreconditions.CheckNotNull(valueBytes);
}
/// <summary>
/// Gets the name of the property.
/// </summary>
public string Name
{
get
{
return name;
}
}
/// <summary>
/// Gets the string value of the property.
/// </summary>
public string Value
{
get
{
return lazyValue ?? (lazyValue = EncodingUTF8.GetString(this.valueBytes));
}
}
/// <summary>
/// Gets the binary value of the property.
/// </summary>
public byte[] ValueBytes
{
get
{
var valueCopy = new byte[valueBytes.Length];
Buffer.BlockCopy(valueBytes, 0, valueCopy, 0, valueBytes.Length);
return valueCopy;
}
}
/// <summary>
/// Creates an instance of <c>AuthProperty</c>.
/// </summary>
/// <param name="name">the name</param>
/// <param name="valueBytes">the binary value of the property</param>
public static AuthProperty Create(string name, byte[] valueBytes)
{
GrpcPreconditions.CheckNotNull(valueBytes);
var valueCopy = new byte[valueBytes.Length];
Buffer.BlockCopy(valueBytes, 0, valueCopy, 0, valueBytes.Length);
return new AuthProperty(name, valueCopy);
}
/// <summary>
/// Gets the binary value of the property (without making a defensive copy).
/// </summary>
internal byte[] ValueBytesUnsafe
{
get
{
return valueBytes;
}
}
/// <summary>
/// Creates and instance of <c>AuthProperty</c> without making a defensive copy of <c>valueBytes</c>.
/// </summary>
internal static AuthProperty CreateUnsafe(string name, byte[] valueBytes)
{
return new AuthProperty(name, valueBytes);
}
}
}

@ -1,59 +0,0 @@
#region Copyright notice and license
// Copyright 2019 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Diagnostics.CodeAnalysis;
namespace Grpc.Core
{
/// <summary>
/// Specifies the location of the service bind method for a gRPC service.
/// The bind method is typically generated code and is used to register a service's
/// methods with the server on startup.
///
/// The bind method signature takes a <see cref="ServiceBinderBase"/> and an optional
/// instance of the service base class, e.g. <c>static void BindService(ServiceBinderBase, GreeterService)</c>.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class BindServiceMethodAttribute : Attribute
{
// grpc-dotnet uses reflection to find the bind service method.
// DynamicallyAccessedMembersAttribute instructs the linker to never trim the method.
private const DynamicallyAccessedMemberTypes ServiceBinderAccessibility = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods;
/// <summary>
/// Initializes a new instance of the <see cref="BindServiceMethodAttribute"/> class.
/// </summary>
/// <param name="bindType">The type the service bind method is defined on.</param>
/// <param name="bindMethodName">The name of the service bind method.</param>
public BindServiceMethodAttribute([DynamicallyAccessedMembers(ServiceBinderAccessibility)] Type bindType, string bindMethodName)
{
BindType = bindType;
BindMethodName = bindMethodName;
}
/// <summary>
/// Gets the type the service bind method is defined on.
/// </summary>
[DynamicallyAccessedMembers(ServiceBinderAccessibility)]
public Type BindType { get; }
/// <summary>
/// Gets the name of the service bind method.
/// </summary>
public string BindMethodName { get; }
}
}

@ -1,90 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
/// Client-side call credentials. Provide authorization with per-call granularity.
/// </summary>
public abstract class CallCredentials
{
/// <summary>
/// Composes multiple <c>CallCredentials</c> objects into
/// a single <c>CallCredentials</c> object.
/// </summary>
/// <param name="credentials">credentials to compose</param>
/// <returns>The new <c>CompositeCallCredentials</c></returns>
public static CallCredentials Compose(params CallCredentials[] credentials)
{
return new CompositeCallCredentials(credentials);
}
/// <summary>
/// Creates a new instance of <c>CallCredentials</c> class from an
/// interceptor that can attach metadata to outgoing calls.
/// </summary>
/// <param name="interceptor">authentication interceptor</param>
public static CallCredentials FromInterceptor(AsyncAuthInterceptor interceptor)
{
return new AsyncAuthInterceptorCredentials(interceptor);
}
/// <summary>
/// Populates call credentials configurator with this instance's configuration.
/// End users never need to invoke this method as it is part of internal implementation.
/// </summary>
public abstract void InternalPopulateConfiguration(CallCredentialsConfiguratorBase configurator, object? state);
private class CompositeCallCredentials : CallCredentials
{
readonly IReadOnlyList<CallCredentials> credentials;
public CompositeCallCredentials(CallCredentials[] credentials)
{
GrpcPreconditions.CheckArgument(credentials.Length >= 2, "Composite credentials object can only be created from 2 or more credentials.");
this.credentials = new List<CallCredentials>(credentials).AsReadOnly();
}
public override void InternalPopulateConfiguration(CallCredentialsConfiguratorBase configurator, object? state)
{
configurator.SetCompositeCredentials(state, credentials);
}
}
private class AsyncAuthInterceptorCredentials : CallCredentials
{
readonly AsyncAuthInterceptor interceptor;
public AsyncAuthInterceptorCredentials(AsyncAuthInterceptor interceptor)
{
this.interceptor = GrpcPreconditions.CheckNotNull(interceptor);
}
public override void InternalPopulateConfiguration(CallCredentialsConfiguratorBase configurator, object? state)
{
configurator.SetAsyncAuthInterceptorCredentials(state, interceptor);
}
}
}
}

@ -1,39 +0,0 @@
#region Copyright notice and license
// Copyright 2019 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System.Collections.Generic;
namespace Grpc.Core
{
/// <summary>
/// Base class for objects that can consume configuration from <c>CallCredentials</c> objects.
/// Note: experimental API that can change or be removed without any prior notice.
/// </summary>
public abstract class CallCredentialsConfiguratorBase
{
/// <summary>
/// Consumes configuration for composite call credentials.
/// </summary>
public abstract void SetCompositeCredentials(object? state, IReadOnlyList<CallCredentials> credentials);
/// <summary>
/// Consumes configuration for call credentials created from <c>AsyncAuthInterceptor</c>
/// </summary>
public abstract void SetAsyncAuthInterceptorCredentials(object? state, AsyncAuthInterceptor interceptor);
}
}

@ -1,45 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
namespace Grpc.Core.Internal
{
/// <summary>
/// Flags to enable special call behaviors (client-side only).
/// </summary>
[Flags]
internal enum CallFlags
{
/// <summary>
/// The call is idempotent (retrying the call doesn't change the outcome of the operation).
/// </summary>
IdempotentRequest = 0x10,
/// <summary>
/// If channel is in <c>ChannelState.TransientFailure</c>, attempt waiting for the channel to recover
/// instead of failing the call immediately.
/// </summary>
WaitForReady = 0x20,
/// <summary>
/// The call is cacheable. gRPC is free to use GET verb */
/// </summary>
CacheableRequest = 0x40
}
}

@ -1,67 +0,0 @@
#region Copyright notice and license
// Copyright 2015-2016 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System.Threading.Tasks;
namespace Grpc.Core
{
/// <summary>
/// Abstraction of client-side RPC invocation.
/// </summary>
public abstract class CallInvoker
{
/// <summary>
/// Invokes a simple remote call in a blocking fashion.
/// </summary>
public abstract TResponse BlockingUnaryCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string? host, CallOptions options, TRequest request)
where TRequest : class
where TResponse : class;
/// <summary>
/// Invokes a simple remote call asynchronously.
/// </summary>
public abstract AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string? host, CallOptions options, TRequest request)
where TRequest : class
where TResponse : class;
/// <summary>
/// Invokes a server streaming call asynchronously.
/// In server streaming scenario, client sends on request and server responds with a stream of responses.
/// </summary>
public abstract AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string? host, CallOptions options, TRequest request)
where TRequest : class
where TResponse : class;
/// <summary>
/// Invokes a client streaming call asynchronously.
/// In client streaming scenario, client sends a stream of requests and server responds with a single response.
/// </summary>
public abstract AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string? host, CallOptions options)
where TRequest : class
where TResponse : class;
/// <summary>
/// Invokes a duplex streaming call asynchronously.
/// In duplex streaming scenario, client sends a stream of requests and server responds with a stream of responses.
/// The response stream is completely independent and both side can be sending messages at the same time.
/// </summary>
public abstract AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string? host, CallOptions options)
where TRequest : class
where TResponse : class;
}
}

@ -1,230 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Threading;
using Grpc.Core.Internal;
namespace Grpc.Core
{
/// <summary>
/// Options for calls made by client.
/// </summary>
public struct CallOptions
{
Metadata? headers;
DateTime? deadline;
CancellationToken cancellationToken;
WriteOptions? writeOptions;
ContextPropagationToken? propagationToken;
CallCredentials? credentials;
CallFlags flags;
/// <summary>
/// Creates a new instance of <c>CallOptions</c> struct.
/// </summary>
/// <param name="headers">Headers to be sent with the call.</param>
/// <param name="deadline">Deadline for the call to finish. null means no deadline.</param>
/// <param name="cancellationToken">Can be used to request cancellation of the call.</param>
/// <param name="writeOptions">Write options that will be used for this call.</param>
/// <param name="propagationToken">Context propagation token obtained from <see cref="ServerCallContext"/>.</param>
/// <param name="credentials">Credentials to use for this call.</param>
public CallOptions(Metadata? headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken),
WriteOptions? writeOptions = null, ContextPropagationToken? propagationToken = null, CallCredentials? credentials = null)
{
this.headers = headers;
this.deadline = deadline;
this.cancellationToken = cancellationToken;
this.writeOptions = writeOptions;
this.propagationToken = propagationToken;
this.credentials = credentials;
this.flags = default(CallFlags);
}
/// <summary>
/// Headers to send at the beginning of the call.
/// </summary>
public Metadata? Headers
{
get { return headers; }
}
/// <summary>
/// Call deadline.
/// </summary>
public DateTime? Deadline
{
get { return deadline; }
}
/// <summary>
/// Token that can be used for cancelling the call on the client side.
/// Cancelling the token will request cancellation
/// of the remote call. Best effort will be made to deliver the cancellation
/// notification to the server and interaction of the call with the server side
/// will be terminated. Unless the call finishes before the cancellation could
/// happen (there is an inherent race),
/// the call will finish with <c>StatusCode.Cancelled</c> status.
/// </summary>
public CancellationToken CancellationToken
{
get { return cancellationToken; }
}
/// <summary>
/// Write options that will be used for this call.
/// </summary>
public WriteOptions? WriteOptions
{
get { return this.writeOptions; }
}
/// <summary>
/// Token for propagating parent call context.
/// </summary>
public ContextPropagationToken? PropagationToken
{
get { return this.propagationToken; }
}
/// <summary>
/// Credentials to use for this call.
/// </summary>
public CallCredentials? Credentials
{
get { return this.credentials; }
}
/// <summary>
/// If <c>true</c> and channel is in <c>ChannelState.TransientFailure</c>, the call will attempt waiting for the channel to recover
/// instead of failing immediately (which is the default "FailFast" semantics).
/// Note: experimental API that can change or be removed without any prior notice.
/// </summary>
public bool IsWaitForReady
{
get { return (this.flags & CallFlags.WaitForReady) == CallFlags.WaitForReady; }
}
/// <summary>
/// Flags to use for this call.
/// </summary>
internal CallFlags Flags
{
get { return this.flags; }
}
/// <summary>
/// Returns new instance of <see cref="CallOptions"/> with
/// <c>Headers</c> set to the value provided. Values of all other fields are preserved.
/// </summary>
/// <param name="headers">The headers.</param>
public CallOptions WithHeaders(Metadata headers)
{
var newOptions = this;
newOptions.headers = headers;
return newOptions;
}
/// <summary>
/// Returns new instance of <see cref="CallOptions"/> with
/// <c>Deadline</c> set to the value provided. Values of all other fields are preserved.
/// </summary>
/// <param name="deadline">The deadline.</param>
public CallOptions WithDeadline(DateTime deadline)
{
var newOptions = this;
newOptions.deadline = deadline;
return newOptions;
}
/// <summary>
/// Returns new instance of <see cref="CallOptions"/> with
/// <c>CancellationToken</c> set to the value provided. Values of all other fields are preserved.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
public CallOptions WithCancellationToken(CancellationToken cancellationToken)
{
var newOptions = this;
newOptions.cancellationToken = cancellationToken;
return newOptions;
}
/// <summary>
/// Returns new instance of <see cref="CallOptions"/> with
/// <c>WriteOptions</c> set to the value provided. Values of all other fields are preserved.
/// </summary>
/// <param name="writeOptions">The write options.</param>
public CallOptions WithWriteOptions(WriteOptions writeOptions)
{
var newOptions = this;
newOptions.writeOptions = writeOptions;
return newOptions;
}
/// <summary>
/// Returns new instance of <see cref="CallOptions"/> with
/// <c>PropagationToken</c> set to the value provided. Values of all other fields are preserved.
/// </summary>
/// <param name="propagationToken">The context propagation token.</param>
public CallOptions WithPropagationToken(ContextPropagationToken propagationToken)
{
var newOptions = this;
newOptions.propagationToken = propagationToken;
return newOptions;
}
/// <summary>
/// Returns new instance of <see cref="CallOptions"/> with
/// <c>Credentials</c> set to the value provided. Values of all other fields are preserved.
/// </summary>
/// <param name="credentials">The call credentials.</param>
public CallOptions WithCredentials(CallCredentials credentials)
{
var newOptions = this;
newOptions.credentials = credentials;
return newOptions;
}
/// <summary>
/// Returns new instance of <see cref="CallOptions"/> with "WaitForReady" semantics enabled/disabled.
/// <see cref="IsWaitForReady"/>.
/// Note: experimental API that can change or be removed without any prior notice.
/// </summary>
public CallOptions WithWaitForReady(bool waitForReady = true)
{
if (waitForReady)
{
return WithFlags(this.flags | CallFlags.WaitForReady);
}
return WithFlags(this.flags & ~CallFlags.WaitForReady);
}
/// <summary>
/// Returns new instance of <see cref="CallOptions"/> with
/// <c>Flags</c> set to the value provided. Values of all other fields are preserved.
/// </summary>
/// <param name="flags">The call flags.</param>
internal CallOptions WithFlags(CallFlags flags)
{
var newOptions = this;
newOptions.flags = flags;
return newOptions;
}
}
}

@ -1,82 +0,0 @@
#region Copyright notice and license
// Copyright 2019 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Threading.Tasks;
using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
/// Base class for gRPC channel. Channels are an abstraction of long-lived connections to remote servers.
/// </summary>
public abstract class ChannelBase
{
private readonly string target;
/// <summary>
/// Initializes a new instance of <see cref="ChannelBase"/> class that connects to a specific host.
/// </summary>
/// <param name="target">Target of the channel.</param>
protected ChannelBase(string target)
{
this.target = GrpcPreconditions.CheckNotNull(target, nameof(target));
}
/// <summary>The original target used to create the channel.</summary>
public string Target
{
get { return this.target; }
}
/// <summary>
/// Create a new <see cref="CallInvoker"/> for the channel.
/// </summary>
/// <returns>A new <see cref="CallInvoker"/>.</returns>
public abstract CallInvoker CreateCallInvoker();
/// <summary>
/// Shuts down the channel cleanly. It is strongly recommended to shutdown
/// the channel once you stopped using it.
/// </summary>
/// <remarks>
/// Guidance for implementors:
/// This method doesn't wait for all calls on this channel to finish (nor does
/// it have to explicitly cancel all outstanding calls). It is user's responsibility to make sure
/// all the calls on this channel have finished (successfully or with an error)
/// before shutting down the channel to ensure channel shutdown won't impact
/// the outcome of those remote calls.
/// </remarks>
public Task ShutdownAsync()
{
return ShutdownAsyncCore();
}
/// <summary>Provides implementation of a non-virtual public member.</summary>
#pragma warning disable 1998
protected virtual async Task ShutdownAsyncCore()
{
// default implementation is no-op for backwards compatibility, but all implementations
// are expected to override this method.
// warning 1998 is disabled to avoid needing TaskUtils.CompletedTask, which is
// only available in Grpc.Core
}
#pragma warning restore 1998
}
}

@ -1,134 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
/// Client-side channel credentials. Used for creation of a secure channel.
/// </summary>
public abstract class ChannelCredentials
{
static readonly ChannelCredentials InsecureInstance = new InsecureCredentials();
static readonly ChannelCredentials SecureSslInstance = new SslCredentials();
/// <summary>
/// Creates a new instance of channel credentials
/// </summary>
public ChannelCredentials()
{
}
/// <summary>
/// Returns instance of credentials that provides no security and
/// will result in creating an unsecure channel with no encryption whatsoever.
/// </summary>
public static ChannelCredentials Insecure
{
get
{
return InsecureInstance;
}
}
/// <summary>
/// Returns instance of credentials that provides SSL security.
/// <para>
/// These credentials are the same as creating <see cref="SslCredentials"/> without parameters.
/// Apps that are using Grpc.Core can create <see cref="SslCredentials"/> directly to customize
/// the secure SSL credentials.
/// </para>
/// </summary>
public static ChannelCredentials SecureSsl
{
get
{
return SecureSslInstance;
}
}
/// <summary>
/// Creates a new instance of <c>ChannelCredentials</c> class by composing
/// given channel credentials with call credentials.
/// </summary>
/// <param name="channelCredentials">Channel credentials.</param>
/// <param name="callCredentials">Call credentials.</param>
/// <returns>The new composite <c>ChannelCredentials</c></returns>
public static ChannelCredentials Create(ChannelCredentials channelCredentials, CallCredentials callCredentials)
{
return new CompositeChannelCredentials(channelCredentials, callCredentials);
}
/// <summary>
/// Populates channel credentials configurator with this instance's configuration.
/// End users never need to invoke this method as it is part of internal implementation.
/// </summary>
public abstract void InternalPopulateConfiguration(ChannelCredentialsConfiguratorBase configurator, object state);
/// <summary>
/// Returns <c>true</c> if this credential type allows being composed by <c>CompositeCredentials</c>.
/// </summary>
internal virtual bool IsComposable => false;
private sealed class InsecureCredentials : ChannelCredentials
{
public override void InternalPopulateConfiguration(ChannelCredentialsConfiguratorBase configurator, object state)
{
configurator.SetInsecureCredentials(state);
}
}
/// <summary>
/// Credentials that allow composing one <see cref="ChannelCredentials"/> object and
/// one or more <see cref="CallCredentials"/> objects into a single <see cref="ChannelCredentials"/>.
/// </summary>
private sealed class CompositeChannelCredentials : ChannelCredentials
{
readonly ChannelCredentials channelCredentials;
readonly CallCredentials callCredentials;
/// <summary>
/// Initializes a new instance of <c>CompositeChannelCredentials</c> class.
/// The resulting credentials object will be composite of all the credentials specified as parameters.
/// </summary>
/// <param name="channelCredentials">channelCredentials to compose</param>
/// <param name="callCredentials">channelCredentials to compose</param>
public CompositeChannelCredentials(ChannelCredentials channelCredentials, CallCredentials callCredentials)
{
this.channelCredentials = GrpcPreconditions.CheckNotNull(channelCredentials);
this.callCredentials = GrpcPreconditions.CheckNotNull(callCredentials);
if (!channelCredentials.IsComposable)
{
throw new ArgumentException(string.Format("CallCredentials can't be composed with {0}. CallCredentials must be used with secure channel credentials like SslCredentials.", channelCredentials.GetType().Name));
}
}
public override void InternalPopulateConfiguration(ChannelCredentialsConfiguratorBase configurator, object state)
{
configurator.SetCompositeCredentials(state, channelCredentials, callCredentials);
}
}
}
}

@ -1,44 +0,0 @@
#region Copyright notice and license
// Copyright 2019 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System.Collections.Generic;
namespace Grpc.Core
{
/// <summary>
/// Base class for objects that can consume configuration from <c>CallCredentials</c> objects.
/// Note: experimental API that can change or be removed without any prior notice.
/// </summary>
public abstract class ChannelCredentialsConfiguratorBase
{
/// <summary>
/// Configures the credentials to use insecure credentials.
/// </summary>
public abstract void SetInsecureCredentials(object state);
/// <summary>
/// Configures the credentials to use <c>SslCredentials</c>.
/// </summary>
public abstract void SetSslCredentials(object state, string? rootCertificates, KeyCertificatePair? keyCertificatePair, VerifyPeerCallback? verifyPeerCallback);
/// <summary>
/// Configures the credentials to use composite channel credentials (a composite of channel credentials and call credentials).
/// </summary>
public abstract void SetCompositeCredentials(object state, ChannelCredentials channelCredentials, CallCredentials callCredentials);
}
}

@ -1,231 +0,0 @@
#region Copyright notice and license
// Copyright 2015-2016 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using Grpc.Core.Interceptors;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
/// Generic base class for client-side stubs.
/// </summary>
public abstract class ClientBase<T> : ClientBase
where T : ClientBase<T>
{
/// <summary>
/// Initializes a new instance of <c>ClientBase</c> class that
/// throws <c>NotImplementedException</c> upon invocation of any RPC.
/// This constructor is only provided to allow creation of test doubles
/// for client classes (e.g. mocking requires a parameterless constructor).
/// </summary>
protected ClientBase() : base()
{
}
/// <summary>
/// Initializes a new instance of <c>ClientBase</c> class.
/// </summary>
/// <param name="configuration">The configuration.</param>
protected ClientBase(ClientBaseConfiguration configuration) : base(configuration)
{
}
/// <summary>
/// Initializes a new instance of <c>ClientBase</c> class.
/// </summary>
/// <param name="channel">The channel to use for remote call invocation.</param>
public ClientBase(ChannelBase channel) : base(channel)
{
}
/// <summary>
/// Initializes a new instance of <c>ClientBase</c> class.
/// </summary>
/// <param name="callInvoker">The <c>CallInvoker</c> for remote call invocation.</param>
public ClientBase(CallInvoker callInvoker) : base(callInvoker)
{
}
/// <summary>
/// Creates a new client that sets host field for calls explicitly.
/// gRPC supports multiple "hosts" being served by a single server.
/// By default (if a client was not created by calling this method),
/// host <c>null</c> with the meaning "use default host" is used.
/// </summary>
public T WithHost(string host)
{
var newConfiguration = this.Configuration.WithHost(host);
return NewInstance(newConfiguration);
}
/// <summary>
/// Creates a new instance of client from given <c>ClientBaseConfiguration</c>.
/// </summary>
protected abstract T NewInstance(ClientBaseConfiguration configuration);
}
/// <summary>
/// Base class for client-side stubs.
/// </summary>
public abstract class ClientBase
{
readonly ClientBaseConfiguration configuration;
readonly CallInvoker callInvoker;
/// <summary>
/// Initializes a new instance of <c>ClientBase</c> class that
/// throws <c>NotImplementedException</c> upon invocation of any RPC.
/// This constructor is only provided to allow creation of test doubles
/// for client classes (e.g. mocking requires a parameterless constructor).
/// </summary>
protected ClientBase() : this(new UnimplementedCallInvoker())
{
}
/// <summary>
/// Initializes a new instance of <c>ClientBase</c> class.
/// </summary>
/// <param name="configuration">The configuration.</param>
protected ClientBase(ClientBaseConfiguration configuration)
{
this.configuration = GrpcPreconditions.CheckNotNull(configuration, "configuration");
this.callInvoker = configuration.CreateDecoratedCallInvoker();
}
/// <summary>
/// Initializes a new instance of <c>ClientBase</c> class.
/// </summary>
/// <param name="channel">The channel to use for remote call invocation.</param>
public ClientBase(ChannelBase channel) : this(channel.CreateCallInvoker())
{
}
/// <summary>
/// Initializes a new instance of <c>ClientBase</c> class.
/// </summary>
/// <param name="callInvoker">The <c>CallInvoker</c> for remote call invocation.</param>
public ClientBase(CallInvoker callInvoker) : this(new ClientBaseConfiguration(callInvoker, null))
{
}
/// <summary>
/// Gets the call invoker.
/// </summary>
protected CallInvoker CallInvoker
{
get { return this.callInvoker; }
}
/// <summary>
/// Gets the configuration.
/// </summary>
internal ClientBaseConfiguration Configuration
{
get { return this.configuration; }
}
/// <summary>
/// Represents configuration of ClientBase. The class itself is visible to
/// subclasses, but contents are marked as internal to make the instances opaque.
/// The verbose name of this class was chosen to make name clash in generated code
/// less likely.
/// </summary>
protected internal class ClientBaseConfiguration
{
private class ClientBaseConfigurationInterceptor : Interceptor
{
readonly Func<IMethod, string?, CallOptions, ClientBaseConfigurationInfo> interceptor;
/// <summary>
/// Creates a new instance of ClientBaseConfigurationInterceptor given the specified header and host interceptor function.
/// </summary>
public ClientBaseConfigurationInterceptor(Func<IMethod, string?, CallOptions, ClientBaseConfigurationInfo> interceptor)
{
this.interceptor = GrpcPreconditions.CheckNotNull(interceptor, nameof(interceptor));
}
private ClientInterceptorContext<TRequest, TResponse> GetNewContext<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context)
where TRequest : class
where TResponse : class
{
var newHostAndCallOptions = interceptor(context.Method, context.Host, context.Options);
return new ClientInterceptorContext<TRequest, TResponse>(context.Method, newHostAndCallOptions.Host, newHostAndCallOptions.CallOptions);
}
public override TResponse BlockingUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, BlockingUnaryCallContinuation<TRequest, TResponse> continuation)
{
return continuation(request, GetNewContext(context));
}
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
{
return continuation(request, GetNewContext(context));
}
public override AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncServerStreamingCallContinuation<TRequest, TResponse> continuation)
{
return continuation(request, GetNewContext(context));
}
public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)
{
return continuation(GetNewContext(context));
}
public override AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncDuplexStreamingCallContinuation<TRequest, TResponse> continuation)
{
return continuation(GetNewContext(context));
}
}
internal struct ClientBaseConfigurationInfo
{
internal readonly string? Host;
internal readonly CallOptions CallOptions;
internal ClientBaseConfigurationInfo(string? host, CallOptions callOptions)
{
Host = host;
CallOptions = callOptions;
}
}
readonly CallInvoker undecoratedCallInvoker;
readonly string? host;
internal ClientBaseConfiguration(CallInvoker undecoratedCallInvoker, string? host)
{
this.undecoratedCallInvoker = GrpcPreconditions.CheckNotNull(undecoratedCallInvoker);
this.host = host;
}
internal CallInvoker CreateDecoratedCallInvoker()
{
return undecoratedCallInvoker.Intercept(new ClientBaseConfigurationInterceptor((method, host, options) => new ClientBaseConfigurationInfo(this.host, options)));
}
internal ClientBaseConfiguration WithHost(string host)
{
GrpcPreconditions.CheckNotNull(host, nameof(host));
return new ClientBaseConfiguration(this.undecoratedCallInvoker, host);
}
}
}
}

@ -1,59 +0,0 @@
#region Copyright notice and license
// Copyright 2019 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
namespace Grpc.Core
{
/// <summary>
/// Options for <see cref="ContextPropagationToken"/>.
/// </summary>
public class ContextPropagationOptions
{
/// <summary>
/// The context propagation options that will be used by default.
/// </summary>
public static readonly ContextPropagationOptions Default = new ContextPropagationOptions();
bool propagateDeadline;
bool propagateCancellation;
/// <summary>
/// Creates new context propagation options.
/// </summary>
/// <param name="propagateDeadline">If set to <c>true</c> parent call's deadline will be propagated to the child call.</param>
/// <param name="propagateCancellation">If set to <c>true</c> parent call's cancellation token will be propagated to the child call.</param>
public ContextPropagationOptions(bool propagateDeadline = true, bool propagateCancellation = true)
{
this.propagateDeadline = propagateDeadline;
this.propagateCancellation = propagateCancellation;
}
/// <summary><c>true</c> if parent call's deadline should be propagated to the child call.</summary>
public bool IsPropagateDeadline
{
get { return this.propagateDeadline; }
}
/// <summary><c>true</c> if parent call's cancellation token should be propagated to the child call.</summary>
public bool IsPropagateCancellation
{
get { return this.propagateCancellation; }
}
}
}

@ -1,35 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
namespace Grpc.Core
{
/// <summary>
/// Token for propagating context of server side handlers to child calls.
/// In situations when a backend is making calls to another backend,
/// it makes sense to propagate properties like deadline and cancellation
/// token of the server call to the child call.
/// Underlying gRPC implementation may provide other "opaque" contexts (like tracing context) that
/// are not explicitly accesible via the public C# API, but this token still allows propagating them.
/// </summary>
public abstract class ContextPropagationToken
{
internal ContextPropagationToken()
{
}
}
}

@ -1,66 +0,0 @@
#region Copyright notice and license
// Copyright 2018 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
namespace Grpc.Core
{
/// <summary>
/// Provides access to the payload being deserialized when deserializing messages.
/// </summary>
public abstract class DeserializationContext
{
/// <summary>
/// Get the total length of the payload in bytes.
/// </summary>
public abstract int PayloadLength { get; }
/// <summary>
/// Gets the entire payload as a newly allocated byte array.
/// Once the byte array is returned, the byte array becomes owned by the caller and won't be ever accessed or reused by gRPC again.
/// NOTE: Obtaining the buffer as a newly allocated byte array is the simplest way of accessing the payload,
/// but it can have important consequences in high-performance scenarios.
/// In particular, using this method usually requires copying of the entire buffer one extra time.
/// Also, allocating a new buffer each time can put excessive pressure on GC, especially if
/// the payload is more than 86700 bytes large (which means the newly allocated buffer will be placed in LOH,
/// and LOH object can only be garbage collected via a full ("stop the world") GC run).
/// NOTE: Deserializers are expected not to call this method (or other payload accessor methods) more than once per received message
/// (as there is no practical reason for doing so) and <c>DeserializationContext</c> implementations are free to assume so.
/// </summary>
/// <returns>byte array containing the entire payload.</returns>
public virtual byte[] PayloadAsNewBuffer()
{
throw new NotImplementedException();
}
/// <summary>
/// Gets the entire payload as a ReadOnlySequence.
/// The ReadOnlySequence is only valid for the duration of the deserializer routine and the caller must not access it after the deserializer returns.
/// Using the read only sequence is the most efficient way to access the message payload. Where possible it allows directly
/// accessing the received payload without needing to perform any buffer copying or buffer allocations.
/// NOTE: When using this method, it is recommended to use C# 7.2 compiler to make it more useful (using Span type directly from your code requires C# 7.2)."
/// NOTE: Deserializers are expected not to call this method (or other payload accessor methods) more than once per received message
/// (as there is no practical reason for doing so) and <c>DeserializationContext</c> implementations are free to assume so.
/// </summary>
/// <returns>read only sequence containing the entire payload.</returns>
public virtual System.Buffers.ReadOnlySequence<byte> PayloadAsReadOnlySequence()
{
throw new NotImplementedException();
}
}
}

@ -1,36 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Authors>The gRPC Authors</Authors>
<Copyright>Copyright 2019 The gRPC Authors</Copyright>
<Description>gRPC C# Surface API</Description>
<PackageIcon>packageIcon.png</PackageIcon>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/grpc/grpc</PackageProjectUrl>
<PackageTags>gRPC RPC HTTP/2</PackageTags>
<VersionPrefix>$(GrpcCsharpVersion)</VersionPrefix>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<TargetFrameworks>net45;netstandard1.5;netstandard2.0;netstandard2.1</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<Import Project="..\Grpc.Core\SourceLink.csproj.include" />
<ItemGroup>
<None Include="../packageIcon.png" Pack="true" PackagePath="\"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Memory" Version="4.5.3" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
<Reference Include="System" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
</Project>

@ -1,66 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System.Threading;
using System.Threading.Tasks;
namespace Grpc.Core
{
/// <summary>
/// A stream of messages to be read.
/// Messages can be awaited <c>await reader.MoveNext()</c>, that returns <c>true</c>
/// if there is a message available and <c>false</c> if there are no more messages
/// (i.e. the stream has been closed).
/// <para>
/// On the client side, the last invocation of <c>MoveNext()</c> either returns <c>false</c>
/// if the call has finished successfully or throws <c>RpcException</c> if call finished
/// with an error. Once the call finishes, subsequent invocations of <c>MoveNext()</c> will
/// continue yielding the same result (returning <c>false</c> or throwing an exception).
/// </para>
/// <para>
/// On the server side, <c>MoveNext()</c> does not throw exceptions.
/// In case of a failure, the request stream will appear to be finished
/// (<c>MoveNext</c> will return <c>false</c>) and the <c>CancellationToken</c>
/// associated with the call will be cancelled to signal the failure.
/// </para>
/// <para>
/// <c>MoveNext()</c> operations can be cancelled via a cancellation token. Cancelling
/// an individual read operation has the same effect as cancelling the entire call
/// (which will also result in the read operation returning prematurely), but the per-read cancellation
/// tokens passed to MoveNext() only result in cancelling the call if the read operation haven't finished
/// yet.
/// </para>
/// </summary>
/// <typeparam name="T">The message type.</typeparam>
public interface IAsyncStreamReader<out T>
{
/// <summary>
/// Gets the current element in the iteration.
/// </summary>
T Current { get; }
/// <summary>
/// Advances the reader to the next element in the sequence, returning the result asynchronously.
/// </summary>
/// <param name="cancellationToken">Cancellation token that can be used to cancel the operation.</param>
/// <returns>
/// Task containing the result of the operation: true if the reader was successfully advanced
/// to the next element; false if the reader has passed the end of the sequence.</returns>
Task<bool> MoveNext(CancellationToken cancellationToken);
}
}

@ -1,68 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Grpc.Core
{
/// <summary>
/// A writable stream of messages.
/// </summary>
/// <typeparam name="T">The message type.</typeparam>
public interface IAsyncStreamWriter<in T>
{
/// <summary>
/// Writes a message asynchronously. Only one write can be pending at a time.
/// </summary>
/// <param name="message">The message to be written. Cannot be null.</param>
Task WriteAsync(T message);
#if NETSTANDARD2_1_OR_GREATER
/// <summary>
/// Writes a message asynchronously. Only one write can be pending at a time.
/// </summary>
/// <param name="message">The message to be written. Cannot be null.</param>
/// <param name="cancellationToken">Cancellation token that can be used to cancel the operation.</param>
Task WriteAsync(T message, CancellationToken cancellationToken)
{
if (cancellationToken.CanBeCanceled)
{
// Note to implementors:
// Add a netstandard2.1 or greater target to your library and override
// WriteAsync(T, CancellationToken) on stream writer to use the cancellation token.
throw new NotSupportedException("Cancellation of stream writes is not supported by this gRPC implementation.");
}
return WriteAsync(message);
}
#endif
/// <summary>
/// Write options that will be used for the next write.
/// If null, default options will be used.
/// Once set, this property maintains its value across subsequent
/// writes.
/// </summary>
WriteOptions? WriteOptions { get; set; }
}
}

@ -1,38 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Grpc.Core
{
/// <summary>
/// Client-side writable stream of messages with Close capability.
/// </summary>
/// <typeparam name="T">The message type.</typeparam>
public interface IClientStreamWriter<in T> : IAsyncStreamWriter<T>
{
/// <summary>
/// Completes/closes the stream. Can only be called once there is no pending write. No writes should follow calling this.
/// </summary>
Task CompleteAsync();
}
}

@ -1,34 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Grpc.Core
{
/// <summary>
/// A writable stream of messages that is used in server-side handlers.
/// </summary>
public interface IServerStreamWriter<in T> : IAsyncStreamWriter<T>
{
// TODO(jtattermusch): consider just using IAsyncStreamWriter instead of this interface.
}
}

@ -1,143 +0,0 @@
#region Copyright notice and license
// Copyright 2018 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Linq;
using Grpc.Core.Utils;
namespace Grpc.Core.Interceptors
{
/// <summary>
/// Extends the CallInvoker class to provide the interceptor facility on the client side.
/// </summary>
public static class CallInvokerExtensions
{
/// <summary>
/// Returns a <see cref="Grpc.Core.CallInvoker" /> instance that intercepts
/// the invoker with the given interceptor.
/// </summary>
/// <param name="invoker">The underlying invoker to intercept.</param>
/// <param name="interceptor">The interceptor to intercept calls to the invoker with.</param>
/// <remarks>
/// Multiple interceptors can be added on top of each other by calling
/// "invoker.Intercept(a, b, c)". The order of invocation will be "a", "b", and then "c".
/// Interceptors can be later added to an existing intercepted CallInvoker, effectively
/// building a chain like "invoker.Intercept(c).Intercept(b).Intercept(a)". Note that
/// in this case, the last interceptor added will be the first to take control.
/// </remarks>
public static CallInvoker Intercept(this CallInvoker invoker, Interceptor interceptor)
{
return new InterceptingCallInvoker(invoker, interceptor);
}
/// <summary>
/// Returns a <see cref="Grpc.Core.CallInvoker" /> instance that intercepts
/// the invoker with the given interceptors.
/// </summary>
/// <param name="invoker">The channel to intercept.</param>
/// <param name="interceptors">
/// An array of interceptors to intercept the calls to the invoker with.
/// Control is passed to the interceptors in the order specified.
/// </param>
/// <remarks>
/// Multiple interceptors can be added on top of each other by calling
/// "invoker.Intercept(a, b, c)". The order of invocation will be "a", "b", and then "c".
/// Interceptors can be later added to an existing intercepted CallInvoker, effectively
/// building a chain like "invoker.Intercept(c).Intercept(b).Intercept(a)". Note that
/// in this case, the last interceptor added will be the first to take control.
/// </remarks>
public static CallInvoker Intercept(this CallInvoker invoker, params Interceptor[] interceptors)
{
GrpcPreconditions.CheckNotNull(invoker, nameof(invoker));
GrpcPreconditions.CheckNotNull(interceptors, nameof(interceptors));
foreach (var interceptor in interceptors.Reverse())
{
invoker = Intercept(invoker, interceptor);
}
return invoker;
}
/// <summary>
/// Returns a <see cref="Grpc.Core.CallInvoker" /> instance that intercepts
/// the invoker with the given interceptor.
/// </summary>
/// <param name="invoker">The underlying invoker to intercept.</param>
/// <param name="interceptor">
/// An interceptor delegate that takes the request metadata to be sent with an outgoing call
/// and returns a <see cref="Grpc.Core.Metadata" /> instance that will replace the existing
/// invocation metadata.
/// </param>
/// <remarks>
/// Multiple interceptors can be added on top of each other by
/// building a chain like "invoker.Intercept(c).Intercept(b).Intercept(a)". Note that
/// in this case, the last interceptor added will be the first to take control.
/// </remarks>
public static CallInvoker Intercept(this CallInvoker invoker, Func<Metadata, Metadata> interceptor)
{
return new InterceptingCallInvoker(invoker, new MetadataInterceptor(interceptor));
}
private class MetadataInterceptor : Interceptor
{
readonly Func<Metadata, Metadata> interceptor;
/// <summary>
/// Creates a new instance of MetadataInterceptor given the specified interceptor function.
/// </summary>
public MetadataInterceptor(Func<Metadata, Metadata> interceptor)
{
this.interceptor = GrpcPreconditions.CheckNotNull(interceptor, nameof(interceptor));
}
private ClientInterceptorContext<TRequest, TResponse> GetNewContext<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context)
where TRequest : class
where TResponse : class
{
var metadata = context.Options.Headers ?? new Metadata();
return new ClientInterceptorContext<TRequest, TResponse>(context.Method, context.Host, context.Options.WithHeaders(interceptor(metadata)));
}
public override TResponse BlockingUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, BlockingUnaryCallContinuation<TRequest, TResponse> continuation)
{
return continuation(request, GetNewContext(context));
}
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
{
return continuation(request, GetNewContext(context));
}
public override AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncServerStreamingCallContinuation<TRequest, TResponse> continuation)
{
return continuation(request, GetNewContext(context));
}
public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)
{
return continuation(GetNewContext(context));
}
public override AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncDuplexStreamingCallContinuation<TRequest, TResponse> continuation)
{
return continuation(GetNewContext(context));
}
}
}
}

@ -1,87 +0,0 @@
#region Copyright notice and license
// Copyright 2018 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
namespace Grpc.Core.Interceptors
{
/// <summary>
/// Provides extension methods to make it easy to register interceptors on Channel objects.
/// </summary>
public static class ChannelExtensions
{
/// <summary>
/// Returns a <see cref="Grpc.Core.CallInvoker" /> instance that intercepts
/// the channel with the given interceptor.
/// </summary>
/// <param name="channel">The channel to intercept.</param>
/// <param name="interceptor">The interceptor to intercept the channel with.</param>
/// <remarks>
/// Multiple interceptors can be added on top of each other by calling
/// "channel.Intercept(a, b, c)". The order of invocation will be "a", "b", and then "c".
/// Interceptors can be later added to an existing intercepted channel, effectively
/// building a chain like "channel.Intercept(c).Intercept(b).Intercept(a)". Note that
/// in this case, the last interceptor added will be the first to take control.
/// </remarks>
public static CallInvoker Intercept(this ChannelBase channel, Interceptor interceptor)
{
return channel.CreateCallInvoker().Intercept(interceptor);
}
/// <summary>
/// Returns a <see cref="Grpc.Core.CallInvoker" /> instance that intercepts
/// the channel with the given interceptors.
/// </summary>
/// <param name="channel">The channel to intercept.</param>
/// <param name="interceptors">
/// An array of interceptors to intercept the channel with.
/// Control is passed to the interceptors in the order specified.
/// </param>
/// <remarks>
/// Multiple interceptors can be added on top of each other by calling
/// "channel.Intercept(a, b, c)". The order of invocation will be "a", "b", and then "c".
/// Interceptors can be later added to an existing intercepted channel, effectively
/// building a chain like "channel.Intercept(c).Intercept(b).Intercept(a)". Note that
/// in this case, the last interceptor added will be the first to take control.
/// </remarks>
public static CallInvoker Intercept(this ChannelBase channel, params Interceptor[] interceptors)
{
return channel.CreateCallInvoker().Intercept(interceptors);
}
/// <summary>
/// Returns a <see cref="Grpc.Core.CallInvoker" /> instance that intercepts
/// the invoker with the given interceptor.
/// </summary>
/// <param name="channel">The channel to intercept.</param>
/// <param name="interceptor">
/// An interceptor delegate that takes the request metadata to be sent with an outgoing call
/// and returns a <see cref="Grpc.Core.Metadata" /> instance that will replace the existing
/// invocation metadata.
/// </param>
/// <remarks>
/// Multiple interceptors can be added on top of each other by
/// building a chain like "channel.Intercept(c).Intercept(b).Intercept(a)". Note that
/// in this case, the last interceptor added will be the first to take control.
/// </remarks>
public static CallInvoker Intercept(this ChannelBase channel, Func<Metadata, Metadata> interceptor)
{
return channel.CreateCallInvoker().Intercept(interceptor);
}
}
}

@ -1,64 +0,0 @@
#region Copyright notice and license
// Copyright 2018 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Reflection;
using System.Threading.Tasks;
using Grpc.Core.Internal;
namespace Grpc.Core.Interceptors
{
/// <summary>
/// Carries along the context associated with intercepted invocations on the client side.
/// </summary>
public struct ClientInterceptorContext<TRequest, TResponse>
where TRequest : class
where TResponse : class
{
/// <summary>
/// Creates a new instance of <see cref="Grpc.Core.Interceptors.ClientInterceptorContext{TRequest, TResponse}" />
/// with the specified method, host, and call options.
/// </summary>
/// <param name="method">A <see cref="Grpc.Core.Method{TRequest, TResponse}"/> object representing the method to be invoked.</param>
/// <param name="host">The host to dispatch the current call to.</param>
/// <param name="options">A <see cref="Grpc.Core.CallOptions"/> instance containing the call options of the current call.</param>
public ClientInterceptorContext(Method<TRequest, TResponse> method, string? host, CallOptions options)
{
Method = method;
Host = host;
Options = options;
}
/// <summary>
/// Gets the <see cref="Grpc.Core.Method{TRequest, TResponse}"/> instance
/// representing the method to be invoked.
/// </summary>
public Method<TRequest, TResponse> Method { get; }
/// <summary>
/// Gets the host that the currect invocation will be dispatched to.
/// </summary>
public string? Host { get; }
/// <summary>
/// Gets the <see cref="Grpc.Core.CallOptions"/> structure representing the
/// call options associated with the current invocation.
/// </summary>
public CallOptions Options { get; }
}
}

@ -1,96 +0,0 @@
#region Copyright notice and license
// Copyright 2018 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using Grpc.Core.Utils;
namespace Grpc.Core.Interceptors
{
/// <summary>
/// Decorates an underlying <see cref="Grpc.Core.CallInvoker" /> to
/// intercept calls through a given interceptor.
/// </summary>
internal class InterceptingCallInvoker : CallInvoker
{
readonly CallInvoker invoker;
readonly Interceptor interceptor;
/// <summary>
/// Creates a new instance of <see cref="Grpc.Core.Interceptors.InterceptingCallInvoker" />
/// with the given underlying invoker and interceptor instances.
/// </summary>
public InterceptingCallInvoker(CallInvoker invoker, Interceptor interceptor)
{
this.invoker = GrpcPreconditions.CheckNotNull(invoker, nameof(invoker));
this.interceptor = GrpcPreconditions.CheckNotNull(interceptor, nameof(interceptor));
}
/// <summary>
/// Intercepts a simple blocking call with the registered interceptor.
/// </summary>
public override TResponse BlockingUnaryCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string? host, CallOptions options, TRequest request)
{
return interceptor.BlockingUnaryCall(
request,
new ClientInterceptorContext<TRequest, TResponse>(method, host, options),
(req, ctx) => invoker.BlockingUnaryCall(ctx.Method, ctx.Host, ctx.Options, req));
}
/// <summary>
/// Intercepts a simple asynchronous call with the registered interceptor.
/// </summary>
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string? host, CallOptions options, TRequest request)
{
return interceptor.AsyncUnaryCall(
request,
new ClientInterceptorContext<TRequest, TResponse>(method, host, options),
(req, ctx) => invoker.AsyncUnaryCall(ctx.Method, ctx.Host, ctx.Options, req));
}
/// <summary>
/// Intercepts an asynchronous server streaming call with the registered interceptor.
/// </summary>
public override AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string? host, CallOptions options, TRequest request)
{
return interceptor.AsyncServerStreamingCall(
request,
new ClientInterceptorContext<TRequest, TResponse>(method, host, options),
(req, ctx) => invoker.AsyncServerStreamingCall(ctx.Method, ctx.Host, ctx.Options, req));
}
/// <summary>
/// Intercepts an asynchronous client streaming call with the registered interceptor.
/// </summary>
public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string? host, CallOptions options)
{
return interceptor.AsyncClientStreamingCall(
new ClientInterceptorContext<TRequest, TResponse>(method, host, options),
ctx => invoker.AsyncClientStreamingCall(ctx.Method, ctx.Host, ctx.Options));
}
/// <summary>
/// Intercepts an asynchronous duplex streaming call with the registered interceptor.
/// </summary>
public override AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string? host, CallOptions options)
{
return interceptor.AsyncDuplexStreamingCall(
new ClientInterceptorContext<TRequest, TResponse>(method, host, options),
ctx => invoker.AsyncDuplexStreamingCall(ctx.Method, ctx.Host, ctx.Options));
}
}
}

@ -1,405 +0,0 @@
#region Copyright notice and license
// Copyright 2018 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Reflection;
using System.Threading.Tasks;
using Grpc.Core.Internal;
namespace Grpc.Core.Interceptors
{
/// <summary>
/// Serves as the base class for gRPC interceptors.
/// </summary>
public abstract class Interceptor
{
/// <summary>
/// Represents a continuation for intercepting simple blocking invocations.
/// A delegate of this type is passed to the BlockingUnaryCall method
/// when an outgoing invocation is being intercepted and calling the
/// delegate will invoke the next interceptor in the chain, or the underlying
/// call invoker if called from the last interceptor. The interceptor is
/// allowed to call it zero, one, or multiple times, passing it the appropriate
/// context and request values as it sees fit.
/// </summary>
/// <typeparam name="TRequest">Request message type for this invocation.</typeparam>
/// <typeparam name="TResponse">Response message type for this invocation.</typeparam>
/// <param name="request">The request value to continue the invocation with.</param>
/// <param name="context">
/// The <see cref="Grpc.Core.Interceptors.ClientInterceptorContext{TRequest, TResponse}"/>
/// instance to pass to the next step in the invocation process.
/// </param>
/// <returns>
/// The response value of the invocation to return to the caller.
/// The interceptor can choose to return the return value of the
/// continuation delegate or an arbitrary value as it sees fit.
/// </returns>
public delegate TResponse BlockingUnaryCallContinuation<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context)
where TRequest : class
where TResponse : class;
/// <summary>
/// Represents a continuation for intercepting simple asynchronous invocations.
/// A delegate of this type is passed to the AsyncUnaryCall method
/// when an outgoing invocation is being intercepted and calling the
/// delegate will invoke the next interceptor in the chain, or the underlying
/// call invoker if called from the last interceptor. The interceptor is
/// allowed to call it zero, one, or multiple times, passing it the appropriate
/// request value and context as it sees fit.
/// </summary>
/// <typeparam name="TRequest">Request message type for this invocation.</typeparam>
/// <typeparam name="TResponse">Response message type for this invocation.</typeparam>
/// <param name="request">The request value to continue the invocation with.</param>
/// <param name="context">
/// The <see cref="Grpc.Core.Interceptors.ClientInterceptorContext{TRequest, TResponse}"/>
/// instance to pass to the next step in the invocation process.
/// </param>
/// <returns>
/// An instance of <see cref="Grpc.Core.AsyncUnaryCall{TResponse}" />
/// representing an asynchronous invocation of a unary RPC.
/// The interceptor can choose to return the same object returned from
/// the continuation delegate or an arbitrarily constructed instance as it sees fit.
/// </returns>
public delegate AsyncUnaryCall<TResponse> AsyncUnaryCallContinuation<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context)
where TRequest : class
where TResponse : class;
/// <summary>
/// Represents a continuation for intercepting asynchronous server-streaming invocations.
/// A delegate of this type is passed to the AsyncServerStreamingCall method
/// when an outgoing invocation is being intercepted and calling the
/// delegate will invoke the next interceptor in the chain, or the underlying
/// call invoker if called from the last interceptor. The interceptor is
/// allowed to call it zero, one, or multiple times, passing it the appropriate
/// request value and context as it sees fit.
/// </summary>
/// <typeparam name="TRequest">Request message type for this invocation.</typeparam>
/// <typeparam name="TResponse">Response message type for this invocation.</typeparam>
/// <param name="request">The request value to continue the invocation with.</param>
/// <param name="context">
/// The <see cref="Grpc.Core.Interceptors.ClientInterceptorContext{TRequest, TResponse}"/>
/// instance to pass to the next step in the invocation process.
/// </param>
/// <returns>
/// An instance of <see cref="Grpc.Core.AsyncServerStreamingCall{TResponse}" />
/// representing an asynchronous invocation of a server-streaming RPC.
/// The interceptor can choose to return the same object returned from
/// the continuation delegate or an arbitrarily constructed instance as it sees fit.
/// </returns>
public delegate AsyncServerStreamingCall<TResponse> AsyncServerStreamingCallContinuation<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context)
where TRequest : class
where TResponse : class;
/// <summary>
/// Represents a continuation for intercepting asynchronous client-streaming invocations.
/// A delegate of this type is passed to the AsyncClientStreamingCall method
/// when an outgoing invocation is being intercepted and calling the
/// delegate will invoke the next interceptor in the chain, or the underlying
/// call invoker if called from the last interceptor. The interceptor is
/// allowed to call it zero, one, or multiple times, passing it the appropriate
/// request value and context as it sees fit.
/// </summary>
/// <typeparam name="TRequest">Request message type for this invocation.</typeparam>
/// <typeparam name="TResponse">Response message type for this invocation.</typeparam>
/// <param name="context">
/// The <see cref="Grpc.Core.Interceptors.ClientInterceptorContext{TRequest, TResponse}"/>
/// instance to pass to the next step in the invocation process.
/// </param>
/// <returns>
/// An instance of <see cref="Grpc.Core.AsyncClientStreamingCall{TRequest, TResponse}" />
/// representing an asynchronous invocation of a client-streaming RPC.
/// The interceptor can choose to return the same object returned from
/// the continuation delegate or an arbitrarily constructed instance as it sees fit.
/// </returns>
public delegate AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCallContinuation<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context)
where TRequest : class
where TResponse : class;
/// <summary>
/// Represents a continuation for intercepting asynchronous duplex invocations.
/// A delegate of this type is passed to the AsyncDuplexStreamingCall method
/// when an outgoing invocation is being intercepted and calling the
/// delegate will invoke the next interceptor in the chain, or the underlying
/// call invoker if called from the last interceptor. The interceptor is
/// allowed to call it zero, one, or multiple times, passing it the appropriate
/// request value and context as it sees fit.
/// </summary>
/// <param name="context">
/// The <see cref="Grpc.Core.Interceptors.ClientInterceptorContext{TRequest, TResponse}"/>
/// instance to pass to the next step in the invocation process.
/// </param>
/// <returns>
/// An instance of <see cref="Grpc.Core.AsyncDuplexStreamingCall{TRequest, TResponse}" />
/// representing an asynchronous invocation of a duplex-streaming RPC.
/// The interceptor can choose to return the same object returned from
/// the continuation delegate or an arbitrarily constructed instance as it sees fit.
/// </returns>
public delegate AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCallContinuation<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context)
where TRequest : class
where TResponse : class;
/// <summary>
/// Intercepts a blocking invocation of a simple remote call.
/// </summary>
/// <param name="request">The request message of the invocation.</param>
/// <param name="context">
/// The <see cref="Grpc.Core.Interceptors.ClientInterceptorContext{TRequest, TResponse}"/>
/// associated with the current invocation.
/// </param>
/// <param name="continuation">
/// The callback that continues the invocation process.
/// This can be invoked zero or more times by the interceptor.
/// The interceptor can invoke the continuation passing the given
/// request value and context arguments, or substitute them as it sees fit.
/// </param>
/// <returns>
/// The response message of the current invocation.
/// The interceptor can simply return the return value of the
/// continuation delegate passed to it intact, or an arbitrary
/// value as it sees fit.
/// </returns>
public virtual TResponse BlockingUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, BlockingUnaryCallContinuation<TRequest, TResponse> continuation)
where TRequest : class
where TResponse : class
{
return continuation(request, context);
}
/// <summary>
/// Intercepts an asynchronous invocation of a simple remote call.
/// </summary>
/// <param name="request">The request message of the invocation.</param>
/// <param name="context">
/// The <see cref="Grpc.Core.Interceptors.ClientInterceptorContext{TRequest, TResponse}"/>
/// associated with the current invocation.
/// </param>
/// <param name="continuation">
/// The callback that continues the invocation process.
/// This can be invoked zero or more times by the interceptor.
/// The interceptor can invoke the continuation passing the given
/// request value and context arguments, or substitute them as it sees fit.
/// </param>
/// <returns>
/// An instance of <see cref="Grpc.Core.AsyncUnaryCall{TResponse}" />
/// representing an asynchronous unary invocation.
/// The interceptor can simply return the return value of the
/// continuation delegate passed to it intact, or construct its
/// own substitute as it sees fit.
/// </returns>
public virtual AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
where TRequest : class
where TResponse : class
{
return continuation(request, context);
}
/// <summary>
/// Intercepts an asynchronous invocation of a streaming remote call.
/// </summary>
/// <param name="request">The request message of the invocation.</param>
/// <param name="context">
/// The <see cref="Grpc.Core.Interceptors.ClientInterceptorContext{TRequest, TResponse}"/>
/// associated with the current invocation.
/// </param>
/// <param name="continuation">
/// The callback that continues the invocation process.
/// This can be invoked zero or more times by the interceptor.
/// The interceptor can invoke the continuation passing the given
/// request value and context arguments, or substitute them as it sees fit.
/// </param>
/// <returns>
/// An instance of <see cref="Grpc.Core.AsyncServerStreamingCall{TResponse}" />
/// representing an asynchronous server-streaming invocation.
/// The interceptor can simply return the return value of the
/// continuation delegate passed to it intact, or construct its
/// own substitute as it sees fit.
/// </returns>
public virtual AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncServerStreamingCallContinuation<TRequest, TResponse> continuation)
where TRequest : class
where TResponse : class
{
return continuation(request, context);
}
/// <summary>
/// Intercepts an asynchronous invocation of a client streaming call.
/// </summary>
/// <param name="context">
/// The <see cref="Grpc.Core.Interceptors.ClientInterceptorContext{TRequest, TResponse}"/>
/// associated with the current invocation.
/// </param>
/// <param name="continuation">
/// The callback that continues the invocation process.
/// This can be invoked zero or more times by the interceptor.
/// The interceptor can invoke the continuation passing the given
/// context argument, or substitute as it sees fit.
/// </param>
/// <returns>
/// An instance of <see cref="Grpc.Core.AsyncClientStreamingCall{TRequest, TResponse}" />
/// representing an asynchronous client-streaming invocation.
/// The interceptor can simply return the return value of the
/// continuation delegate passed to it intact, or construct its
/// own substitute as it sees fit.
/// </returns>
public virtual AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)
where TRequest : class
where TResponse : class
{
return continuation(context);
}
/// <summary>
/// Intercepts an asynchronous invocation of a duplex streaming call.
/// </summary>
/// <param name="context">
/// The <see cref="Grpc.Core.Interceptors.ClientInterceptorContext{TRequest, TResponse}"/>
/// associated with the current invocation.
/// </param>
/// <param name="continuation">
/// The callback that continues the invocation process.
/// This can be invoked zero or more times by the interceptor.
/// The interceptor can invoke the continuation passing the given
/// context argument, or substitute as it sees fit.
/// </param>
/// <returns>
/// An instance of <see cref="Grpc.Core.AsyncDuplexStreamingCall{TRequest, TResponse}" />
/// representing an asynchronous duplex-streaming invocation.
/// The interceptor can simply return the return value of the
/// continuation delegate passed to it intact, or construct its
/// own substitute as it sees fit.
/// </returns>
public virtual AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncDuplexStreamingCallContinuation<TRequest, TResponse> continuation)
where TRequest : class
where TResponse : class
{
return continuation(context);
}
/// <summary>
/// Server-side handler for intercepting and incoming unary call.
/// </summary>
/// <typeparam name="TRequest">Request message type for this method.</typeparam>
/// <typeparam name="TResponse">Response message type for this method.</typeparam>
/// <param name="request">The request value of the incoming invocation.</param>
/// <param name="context">
/// An instance of <see cref="Grpc.Core.ServerCallContext" /> representing
/// the context of the invocation.
/// </param>
/// <param name="continuation">
/// A delegate that asynchronously proceeds with the invocation, calling
/// the next interceptor in the chain, or the service request handler,
/// in case of the last interceptor and return the response value of
/// the RPC. The interceptor can choose to call it zero or more times
/// at its discretion.
/// </param>
/// <returns>
/// A future representing the response value of the RPC. The interceptor
/// can simply return the return value from the continuation intact,
/// or an arbitrary response value as it sees fit.
/// </returns>
public virtual Task<TResponse> UnaryServerHandler<TRequest, TResponse>(TRequest request, ServerCallContext context, UnaryServerMethod<TRequest, TResponse> continuation)
where TRequest : class
where TResponse : class
{
return continuation(request, context);
}
/// <summary>
/// Server-side handler for intercepting client streaming call.
/// </summary>
/// <typeparam name="TRequest">Request message type for this method.</typeparam>
/// <typeparam name="TResponse">Response message type for this method.</typeparam>
/// <param name="requestStream">The request stream of the incoming invocation.</param>
/// <param name="context">
/// An instance of <see cref="Grpc.Core.ServerCallContext" /> representing
/// the context of the invocation.
/// </param>
/// <param name="continuation">
/// A delegate that asynchronously proceeds with the invocation, calling
/// the next interceptor in the chain, or the service request handler,
/// in case of the last interceptor and return the response value of
/// the RPC. The interceptor can choose to call it zero or more times
/// at its discretion.
/// </param>
/// <returns>
/// A future representing the response value of the RPC. The interceptor
/// can simply return the return value from the continuation intact,
/// or an arbitrary response value as it sees fit. The interceptor has
/// the ability to wrap or substitute the request stream when calling
/// the continuation.
/// </returns>
public virtual Task<TResponse> ClientStreamingServerHandler<TRequest, TResponse>(IAsyncStreamReader<TRequest> requestStream, ServerCallContext context, ClientStreamingServerMethod<TRequest, TResponse> continuation)
where TRequest : class
where TResponse : class
{
return continuation(requestStream, context);
}
/// <summary>
/// Server-side handler for intercepting server streaming call.
/// </summary>
/// <typeparam name="TRequest">Request message type for this method.</typeparam>
/// <typeparam name="TResponse">Response message type for this method.</typeparam>
/// <param name="request">The request value of the incoming invocation.</param>
/// <param name="responseStream">The response stream of the incoming invocation.</param>
/// <param name="context">
/// An instance of <see cref="Grpc.Core.ServerCallContext" /> representing
/// the context of the invocation.
/// </param>
/// <param name="continuation">
/// A delegate that asynchronously proceeds with the invocation, calling
/// the next interceptor in the chain, or the service request handler,
/// in case of the last interceptor and the interceptor can choose to
/// call it zero or more times at its discretion. The interceptor has
/// the ability to wrap or substitute the request value and the response stream
/// when calling the continuation.
/// </param>
public virtual Task ServerStreamingServerHandler<TRequest, TResponse>(TRequest request, IServerStreamWriter<TResponse> responseStream, ServerCallContext context, ServerStreamingServerMethod<TRequest, TResponse> continuation)
where TRequest : class
where TResponse : class
{
return continuation(request, responseStream, context);
}
/// <summary>
/// Server-side handler for intercepting bidirectional streaming calls.
/// </summary>
/// <typeparam name="TRequest">Request message type for this method.</typeparam>
/// <typeparam name="TResponse">Response message type for this method.</typeparam>
/// <param name="requestStream">The request stream of the incoming invocation.</param>
/// <param name="responseStream">The response stream of the incoming invocation.</param>
/// <param name="context">
/// An instance of <see cref="Grpc.Core.ServerCallContext" /> representing
/// the context of the invocation.
/// </param>
/// <param name="continuation">
/// A delegate that asynchronously proceeds with the invocation, calling
/// the next interceptor in the chain, or the service request handler,
/// in case of the last interceptor and the interceptor can choose to
/// call it zero or more times at its discretion. The interceptor has
/// the ability to wrap or substitute the request and response streams
/// when calling the continuation.
/// </param>
public virtual Task DuplexStreamingServerHandler<TRequest, TResponse>(IAsyncStreamReader<TRequest> requestStream, IServerStreamWriter<TResponse> responseStream, ServerCallContext context, DuplexStreamingServerMethod<TRequest, TResponse> continuation)
where TRequest : class
where TResponse : class
{
return continuation(requestStream, responseStream, context);
}
}
}

@ -1,151 +0,0 @@
#region Copyright notice and license
// Copyright 2015-2016 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
// Content of this file is copied with permission from:
// https://github.com/dotnet/runtime/tree/e2e43f44f1032780fa7c2bb965153c9da615c3d0/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis
// These types are intented to be added as internalized source to libraries and apps that want to
// use them without requiring a dependency on .NET 5.
namespace System.Diagnostics.CodeAnalysis
{
/// <summary>
/// Indicates that certain members on a specified <see cref="Type"/> are accessed dynamically,
/// for example through <see cref="System.Reflection"/>.
/// </summary>
/// <remarks>
/// This allows tools to understand which members are being accessed during the execution
/// of a program.
///
/// This attribute is valid on members whose type is <see cref="Type"/> or <see cref="string"/>.
///
/// When this attribute is applied to a location of type <see cref="string"/>, the assumption is
/// that the string represents a fully qualified type name.
///
/// If the attribute is applied to a method it's treated as a special case and it implies
/// the attribute should be applied to the "this" parameter of the method. As such the attribute
/// should only be used on instance methods of types assignable to System.Type (or string, but no methods
/// will use it there).
/// </remarks>
[AttributeUsage(
AttributeTargets.Field | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter |
AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Method,
Inherited = false)]
internal sealed class DynamicallyAccessedMembersAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="DynamicallyAccessedMembersAttribute"/> class
/// with the specified member types.
/// </summary>
/// <param name="memberTypes">The types of members dynamically accessed.</param>
public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes)
{
MemberTypes = memberTypes;
}
/// <summary>
/// Gets the <see cref="DynamicallyAccessedMemberTypes"/> which specifies the type
/// of members dynamically accessed.
/// </summary>
public DynamicallyAccessedMemberTypes MemberTypes { get; }
}
/// <summary>
/// Specifies the types of members that are dynamically accessed.
///
/// This enumeration has a <see cref="FlagsAttribute"/> attribute that allows a
/// bitwise combination of its member values.
/// </summary>
[Flags]
internal enum DynamicallyAccessedMemberTypes
{
/// <summary>
/// Specifies no members.
/// </summary>
None = 0,
/// <summary>
/// Specifies the default, parameterless public constructor.
/// </summary>
PublicParameterlessConstructor = 0x0001,
/// <summary>
/// Specifies all public constructors.
/// </summary>
PublicConstructors = 0x0002 | PublicParameterlessConstructor,
/// <summary>
/// Specifies all non-public constructors.
/// </summary>
NonPublicConstructors = 0x0004,
/// <summary>
/// Specifies all public methods.
/// </summary>
PublicMethods = 0x0008,
/// <summary>
/// Specifies all non-public methods.
/// </summary>
NonPublicMethods = 0x0010,
/// <summary>
/// Specifies all public fields.
/// </summary>
PublicFields = 0x0020,
/// <summary>
/// Specifies all non-public fields.
/// </summary>
NonPublicFields = 0x0040,
/// <summary>
/// Specifies all public nested types.
/// </summary>
PublicNestedTypes = 0x0080,
/// <summary>
/// Specifies all non-public nested types.
/// </summary>
NonPublicNestedTypes = 0x0100,
/// <summary>
/// Specifies all public properties.
/// </summary>
PublicProperties = 0x0200,
/// <summary>
/// Specifies all non-public properties.
/// </summary>
NonPublicProperties = 0x0400,
/// <summary>
/// Specifies all public events.
/// </summary>
PublicEvents = 0x0800,
/// <summary>
/// Specifies all non-public events.
/// </summary>
NonPublicEvents = 0x1000,
/// <summary>
/// Specifies all members.
/// </summary>
All = ~None
}
}

@ -1,60 +0,0 @@
#region Copyright notice and license
// Copyright 2015-2016 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Utils;
namespace Grpc.Core.Internal
{
/// <summary>
/// Call invoker that throws <c>NotImplementedException</c> for all requests.
/// </summary>
internal class UnimplementedCallInvoker : CallInvoker
{
public UnimplementedCallInvoker()
{
}
public override TResponse BlockingUnaryCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string? host, CallOptions options, TRequest request)
{
throw new NotImplementedException();
}
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string? host, CallOptions options, TRequest request)
{
throw new NotImplementedException();
}
public override AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string? host, CallOptions options, TRequest request)
{
throw new NotImplementedException();
}
public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string? host, CallOptions options)
{
throw new NotImplementedException();
}
public override AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string? host, CallOptions options)
{
throw new NotImplementedException();
}
}
}

@ -1,68 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Collections.Generic;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
/// Key certificate pair (in PEM encoding).
/// </summary>
public sealed class KeyCertificatePair
{
readonly string certificateChain;
readonly string privateKey;
/// <summary>
/// Creates a new certificate chain - private key pair.
/// </summary>
/// <param name="certificateChain">PEM encoded certificate chain.</param>
/// <param name="privateKey">PEM encoded private key.</param>
public KeyCertificatePair(string certificateChain, string privateKey)
{
this.certificateChain = GrpcPreconditions.CheckNotNull(certificateChain, "certificateChain");
this.privateKey = GrpcPreconditions.CheckNotNull(privateKey, "privateKey");
}
/// <summary>
/// PEM encoded certificate chain.
/// </summary>
public string CertificateChain
{
get
{
return certificateChain;
}
}
/// <summary>
/// PEM encoded private key.
/// </summary>
public string PrivateKey
{
get
{
return privateKey;
}
}
}
}

@ -1,138 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
/// Encapsulates the logic for serializing and deserializing messages.
/// </summary>
public class Marshaller<T>
{
readonly Func<T, byte[]> serializer;
readonly Func<byte[], T> deserializer;
readonly Action<T, SerializationContext> contextualSerializer;
readonly Func<DeserializationContext, T> contextualDeserializer;
/// <summary>
/// Initializes a new marshaller from simple serialize/deserialize functions.
/// </summary>
/// <param name="serializer">Function that will be used to serialize messages.</param>
/// <param name="deserializer">Function that will be used to deserialize messages.</param>
public Marshaller(Func<T, byte[]> serializer, Func<byte[], T> deserializer)
{
this.serializer = GrpcPreconditions.CheckNotNull(serializer, nameof(serializer));
this.deserializer = GrpcPreconditions.CheckNotNull(deserializer, nameof(deserializer));
// contextual serialization/deserialization is emulated to make the marshaller
// usable with the grpc library (required for backward compatibility).
this.contextualSerializer = EmulateContextualSerializer;
this.contextualDeserializer = EmulateContextualDeserializer;
}
/// <summary>
/// Initializes a new marshaller from serialize/deserialize fuctions that can access serialization and deserialization
/// context. Compared to the simple serializer/deserializer functions, using the contextual version provides more
/// flexibility and can lead to increased efficiency (and better performance).
/// Note: This constructor is part of an experimental API that can change or be removed without any prior notice.
/// </summary>
/// <param name="serializer">Function that will be used to serialize messages.</param>
/// <param name="deserializer">Function that will be used to deserialize messages.</param>
public Marshaller(Action<T, SerializationContext> serializer, Func<DeserializationContext, T> deserializer)
{
this.contextualSerializer = GrpcPreconditions.CheckNotNull(serializer, nameof(serializer));
this.contextualDeserializer = GrpcPreconditions.CheckNotNull(deserializer, nameof(deserializer));
// gRPC only uses contextual serializer/deserializer internally, so emulating the legacy
// (de)serializer is not necessary.
this.serializer = (msg) => { throw new NotImplementedException(); };
this.deserializer = (payload) => { throw new NotImplementedException(); };
}
/// <summary>
/// Gets the serializer function.
/// </summary>
public Func<T, byte[]> Serializer => this.serializer;
/// <summary>
/// Gets the deserializer function.
/// </summary>
public Func<byte[], T> Deserializer => this.deserializer;
/// <summary>
/// Gets the serializer function.
/// Note: experimental API that can change or be removed without any prior notice.
/// </summary>
public Action<T, SerializationContext> ContextualSerializer => this.contextualSerializer;
/// <summary>
/// Gets the serializer function.
/// Note: experimental API that can change or be removed without any prior notice.
/// </summary>
public Func<DeserializationContext, T> ContextualDeserializer => this.contextualDeserializer;
// for backward compatibility, emulate the contextual serializer using the simple one
private void EmulateContextualSerializer(T message, SerializationContext context)
{
var payload = this.serializer(message);
context.Complete(payload);
}
// for backward compatibility, emulate the contextual deserializer using the simple one
private T EmulateContextualDeserializer(DeserializationContext context)
{
return this.deserializer(context.PayloadAsNewBuffer());
}
}
/// <summary>
/// Utilities for creating marshallers.
/// </summary>
public static class Marshallers
{
/// <summary>
/// Creates a marshaller from specified serializer and deserializer.
/// </summary>
public static Marshaller<T> Create<T>(Func<T, byte[]> serializer, Func<byte[], T> deserializer)
{
return new Marshaller<T>(serializer, deserializer);
}
/// <summary>
/// Creates a marshaller from specified contextual serializer and deserializer.
/// Note: This method is part of an experimental API that can change or be removed without any prior notice.
/// </summary>
public static Marshaller<T> Create<T>(Action<T, SerializationContext> serializer, Func<DeserializationContext, T> deserializer)
{
return new Marshaller<T>(serializer, deserializer);
}
/// <summary>
/// Returns a marshaller for <c>string</c> type. This is useful for testing.
/// </summary>
public static Marshaller<string> StringMarshaller
{
get
{
return new Marshaller<string>(System.Text.Encoding.UTF8.GetBytes,
System.Text.Encoding.UTF8.GetString);
}
}
}
}

@ -1,497 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using Grpc.Core.Api.Utils;
using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
/// A collection of metadata entries that can be exchanged during a call.
/// gRPC supports these types of metadata:
/// <list type="bullet">
/// <item><term>Request headers</term><description>are sent by the client at the beginning of a remote call before any request messages are sent.</description></item>
/// <item><term>Response headers</term><description>are sent by the server at the beginning of a remote call handler before any response messages are sent.</description></item>
/// <item><term>Response trailers</term><description>are sent by the server at the end of a remote call along with resulting call status.</description></item>
/// </list>
/// </summary>
public sealed class Metadata : IList<Metadata.Entry>
{
/// <summary>
/// All binary headers should have this suffix.
/// </summary>
public const string BinaryHeaderSuffix = "-bin";
/// <summary>
/// An read-only instance of metadata containing no entries.
/// </summary>
public static readonly Metadata Empty = new Metadata().Freeze();
/// <summary>
/// To be used in initial metadata to request specific compression algorithm
/// for given call. Direct selection of compression algorithms is an internal
/// feature and is not part of public API.
/// </summary>
internal const string CompressionRequestAlgorithmMetadataKey = "grpc-internal-encoding-request";
static readonly Encoding EncodingASCII = System.Text.Encoding.ASCII;
readonly List<Entry> entries;
bool readOnly;
/// <summary>
/// Initializes a new instance of <c>Metadata</c>.
/// </summary>
public Metadata()
{
this.entries = new List<Entry>();
}
/// <summary>
/// Makes this object read-only.
/// </summary>
/// <returns>this object</returns>
internal Metadata Freeze()
{
this.readOnly = true;
return this;
}
/// <summary>
/// Gets the last metadata entry with the specified key.
/// If there are no matching entries then <c>null</c> is returned.
/// </summary>
public Entry? Get(string key)
{
for (int i = entries.Count - 1; i >= 0; i--)
{
if (entries[i].KeyEqualsIgnoreCase(key))
{
return entries[i];
}
}
return null;
}
/// <summary>
/// Gets the string value of the last metadata entry with the specified key.
/// If the metadata entry is binary then an exception is thrown.
/// If there are no matching entries then <c>null</c> is returned.
/// </summary>
public string? GetValue(string key)
{
return Get(key)?.Value;
}
/// <summary>
/// Gets the bytes value of the last metadata entry with the specified key.
/// If the metadata entry is not binary the string value will be returned as ASCII encoded bytes.
/// If there are no matching entries then <c>null</c> is returned.
/// </summary>
public byte[]? GetValueBytes(string key)
{
return Get(key)?.ValueBytes;
}
/// <summary>
/// Gets all metadata entries with the specified key.
/// </summary>
public IEnumerable<Entry> GetAll(string key)
{
for (int i = 0; i < entries.Count; i++)
{
if (entries[i].KeyEqualsIgnoreCase(key))
{
yield return entries[i];
}
}
}
/// <summary>
/// Adds a new ASCII-valued metadata entry. See <c>Metadata.Entry</c> constructor for params.
/// </summary>
public void Add(string key, string value)
{
Add(new Entry(key, value));
}
/// <summary>
/// Adds a new binary-valued metadata entry. See <c>Metadata.Entry</c> constructor for params.
/// </summary>
public void Add(string key, byte[] valueBytes)
{
Add(new Entry(key, valueBytes));
}
#region IList members
/// <summary>
/// <see cref="T:IList`1"/>
/// </summary>
public int IndexOf(Metadata.Entry item)
{
return entries.IndexOf(item);
}
/// <summary>
/// <see cref="T:IList`1"/>
/// </summary>
public void Insert(int index, Metadata.Entry item)
{
GrpcPreconditions.CheckNotNull(item);
CheckWriteable();
entries.Insert(index, item);
}
/// <summary>
/// <see cref="T:IList`1"/>
/// </summary>
public void RemoveAt(int index)
{
CheckWriteable();
entries.RemoveAt(index);
}
/// <summary>
/// <see cref="T:IList`1"/>
/// </summary>
public Metadata.Entry this[int index]
{
get
{
return entries[index];
}
set
{
GrpcPreconditions.CheckNotNull(value);
CheckWriteable();
entries[index] = value;
}
}
/// <summary>
/// <see cref="T:IList`1"/>
/// </summary>
public void Add(Metadata.Entry item)
{
GrpcPreconditions.CheckNotNull(item);
CheckWriteable();
entries.Add(item);
}
/// <summary>
/// <see cref="T:IList`1"/>
/// </summary>
public void Clear()
{
CheckWriteable();
entries.Clear();
}
/// <summary>
/// <see cref="T:IList`1"/>
/// </summary>
public bool Contains(Metadata.Entry item)
{
return entries.Contains(item);
}
/// <summary>
/// <see cref="T:IList`1"/>
/// </summary>
public void CopyTo(Metadata.Entry[] array, int arrayIndex)
{
entries.CopyTo(array, arrayIndex);
}
/// <summary>
/// <see cref="T:IList`1"/>
/// </summary>
public int Count
{
get { return entries.Count; }
}
/// <summary>
/// <see cref="T:IList`1"/>
/// </summary>
public bool IsReadOnly
{
get { return readOnly; }
}
/// <summary>
/// <see cref="T:IList`1"/>
/// </summary>
public bool Remove(Metadata.Entry item)
{
CheckWriteable();
return entries.Remove(item);
}
/// <summary>
/// <see cref="T:IList`1"/>
/// </summary>
public IEnumerator<Metadata.Entry> GetEnumerator()
{
return entries.GetEnumerator();
}
IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return entries.GetEnumerator();
}
private void CheckWriteable()
{
GrpcPreconditions.CheckState(!readOnly, "Object is read only");
}
#endregion
/// <summary>
/// Metadata entry
/// </summary>
public class Entry
{
readonly string key;
readonly string? value;
readonly byte[]? valueBytes;
private Entry(string key, string? value, byte[]? valueBytes)
{
this.key = key;
this.value = value;
this.valueBytes = valueBytes;
}
/// <summary>
/// Initializes a new instance of the <see cref="Grpc.Core.Metadata.Entry"/> struct with a binary value.
/// </summary>
/// <param name="key">Metadata key. Gets converted to lowercase. Needs to have suffix indicating a binary valued metadata entry. Can only contain lowercase alphanumeric characters, underscores, hyphens and dots.</param>
/// <param name="valueBytes">Value bytes.</param>
public Entry(string key, byte[] valueBytes)
{
this.key = NormalizeKey(key);
GrpcPreconditions.CheckArgument(HasBinaryHeaderSuffix(this.key),
"Key for binary valued metadata entry needs to have suffix indicating binary value.");
this.value = null;
GrpcPreconditions.CheckNotNull(valueBytes, "valueBytes");
this.valueBytes = new byte[valueBytes.Length];
Buffer.BlockCopy(valueBytes, 0, this.valueBytes, 0, valueBytes.Length); // defensive copy to guarantee immutability
}
/// <summary>
/// Initializes a new instance of the <see cref="Grpc.Core.Metadata.Entry"/> struct with an ASCII value.
/// </summary>
/// <param name="key">Metadata key. Gets converted to lowercase. Must not use suffix indicating a binary valued metadata entry. Can only contain lowercase alphanumeric characters, underscores, hyphens and dots.</param>
/// <param name="value">Value string. Only ASCII characters are allowed.</param>
public Entry(string key, string value)
{
this.key = NormalizeKey(key);
GrpcPreconditions.CheckArgument(!HasBinaryHeaderSuffix(this.key),
"Key for ASCII valued metadata entry cannot have suffix indicating binary value.");
this.value = GrpcPreconditions.CheckNotNull(value, "value");
this.valueBytes = null;
}
/// <summary>
/// Gets the metadata entry key.
/// </summary>
public string Key
{
get
{
return this.key;
}
}
/// <summary>
/// Gets the binary value of this metadata entry.
/// If the metadata entry is not binary the string value will be returned as ASCII encoded bytes.
/// </summary>
public byte[] ValueBytes
{
get
{
if (valueBytes == null)
{
return EncodingASCII.GetBytes(value);
}
// defensive copy to guarantee immutability
var bytes = new byte[valueBytes.Length];
Buffer.BlockCopy(valueBytes, 0, bytes, 0, valueBytes.Length);
return bytes;
}
}
/// <summary>
/// Gets the string value of this metadata entry.
/// If the metadata entry is binary then an exception is thrown.
/// </summary>
public string Value
{
get
{
GrpcPreconditions.CheckState(!IsBinary, "Cannot access string value of a binary metadata entry");
return value!;
}
}
/// <summary>
/// Returns <c>true</c> if this entry is a binary-value entry.
/// </summary>
public bool IsBinary
{
get
{
return value == null;
}
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents the current <see cref="Grpc.Core.Metadata.Entry"/>.
/// </summary>
public override string ToString()
{
if (IsBinary)
{
return string.Format("[Entry: key={0}, valueBytes={1}]", key, valueBytes);
}
return string.Format("[Entry: key={0}, value={1}]", key, value);
}
/// <summary>
/// Gets the serialized value for this entry. For binary metadata entries, this leaks
/// the internal <c>valueBytes</c> byte array and caller must not change contents of it.
/// </summary>
internal byte[] GetSerializedValueUnsafe()
{
return valueBytes ?? EncodingASCII.GetBytes(value);
}
internal bool KeyEqualsIgnoreCase(string key)
{
// NormalizeKey() uses ToLowerInvariant() to lowercase keys, so we'd like to use the same invariant culture
// for comparisons to get valid results. StringComparison.InvariantCultureIgnoreCase isn't available
// on all the frameworks we're targeting, but since we know that the Entry's key has already
// been checked by IsValidKey and it only contains a subset of ASCII, using StringComparison.OrdinalIgnoreCase
// is also fine.
return string.Equals(this.key, key, StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// Creates a binary value or ascii value metadata entry from data received from the native layer.
/// We trust C core to give us well-formed data, so we don't perform any checks or defensive copying.
/// </summary>
internal static Entry CreateUnsafe(string key, IntPtr source, int length)
{
if (HasBinaryHeaderSuffix(key))
{
byte[] arr;
if (length == 0)
{
arr = EmptyByteArray;
}
else
{ // create a local copy in a fresh array
arr = new byte[length];
Marshal.Copy(source, arr, 0, length);
}
return new Entry(key, null, arr);
}
else
{
string s = EncodingASCII.GetString(source, length);
return new Entry(key, s, null);
}
}
static readonly byte[] EmptyByteArray = new byte[0];
private static string NormalizeKey(string key)
{
GrpcPreconditions.CheckNotNull(key, "key");
GrpcPreconditions.CheckArgument(IsValidKey(key, out bool isLowercase),
"Metadata entry key not valid. Keys can only contain lowercase alphanumeric characters, underscores, hyphens and dots.");
if (isLowercase)
{
// save allocation of a new string if already lowercase
return key;
}
return key.ToLowerInvariant();
}
private static bool IsValidKey(string input, out bool isLowercase)
{
isLowercase = true;
for (int i = 0; i < input.Length; i++)
{
char c = input[i];
if ('a' <= c && c <= 'z' ||
'0' <= c && c <= '9' ||
c == '.' ||
c == '_' ||
c == '-' )
continue;
if ('A' <= c && c <= 'Z')
{
isLowercase = false;
continue;
}
return false;
}
return true;
}
/// <summary>
/// Returns <c>true</c> if the key has "-bin" binary header suffix.
/// </summary>
private static bool HasBinaryHeaderSuffix(string key)
{
// We don't use just string.EndsWith because its implementation is extremely slow
// on CoreCLR and we've seen significant differences in gRPC benchmarks caused by it.
// See https://github.com/dotnet/coreclr/issues/5612
int len = key.Length;
if (len >= 4 &&
key[len - 4] == '-' &&
key[len - 3] == 'b' &&
key[len - 2] == 'i' &&
key[len - 1] == 'n')
{
return true;
}
return false;
}
}
}
}

@ -1,176 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
/// Method types supported by gRPC.
/// </summary>
public enum MethodType
{
/// <summary>Single request sent from client, single response received from server.</summary>
Unary,
/// <summary>Stream of request sent from client, single response received from server.</summary>
ClientStreaming,
/// <summary>Single request sent from client, stream of responses received from server.</summary>
ServerStreaming,
/// <summary>Both server and client can stream arbitrary number of requests and responses simultaneously.</summary>
DuplexStreaming
}
/// <summary>
/// A non-generic representation of a remote method.
/// </summary>
public interface IMethod
{
/// <summary>
/// Gets the type of the method.
/// </summary>
MethodType Type { get; }
/// <summary>
/// Gets the name of the service to which this method belongs.
/// </summary>
string ServiceName { get; }
/// <summary>
/// Gets the unqualified name of the method.
/// </summary>
string Name { get; }
/// <summary>
/// Gets the fully qualified name of the method. On the server side, methods are dispatched
/// based on this name.
/// </summary>
string FullName { get; }
}
/// <summary>
/// A description of a remote method.
/// </summary>
/// <typeparam name="TRequest">Request message type for this method.</typeparam>
/// <typeparam name="TResponse">Response message type for this method.</typeparam>
public class Method<TRequest, TResponse> : IMethod
{
readonly MethodType type;
readonly string serviceName;
readonly string name;
readonly Marshaller<TRequest> requestMarshaller;
readonly Marshaller<TResponse> responseMarshaller;
readonly string fullName;
/// <summary>
/// Initializes a new instance of the <c>Method</c> class.
/// </summary>
/// <param name="type">Type of method.</param>
/// <param name="serviceName">Name of service this method belongs to.</param>
/// <param name="name">Unqualified name of the method.</param>
/// <param name="requestMarshaller">Marshaller used for request messages.</param>
/// <param name="responseMarshaller">Marshaller used for response messages.</param>
public Method(MethodType type, string serviceName, string name, Marshaller<TRequest> requestMarshaller, Marshaller<TResponse> responseMarshaller)
{
this.type = type;
this.serviceName = GrpcPreconditions.CheckNotNull(serviceName, "serviceName");
this.name = GrpcPreconditions.CheckNotNull(name, "name");
this.requestMarshaller = GrpcPreconditions.CheckNotNull(requestMarshaller, "requestMarshaller");
this.responseMarshaller = GrpcPreconditions.CheckNotNull(responseMarshaller, "responseMarshaller");
this.fullName = GetFullName(serviceName, name);
}
/// <summary>
/// Gets the type of the method.
/// </summary>
public MethodType Type
{
get
{
return this.type;
}
}
/// <summary>
/// Gets the name of the service to which this method belongs.
/// </summary>
public string ServiceName
{
get
{
return this.serviceName;
}
}
/// <summary>
/// Gets the unqualified name of the method.
/// </summary>
public string Name
{
get
{
return this.name;
}
}
/// <summary>
/// Gets the marshaller used for request messages.
/// </summary>
public Marshaller<TRequest> RequestMarshaller
{
get
{
return this.requestMarshaller;
}
}
/// <summary>
/// Gets the marshaller used for response messages.
/// </summary>
public Marshaller<TResponse> ResponseMarshaller
{
get
{
return this.responseMarshaller;
}
}
/// <summary>
/// Gets the fully qualified name of the method. On the server side, methods are dispatched
/// based on this name.
/// </summary>
public string FullName
{
get
{
return this.fullName;
}
}
/// <summary>
/// Gets full name of the method including the service name.
/// </summary>
internal static string GetFullName(string serviceName, string methodName)
{
return "/" + serviceName + "/" + methodName;
}
}
}

@ -1,55 +0,0 @@
#region Copyright notice and license
// Copyright 2019 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System.Reflection;
using System.Runtime.CompilerServices;
[assembly: AssemblyTitle("Grpc.Core.Api")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("Google Inc. All rights reserved.")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: InternalsVisibleTo("Grpc.Core,PublicKey=" +
"00240000048000009400000006020000002400005253413100040000010001002f5797a92c6fcde81bd4098f43" +
"0442bb8e12768722de0b0cb1b15e955b32a11352740ee59f2c94c48edc8e177d1052536b8ac651bce11ce5da3a" +
"27fc95aff3dc604a6971417453f9483c7b5e836756d5b271bf8f2403fe186e31956148c03d804487cf642f8cc0" +
"71394ee9672dfe5b55ea0f95dfd5a7f77d22c962ccf51320d3")]
[assembly: InternalsVisibleTo("Grpc.Core.Tests,PublicKey=" +
"00240000048000009400000006020000002400005253413100040000010001002f5797a92c6fcde81bd4098f43" +
"0442bb8e12768722de0b0cb1b15e955b32a11352740ee59f2c94c48edc8e177d1052536b8ac651bce11ce5da3a" +
"27fc95aff3dc604a6971417453f9483c7b5e836756d5b271bf8f2403fe186e31956148c03d804487cf642f8cc0" +
"71394ee9672dfe5b55ea0f95dfd5a7f77d22c962ccf51320d3")]
[assembly: InternalsVisibleTo("Grpc.Core.Testing,PublicKey=" +
"00240000048000009400000006020000002400005253413100040000010001002f5797a92c6fcde81bd4098f43" +
"0442bb8e12768722de0b0cb1b15e955b32a11352740ee59f2c94c48edc8e177d1052536b8ac651bce11ce5da3a" +
"27fc95aff3dc604a6971417453f9483c7b5e836756d5b271bf8f2403fe186e31956148c03d804487cf642f8cc0" +
"71394ee9672dfe5b55ea0f95dfd5a7f77d22c962ccf51320d3")]
[assembly: InternalsVisibleTo("Grpc.IntegrationTesting,PublicKey=" +
"00240000048000009400000006020000002400005253413100040000010001002f5797a92c6fcde81bd4098f43" +
"0442bb8e12768722de0b0cb1b15e955b32a11352740ee59f2c94c48edc8e177d1052536b8ac651bce11ce5da3a" +
"27fc95aff3dc604a6971417453f9483c7b5e836756d5b271bf8f2403fe186e31956148c03d804487cf642f8cc0" +
"71394ee9672dfe5b55ea0f95dfd5a7f77d22c962ccf51320d3")]
[assembly: InternalsVisibleTo("Grpc.Microbenchmarks,PublicKey=" +
"00240000048000009400000006020000002400005253413100040000010001002f5797a92c6fcde81bd4098f43" +
"0442bb8e12768722de0b0cb1b15e955b32a11352740ee59f2c94c48edc8e177d1052536b8ac651bce11ce5da3a" +
"27fc95aff3dc604a6971417453f9483c7b5e836756d5b271bf8f2403fe186e31956148c03d804487cf642f8cc0" +
"71394ee9672dfe5b55ea0f95dfd5a7f77d22c962ccf51320d3")]

@ -1,109 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
/// Thrown when remote procedure call fails. Every <c>RpcException</c> is associated with a resulting <see cref="Status"/> of the call.
/// </summary>
public class RpcException : Exception
{
private readonly Status status;
private readonly Metadata trailers;
/// <summary>
/// Creates a new <c>RpcException</c> associated with given status.
/// </summary>
/// <param name="status">Resulting status of a call.</param>
public RpcException(Status status) : this(status, Metadata.Empty, status.ToString())
{
}
/// <summary>
/// Creates a new <c>RpcException</c> associated with given status and message.
/// NOTE: the exception message is not sent to the remote peer. Use <c>status.Details</c> to pass error
/// details to the peer.
/// </summary>
/// <param name="status">Resulting status of a call.</param>
/// <param name="message">The exception message.</param>
public RpcException(Status status, string message) : this(status, Metadata.Empty, message)
{
}
/// <summary>
/// Creates a new <c>RpcException</c> associated with given status and trailing response metadata.
/// </summary>
/// <param name="status">Resulting status of a call.</param>
/// <param name="trailers">Response trailing metadata.</param>
public RpcException(Status status, Metadata trailers) : this(status, trailers, status.ToString())
{
}
/// <summary>
/// Creates a new <c>RpcException</c> associated with given status, message and trailing response metadata.
/// NOTE: the exception message is not sent to the remote peer. Use <c>status.Details</c> to pass error
/// details to the peer.
/// </summary>
/// <param name="status">Resulting status of a call.</param>
/// <param name="trailers">Response trailing metadata.</param>
/// <param name="message">The exception message.</param>
public RpcException(Status status, Metadata trailers, string message) : base(message)
{
this.status = status;
this.trailers = GrpcPreconditions.CheckNotNull(trailers);
}
/// <summary>
/// Resulting status of the call.
/// </summary>
public Status Status
{
get
{
return status;
}
}
/// <summary>
/// Returns the status code of the call, as a convenient alternative to <see cref="StatusCode">Status.StatusCode</see>.
/// </summary>
public StatusCode StatusCode
{
get
{
return status.StatusCode;
}
}
/// <summary>
/// Gets the call trailing metadata.
/// Trailers only have meaningful content for client-side calls (in which case they represent the trailing metadata sent by the server when closing the call).
/// Instances of <c>RpcException</c> thrown by the server-side part of the stack will have trailers always set to empty.
/// </summary>
public Metadata Trailers
{
get
{
return trailers;
}
}
}
}

@ -1,69 +0,0 @@
#region Copyright notice and license
// Copyright 2018 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Buffers;
namespace Grpc.Core
{
/// <summary>
/// Provides storage for payload when serializing a message.
/// </summary>
public abstract class SerializationContext
{
/// <summary>
/// Use the byte array as serialized form of current message and mark serialization process as complete.
/// <c>Complete(byte[])</c> can only be called once. By calling this method the caller gives up the ownership of the
/// payload which must not be accessed afterwards.
/// </summary>
/// <param name="payload">the serialized form of current message</param>
public virtual void Complete(byte[] payload)
{
throw new NotImplementedException();
}
/// <summary>
/// Gets buffer writer that can be used to write the serialized data. Once serialization is finished,
/// <c>Complete()</c> needs to be called.
/// </summary>
public virtual IBufferWriter<byte> GetBufferWriter()
{
throw new NotImplementedException();
}
/// <summary>
/// Sets the payload length when writing serialized data into a buffer writer. If the serializer knows the full payload
/// length in advance, providing that information before obtaining the buffer writer using <c>GetBufferWriter()</c> can improve
/// serialization efficiency by avoiding copies. The provided payload length must be the same as the data written to the writer.
/// Calling this method is optional. If the payload length is not set then the length is calculated using the data written to
/// the buffer writer when <c>Complete()</c> is called.
/// </summary>
/// <param name="payloadLength">The total length of the payload in bytes.</param>
public virtual void SetPayloadLength(int payloadLength)
{
}
/// <summary>
/// Complete the payload written to the buffer writer. <c>Complete()</c> can only be called once.
/// </summary>
public virtual void Complete()
{
throw new NotImplementedException();
}
}
}

@ -1,163 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Grpc.Core
{
/// <summary>
/// Context for a server-side call.
/// </summary>
public abstract class ServerCallContext
{
private Dictionary<object, object>? userState;
/// <summary>
/// Creates a new instance of <c>ServerCallContext</c>.
/// </summary>
protected ServerCallContext()
{
}
/// <summary>
/// Asynchronously sends response headers for the current call to the client. This method may only be invoked once for each call and needs to be invoked
/// before any response messages are written. Writing the first response message implicitly sends empty response headers if <c>WriteResponseHeadersAsync</c> haven't
/// been called yet.
/// </summary>
/// <param name="responseHeaders">The response headers to send.</param>
/// <returns>The task that finished once response headers have been written.</returns>
public Task WriteResponseHeadersAsync(Metadata responseHeaders)
{
return WriteResponseHeadersAsyncCore(responseHeaders);
}
/// <summary>
/// Creates a propagation token to be used to propagate call context to a child call.
/// </summary>
public ContextPropagationToken CreatePropagationToken(ContextPropagationOptions? options = null)
{
return CreatePropagationTokenCore(options);
}
/// <summary>Name of method called in this RPC.</summary>
public string Method => MethodCore;
/// <summary>Name of host called in this RPC.</summary>
public string Host => HostCore;
/// <summary>Address of the remote endpoint in URI format.</summary>
public string Peer => PeerCore;
/// <summary>Deadline for this RPC. The call will be automatically cancelled once the deadline is exceeded.</summary>
public DateTime Deadline => DeadlineCore;
/// <summary>Initial metadata sent by client.</summary>
public Metadata RequestHeaders => RequestHeadersCore;
/// <summary>Cancellation token signals when call is cancelled. It is also triggered when the deadline is exceeeded or there was some other error (e.g. network problem).</summary>
public CancellationToken CancellationToken => CancellationTokenCore;
/// <summary>Trailers to send back to client after RPC finishes.</summary>
public Metadata ResponseTrailers => ResponseTrailersCore;
/// <summary> Status to send back to client after RPC finishes.</summary>
public Status Status
{
get
{
return StatusCore;
}
set
{
StatusCore = value;
}
}
/// <summary>
/// Allows setting write options for the following write.
/// For streaming response calls, this property is also exposed as on IServerStreamWriter for convenience.
/// Both properties are backed by the same underlying value.
/// </summary>
public WriteOptions? WriteOptions
{
get
{
return WriteOptionsCore;
}
set
{
WriteOptionsCore = value;
}
}
/// <summary>
/// Gets the <c>AuthContext</c> associated with this call.
/// Note: Access to AuthContext is an experimental API that can change without any prior notice.
/// </summary>
public AuthContext AuthContext => AuthContextCore;
/// <summary>
/// Gets a dictionary that can be used by the various interceptors and handlers of this
/// call to store arbitrary state.
/// </summary>
public IDictionary<object, object> UserState => UserStateCore;
/// <summary>Provides implementation of a non-virtual public member.</summary>
protected abstract Task WriteResponseHeadersAsyncCore(Metadata responseHeaders);
/// <summary>Provides implementation of a non-virtual public member.</summary>
protected abstract ContextPropagationToken CreatePropagationTokenCore(ContextPropagationOptions? options);
/// <summary>Provides implementation of a non-virtual public member.</summary>
protected abstract string MethodCore { get; }
/// <summary>Provides implementation of a non-virtual public member.</summary>
protected abstract string HostCore { get; }
/// <summary>Provides implementation of a non-virtual public member.</summary>
protected abstract string PeerCore { get; }
/// <summary>Provides implementation of a non-virtual public member.</summary>
protected abstract DateTime DeadlineCore { get; }
/// <summary>Provides implementation of a non-virtual public member.</summary>
protected abstract Metadata RequestHeadersCore { get; }
/// <summary>Provides implementation of a non-virtual public member.</summary>
protected abstract CancellationToken CancellationTokenCore { get; }
/// <summary>Provides implementation of a non-virtual public member.</summary>
protected abstract Metadata ResponseTrailersCore { get; }
/// <summary>Provides implementation of a non-virtual public member.</summary>
protected abstract Status StatusCore { get; set; }
/// <summary>Provides implementation of a non-virtual public member.</summary>
protected abstract WriteOptions? WriteOptionsCore { get; set; }
/// <summary>Provides implementation of a non-virtual public member.</summary>
protected abstract AuthContext AuthContextCore { get; }
/// <summary>Provides implementation of a non-virtual public member.</summary>
protected virtual IDictionary<object, object> UserStateCore
{
get
{
if (userState == null)
{
userState = new Dictionary<object, object>();
}
return userState;
}
}
}
}

@ -1,58 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System.Threading.Tasks;
namespace Grpc.Core
{
/// <summary>
/// Server-side handler for unary call.
/// </summary>
/// <typeparam name="TRequest">Request message type for this method.</typeparam>
/// <typeparam name="TResponse">Response message type for this method.</typeparam>
public delegate Task<TResponse> UnaryServerMethod<TRequest, TResponse>(TRequest request, ServerCallContext context)
where TRequest : class
where TResponse : class;
/// <summary>
/// Server-side handler for client streaming call.
/// </summary>
/// <typeparam name="TRequest">Request message type for this method.</typeparam>
/// <typeparam name="TResponse">Response message type for this method.</typeparam>
public delegate Task<TResponse> ClientStreamingServerMethod<TRequest, TResponse>(IAsyncStreamReader<TRequest> requestStream, ServerCallContext context)
where TRequest : class
where TResponse : class;
/// <summary>
/// Server-side handler for server streaming call.
/// </summary>
/// <typeparam name="TRequest">Request message type for this method.</typeparam>
/// <typeparam name="TResponse">Response message type for this method.</typeparam>
public delegate Task ServerStreamingServerMethod<TRequest, TResponse>(TRequest request, IServerStreamWriter<TResponse> responseStream, ServerCallContext context)
where TRequest : class
where TResponse : class;
/// <summary>
/// Server-side handler for bidi streaming call.
/// </summary>
/// <typeparam name="TRequest">Request message type for this method.</typeparam>
/// <typeparam name="TResponse">Response message type for this method.</typeparam>
public delegate Task DuplexStreamingServerMethod<TRequest, TResponse>(IAsyncStreamReader<TRequest> requestStream, IServerStreamWriter<TResponse> responseStream, ServerCallContext context)
where TRequest : class
where TResponse : class;
}

@ -1,161 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Collections.Generic;
namespace Grpc.Core
{
/// <summary>
/// Stores mapping of methods to server call handlers.
/// Normally, the <c>ServerServiceDefinition</c> objects will be created by the <c>BindService</c> factory method
/// that is part of the autogenerated code for a protocol buffers service definition.
/// </summary>
public class ServerServiceDefinition
{
readonly IReadOnlyList<Action<ServiceBinderBase>> addMethodActions;
internal ServerServiceDefinition(List<Action<ServiceBinderBase>> addMethodActions)
{
this.addMethodActions = addMethodActions.AsReadOnly();
}
/// <summary>
/// Forwards all the previously stored <c>AddMethod</c> calls to the service binder.
/// </summary>
internal void BindService(ServiceBinderBase serviceBinder)
{
foreach (var addMethodAction in addMethodActions)
{
addMethodAction(serviceBinder);
}
}
/// <summary>
/// Creates a new builder object for <c>ServerServiceDefinition</c>.
/// </summary>
/// <returns>The builder object.</returns>
public static Builder CreateBuilder()
{
return new Builder();
}
/// <summary>
/// Builder class for <see cref="ServerServiceDefinition"/>.
/// </summary>
public class Builder
{
// to maintain legacy behavior, we need to detect duplicate keys and throw the same exception as before
readonly Dictionary<string, object?> duplicateDetector = new Dictionary<string, object?>();
// for each AddMethod call, we store an action that will later register the method and handler with ServiceBinderBase
readonly List<Action<ServiceBinderBase>> addMethodActions = new List<Action<ServiceBinderBase>>();
/// <summary>
/// Creates a new instance of builder.
/// </summary>
public Builder()
{
}
/// <summary>
/// Adds a definition for a single request - single response method.
/// </summary>
/// <typeparam name="TRequest">The request message class.</typeparam>
/// <typeparam name="TResponse">The response message class.</typeparam>
/// <param name="method">The method.</param>
/// <param name="handler">The method handler.</param>
/// <returns>This builder instance.</returns>
public Builder AddMethod<TRequest, TResponse>(
Method<TRequest, TResponse> method,
UnaryServerMethod<TRequest, TResponse> handler)
where TRequest : class
where TResponse : class
{
duplicateDetector.Add(method.FullName, null);
addMethodActions.Add((serviceBinder) => serviceBinder.AddMethod(method, handler));
return this;
}
/// <summary>
/// Adds a definition for a client streaming method.
/// </summary>
/// <typeparam name="TRequest">The request message class.</typeparam>
/// <typeparam name="TResponse">The response message class.</typeparam>
/// <param name="method">The method.</param>
/// <param name="handler">The method handler.</param>
/// <returns>This builder instance.</returns>
public Builder AddMethod<TRequest, TResponse>(
Method<TRequest, TResponse> method,
ClientStreamingServerMethod<TRequest, TResponse> handler)
where TRequest : class
where TResponse : class
{
duplicateDetector.Add(method.FullName, null);
addMethodActions.Add((serviceBinder) => serviceBinder.AddMethod(method, handler));
return this;
}
/// <summary>
/// Adds a definition for a server streaming method.
/// </summary>
/// <typeparam name="TRequest">The request message class.</typeparam>
/// <typeparam name="TResponse">The response message class.</typeparam>
/// <param name="method">The method.</param>
/// <param name="handler">The method handler.</param>
/// <returns>This builder instance.</returns>
public Builder AddMethod<TRequest, TResponse>(
Method<TRequest, TResponse> method,
ServerStreamingServerMethod<TRequest, TResponse> handler)
where TRequest : class
where TResponse : class
{
duplicateDetector.Add(method.FullName, null);
addMethodActions.Add((serviceBinder) => serviceBinder.AddMethod(method, handler));
return this;
}
/// <summary>
/// Adds a definition for a bidirectional streaming method.
/// </summary>
/// <typeparam name="TRequest">The request message class.</typeparam>
/// <typeparam name="TResponse">The response message class.</typeparam>
/// <param name="method">The method.</param>
/// <param name="handler">The method handler.</param>
/// <returns>This builder instance.</returns>
public Builder AddMethod<TRequest, TResponse>(
Method<TRequest, TResponse> method,
DuplexStreamingServerMethod<TRequest, TResponse> handler)
where TRequest : class
where TResponse : class
{
duplicateDetector.Add(method.FullName, null);
addMethodActions.Add((serviceBinder) => serviceBinder.AddMethod(method, handler));
return this;
}
/// <summary>
/// Creates an immutable <c>ServerServiceDefinition</c> from this builder.
/// </summary>
/// <returns>The <c>ServerServiceDefinition</c> object.</returns>
public ServerServiceDefinition Build()
{
return new ServerServiceDefinition(addMethodActions);
}
}
}
}

@ -1,98 +0,0 @@
#region Copyright notice and license
// Copyright 2018 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
/// Allows binding server-side method implementations in alternative serving stacks.
/// Instances of this class are usually populated by the <c>BindService</c> method
/// that is part of the autogenerated code for a protocol buffers service definition.
/// </summary>
public class ServiceBinderBase
{
/// <summary>
/// Adds a definition for a single request - single response method.
/// </summary>
/// <typeparam name="TRequest">The request message class.</typeparam>
/// <typeparam name="TResponse">The response message class.</typeparam>
/// <param name="method">The method.</param>
/// <param name="handler">The method handler.</param>
public virtual void AddMethod<TRequest, TResponse>(
Method<TRequest, TResponse> method,
UnaryServerMethod<TRequest, TResponse> handler)
where TRequest : class
where TResponse : class
{
throw new NotImplementedException();
}
/// <summary>
/// Adds a definition for a client streaming method.
/// </summary>
/// <typeparam name="TRequest">The request message class.</typeparam>
/// <typeparam name="TResponse">The response message class.</typeparam>
/// <param name="method">The method.</param>
/// <param name="handler">The method handler.</param>
public virtual void AddMethod<TRequest, TResponse>(
Method<TRequest, TResponse> method,
ClientStreamingServerMethod<TRequest, TResponse> handler)
where TRequest : class
where TResponse : class
{
throw new NotImplementedException();
}
/// <summary>
/// Adds a definition for a server streaming method.
/// </summary>
/// <typeparam name="TRequest">The request message class.</typeparam>
/// <typeparam name="TResponse">The response message class.</typeparam>
/// <param name="method">The method.</param>
/// <param name="handler">The method handler.</param>
public virtual void AddMethod<TRequest, TResponse>(
Method<TRequest, TResponse> method,
ServerStreamingServerMethod<TRequest, TResponse> handler)
where TRequest : class
where TResponse : class
{
throw new NotImplementedException();
}
/// <summary>
/// Adds a definition for a bidirectional streaming method.
/// </summary>
/// <typeparam name="TRequest">The request message class.</typeparam>
/// <typeparam name="TResponse">The response message class.</typeparam>
/// <param name="method">The method.</param>
/// <param name="handler">The method handler.</param>
public virtual void AddMethod<TRequest, TResponse>(
Method<TRequest, TResponse> method,
DuplexStreamingServerMethod<TRequest, TResponse> handler)
where TRequest : class
where TResponse : class
{
throw new NotImplementedException();
}
}
}

@ -1,122 +0,0 @@
#region Copyright notice and license
// Copyright 2019 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
namespace Grpc.Core
{
/// <summary>
/// Callback invoked with the expected targetHost and the peer's certificate.
/// If false is returned by this callback then it is treated as a
/// verification failure and the attempted connection will fail.
/// Invocation of the callback is blocking, so any
/// implementation should be light-weight.
/// Note that the callback can potentially be invoked multiple times,
/// concurrently from different threads (e.g. when multiple connections
/// are being created for the same credentials).
/// </summary>
/// <param name="context">The <see cref="T:Grpc.Core.VerifyPeerContext"/> associated with the callback</param>
/// <returns>true if verification succeeded, false otherwise.</returns>
/// Note: experimental API that can change or be removed without any prior notice.
public delegate bool VerifyPeerCallback(VerifyPeerContext context);
/// <summary>
/// Client-side SSL credentials.
/// </summary>
public sealed class SslCredentials : ChannelCredentials
{
readonly string? rootCertificates;
readonly KeyCertificatePair? keyCertificatePair;
readonly VerifyPeerCallback? verifyPeerCallback;
/// <summary>
/// Creates client-side SSL credentials loaded from
/// disk file pointed to by the GRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment variable.
/// If that fails, gets the roots certificates from a well known place on disk.
/// </summary>
public SslCredentials() : this(null, null, null)
{
}
/// <summary>
/// Creates client-side SSL credentials from
/// a string containing PEM encoded root certificates.
/// </summary>
public SslCredentials(string rootCertificates) : this(rootCertificates, null, null)
{
}
/// <summary>
/// Creates client-side SSL credentials.
/// </summary>
/// <param name="rootCertificates">string containing PEM encoded server root certificates.</param>
/// <param name="keyCertificatePair">a key certificate pair.</param>
public SslCredentials(string rootCertificates, KeyCertificatePair keyCertificatePair) :
this(rootCertificates, keyCertificatePair, null)
{
}
/// <summary>
/// Creates client-side SSL credentials.
/// </summary>
/// <param name="rootCertificates">string containing PEM encoded server root certificates.</param>
/// <param name="keyCertificatePair">a key certificate pair.</param>
/// <param name="verifyPeerCallback">a callback to verify peer's target name and certificate.</param>
/// Note: experimental API that can change or be removed without any prior notice.
public SslCredentials(string? rootCertificates, KeyCertificatePair? keyCertificatePair, VerifyPeerCallback? verifyPeerCallback)
{
this.rootCertificates = rootCertificates;
this.keyCertificatePair = keyCertificatePair;
this.verifyPeerCallback = verifyPeerCallback;
}
/// <summary>
/// PEM encoding of the server root certificates.
/// </summary>
public string? RootCertificates
{
get
{
return this.rootCertificates;
}
}
/// <summary>
/// Client side key and certificate pair.
/// If null, client will not use key and certificate pair.
/// </summary>
public KeyCertificatePair? KeyCertificatePair
{
get
{
return this.keyCertificatePair;
}
}
/// <summary>
/// Populates channel credentials configurator with this instance's configuration.
/// End users never need to invoke this method as it is part of internal implementation.
/// </summary>
public override void InternalPopulateConfiguration(ChannelCredentialsConfiguratorBase configurator, object state)
{
configurator.SetSslCredentials(state, rootCertificates, keyCertificatePair, verifyPeerCallback);
}
internal override bool IsComposable => true;
}
}

@ -1,95 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
namespace Grpc.Core
{
/// <summary>
/// Represents RPC result, which consists of <see cref="StatusCode"/> and an optional detail string.
/// </summary>
public struct Status
{
/// <summary>
/// Default result of a successful RPC. StatusCode=OK, empty details message.
/// </summary>
public static readonly Status DefaultSuccess = new Status(StatusCode.OK, "");
/// <summary>
/// Default result of a cancelled RPC. StatusCode=Cancelled, empty details message.
/// </summary>
public static readonly Status DefaultCancelled = new Status(StatusCode.Cancelled, "");
/// <summary>
/// Creates a new instance of <c>Status</c>.
/// </summary>
/// <param name="statusCode">Status code.</param>
/// <param name="detail">Detail.</param>
public Status(StatusCode statusCode, string detail) : this(statusCode, detail, null)
{
}
/// <summary>
/// Creates a new instance of <c>Status</c>.
/// Users should not use this constructor, except for creating instances for testing.
/// The debug error string should only be populated by gRPC internals.
/// Note: experimental API that can change or be removed without any prior notice.
/// </summary>
/// <param name="statusCode">Status code.</param>
/// <param name="detail">Detail.</param>
/// <param name="debugException">Optional internal error details.</param>
public Status(StatusCode statusCode, string detail, Exception? debugException)
{
StatusCode = statusCode;
Detail = detail;
DebugException = debugException;
}
/// <summary>
/// Gets the gRPC status code. OK indicates success, all other values indicate an error.
/// </summary>
public StatusCode StatusCode { get; }
/// <summary>
/// Gets the detail.
/// </summary>
public string Detail { get; }
/// <summary>
/// In case of an error, this field may contain additional error details to help with debugging.
/// This field will be only populated on a client and its value is generated locally,
/// based on the internal state of the gRPC client stack (i.e. the value is never sent over the wire).
/// Note that this field is available only for debugging purposes, the application logic should
/// never rely on values of this field (it should use <c>StatusCode</c> and <c>Detail</c> instead).
/// Example: when a client fails to connect to a server, this field may provide additional details
/// why the connection to the server has failed.
/// Note: experimental API that can change or be removed without any prior notice.
/// </summary>
public Exception? DebugException { get; }
/// <summary>
/// Returns a <see cref="System.String"/> that represents the current <see cref="Grpc.Core.Status"/>.
/// </summary>
public override string ToString()
{
if (DebugException != null)
{
return $"Status(StatusCode=\"{StatusCode}\", Detail=\"{Detail}\", DebugException=\"{DebugException}\")";
}
return $"Status(StatusCode=\"{StatusCode}\", Detail=\"{Detail}\")";
}
}
}

@ -1,125 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
namespace Grpc.Core
{
/// <summary>
/// Result of a remote procedure call.
/// Based on grpc_status_code from grpc/status.h
/// </summary>
public enum StatusCode
{
/// <summary>Not an error; returned on success.</summary>
OK = 0,
/// <summary>The operation was cancelled (typically by the caller).</summary>
Cancelled = 1,
/// <summary>
/// Unknown error. An example of where this error may be returned is
/// if a Status value received from another address space belongs to
/// an error-space that is not known in this address space. Also
/// errors raised by APIs that do not return enough error information
/// may be converted to this error.
/// </summary>
Unknown = 2,
/// <summary>
/// Client specified an invalid argument. Note that this differs
/// from FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments
/// that are problematic regardless of the state of the system
/// (e.g., a malformed file name).
/// </summary>
InvalidArgument = 3,
/// <summary>
/// Deadline expired before operation could complete. For operations
/// that change the state of the system, this error may be returned
/// even if the operation has completed successfully. For example, a
/// successful response from a server could have been delayed long
/// enough for the deadline to expire.
/// </summary>
DeadlineExceeded = 4,
/// <summary>Some requested entity (e.g., file or directory) was not found.</summary>
NotFound = 5,
/// <summary>Some entity that we attempted to create (e.g., file or directory) already exists.</summary>
AlreadyExists = 6,
/// <summary>
/// The caller does not have permission to execute the specified
/// operation. PERMISSION_DENIED must not be used for rejections
/// caused by exhausting some resource (use RESOURCE_EXHAUSTED
/// instead for those errors). PERMISSION_DENIED must not be
/// used if the caller can not be identified (use UNAUTHENTICATED
/// instead for those errors).
/// </summary>
PermissionDenied = 7,
/// <summary>The request does not have valid authentication credentials for the operation.</summary>
Unauthenticated = 16,
/// <summary>
/// Some resource has been exhausted, perhaps a per-user quota, or
/// perhaps the entire file system is out of space.
/// </summary>
ResourceExhausted = 8,
/// <summary>
/// Operation was rejected because the system is not in a state
/// required for the operation's execution. For example, directory
/// to be deleted may be non-empty, an rmdir operation is applied to
/// a non-directory, etc.
/// </summary>
FailedPrecondition = 9,
/// <summary>
/// The operation was aborted, typically due to a concurrency issue
/// like sequencer check failures, transaction aborts, etc.
/// </summary>
Aborted = 10,
/// <summary>
/// Operation was attempted past the valid range. E.g., seeking or
/// reading past end of file.
/// </summary>
OutOfRange = 11,
/// <summary>Operation is not implemented or not supported/enabled in this service.</summary>
Unimplemented = 12,
/// <summary>
/// Internal errors. Means some invariants expected by underlying
/// system has been broken. If you see one of these errors,
/// something is very broken.
/// </summary>
Internal = 13,
/// <summary>
/// The service is currently unavailable. This is a most likely a
/// transient condition and may be corrected by retrying with
/// a backoff. Note that it is not always safe to retry
/// non-idempotent operations.
/// </summary>
Unavailable = 14,
/// <summary>Unrecoverable data loss or corruption.</summary>
DataLoss = 15
}
}

@ -1,54 +0,0 @@
#region Copyright notice and license
// Copyright 2019 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Runtime.CompilerServices;
using System.Text;
namespace Grpc.Core.Api.Utils
{
internal static class EncodingExtensions
{
#if NET45 // back-fill over a method missing in NET45
/// <summary>
/// Converts <c>byte*</c> pointing to an encoded byte array to a <c>string</c> using the provided <c>Encoding</c>.
/// </summary>
public static unsafe string GetString(this Encoding encoding, byte* source, int byteCount)
{
if (byteCount == 0) return ""; // most callers will have already checked, but: make sure
// allocate a right-sized string and decode into it
int charCount = encoding.GetCharCount(source, byteCount);
string s = new string('\0', charCount);
fixed (char* cPtr = s)
{
encoding.GetChars(source, byteCount, cPtr, charCount);
}
return s;
}
#endif
/// <summary>
/// Converts <c>IntPtr</c> pointing to a encoded byte array to a <c>string</c> using the provided <c>Encoding</c>.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe string GetString(this Encoding encoding, IntPtr ptr, int len)
{
return len == 0 ? "" : encoding.GetString((byte*)ptr.ToPointer(), len);
}
}
}

@ -1,105 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
namespace Grpc.Core.Utils
{
/// <summary>
/// Utility methods to simplify checking preconditions in the code.
/// </summary>
public static class GrpcPreconditions
{
/// <summary>
/// Throws <see cref="ArgumentException"/> if condition is false.
/// </summary>
/// <param name="condition">The condition.</param>
public static void CheckArgument(bool condition)
{
if (!condition)
{
throw new ArgumentException();
}
}
/// <summary>
/// Throws <see cref="ArgumentException"/> with given message if condition is false.
/// </summary>
/// <param name="condition">The condition.</param>
/// <param name="errorMessage">The error message.</param>
public static void CheckArgument(bool condition, string errorMessage)
{
if (!condition)
{
throw new ArgumentException(errorMessage);
}
}
/// <summary>
/// Throws <see cref="ArgumentNullException"/> if reference is null.
/// </summary>
/// <param name="reference">The reference.</param>
public static T CheckNotNull<T>(T reference)
{
if (reference == null)
{
throw new ArgumentNullException();
}
return reference;
}
/// <summary>
/// Throws <see cref="ArgumentNullException"/> if reference is null.
/// </summary>
/// <param name="reference">The reference.</param>
/// <param name="paramName">The parameter name.</param>
public static T CheckNotNull<T>(T reference, string paramName)
{
if (reference == null)
{
throw new ArgumentNullException(paramName);
}
return reference;
}
/// <summary>
/// Throws <see cref="InvalidOperationException"/> if condition is false.
/// </summary>
/// <param name="condition">The condition.</param>
public static void CheckState(bool condition)
{
if (!condition)
{
throw new InvalidOperationException();
}
}
/// <summary>
/// Throws <see cref="InvalidOperationException"/> with given message if condition is false.
/// </summary>
/// <param name="condition">The condition.</param>
/// <param name="errorMessage">The error message.</param>
public static void CheckState(bool condition, string errorMessage)
{
if (!condition)
{
throw new InvalidOperationException(errorMessage);
}
}
}
}

@ -1,48 +0,0 @@
#region Copyright notice and license
// Copyright 2019 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
namespace Grpc.Core
{
/// <summary>
/// Verification context for VerifyPeerCallback.
/// Note: experimental API that can change or be removed without any prior notice.
/// </summary>
public class VerifyPeerContext
{
/// <summary>
/// Initializes a new instance of the <see cref="T:Grpc.Core.VerifyPeerContext"/> class.
/// </summary>
/// <param name="targetName">The target name of the peer.</param>
/// <param name="peerPem">The PEM encoded certificate of the peer.</param>
internal VerifyPeerContext(string targetName, string peerPem)
{
this.TargetName = targetName;
this.PeerPem = peerPem;
}
/// <summary>
/// The target name of the peer.
/// </summary>
public string TargetName { get; }
/// <summary>
/// The PEM encoded certificate of the peer.
/// </summary>
public string PeerPem { get; }
}
}

@ -1,23 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System.Reflection;
[assembly: AssemblyVersion(Grpc.Core.VersionInfo.CurrentAssemblyVersion)]
[assembly: AssemblyFileVersion(Grpc.Core.VersionInfo.CurrentAssemblyFileVersion)]
[assembly: AssemblyInformationalVersion(Grpc.Core.VersionInfo.CurrentVersion)]

@ -1,43 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
namespace Grpc.Core
{
/// <summary>
/// Provides info about current version of gRPC.
/// See https://codingforsmarties.wordpress.com/2016/01/21/how-to-version-assemblies-destined-for-nuget/
/// for rationale about assembly versioning.
/// </summary>
public static class VersionInfo
{
/// <summary>
/// Current <c>AssemblyVersion</c> attribute of gRPC C# assemblies
/// </summary>
public const string CurrentAssemblyVersion = "2.0.0.0";
/// <summary>
/// Current <c>AssemblyFileVersion</c> of gRPC C# assemblies
/// </summary>
public const string CurrentAssemblyFileVersion = "2.47.0.0";
/// <summary>
/// Current version of gRPC C#
/// </summary>
public const string CurrentVersion = "2.47.0-dev";
}
}

@ -1,74 +0,0 @@
#region Copyright notice and license
// Copyright 2015-2016 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
namespace Grpc.Core
{
/// <summary>
/// Flags for write operations.
/// </summary>
[Flags]
public enum WriteFlags
{
/// <summary>
/// Hint that the write may be buffered and need not go out on the wire immediately.
/// gRPC is free to buffer the message until the next non-buffered
/// write, or until write stream completion, but it need not buffer completely or at all.
/// </summary>
BufferHint = 0x1,
/// <summary>
/// Force compression to be disabled for a particular write.
/// </summary>
NoCompress = 0x2
}
/// <summary>
/// Options for write operations.
/// </summary>
public class WriteOptions
{
/// <summary>
/// Default write options.
/// </summary>
public static readonly WriteOptions Default = new WriteOptions();
private readonly WriteFlags flags;
/// <summary>
/// Initializes a new instance of <c>WriteOptions</c> class.
/// </summary>
/// <param name="flags">The write flags.</param>
public WriteOptions(WriteFlags flags = default(WriteFlags))
{
this.flags = flags;
}
/// <summary>
/// Gets the write flags.
/// </summary>
public WriteFlags Flags
{
get
{
return this.flags;
}
}
}
}

@ -1,53 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Authors>The gRPC Authors</Authors>
<Copyright>Copyright 2015 The gRPC Authors</Copyright>
<Description>Debug symbols for the grpc_csharp_ext native library contained in Grpc.Core
Note that the Grpc.Core implementation of gRPC for C# is in maintenance mode and will be replaced by grpc-dotnet in the future.
See https://grpc.io/blog/grpc-csharp-future/ for details.</Description>
<PackageIcon>packageIcon.png</PackageIcon>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/grpc/grpc</PackageProjectUrl>
<PackageTags>gRPC RPC HTTP/2</PackageTags>
<VersionPrefix>$(GrpcCsharpVersion)</VersionPrefix>
</PropertyGroup>
<PropertyGroup>
<TargetFrameworks>net45;netstandard1.5;netstandard2.0</TargetFrameworks>
<!-- This package only carries native debug symbols -->
<IncludeBuildOutput>false</IncludeBuildOutput>
<DisableImplicitFrameworkReferences>true</DisableImplicitFrameworkReferences>
</PropertyGroup>
<ItemGroup>
<None Include="../packageIcon.png" Pack="true" PackagePath="\"/>
</ItemGroup>
<ItemGroup>
<None Include="..\nativelibs\csharp_ext_linux_x64\libgrpc_csharp_ext.dbginfo.so">
<PackagePath>runtimes/linux-x64/native/libgrpc_csharp_ext.x64.dbginfo.so</PackagePath>
<Pack>true</Pack>
</None>
<None Include="..\nativelibs\csharp_ext_linux_aarch64\libgrpc_csharp_ext.dbginfo.so">
<PackagePath>runtimes/linux-arm64/native/libgrpc_csharp_ext.arm64.dbginfo.so</PackagePath>
<Pack>true</Pack>
</None>
<None Include="..\nativelibs\csharp_ext_windows_x86\grpc_csharp_ext.dll">
<PackagePath>runtimes/win-x86/native/grpc_csharp_ext.x86.dll</PackagePath>
<Pack>true</Pack>
</None>
<None Include="..\nativelibs\csharp_ext_windows_x86\grpc_csharp_ext.pdb">
<PackagePath>runtimes/win-x86/native/grpc_csharp_ext.x86.pdb</PackagePath>
<Pack>true</Pack>
</None>
<None Include="..\nativelibs\csharp_ext_windows_x64\grpc_csharp_ext.dll">
<PackagePath>runtimes/win-x64/native/grpc_csharp_ext.x64.dll</PackagePath>
<Pack>true</Pack>
</None>
<None Include="..\nativelibs\csharp_ext_windows_x64\grpc_csharp_ext.pdb">
<PackagePath>runtimes/win-x64/native/grpc_csharp_ext.x64.pdb</PackagePath>
<Pack>true</Pack>
</None>
</ItemGroup>
</Project>

@ -1,43 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Authors>The gRPC Authors</Authors>
<Description>Miscellaneous code for testing Grpc.Core
Note that the Grpc.Core implementation of gRPC for C# is in maintenance mode and will be replaced by grpc-dotnet in the future.
See https://grpc.io/blog/grpc-csharp-future/ for details.</Description>
<Copyright>Copyright 2017 The gRPC Authors</Copyright>
<PackageIcon>packageIcon.png</PackageIcon>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/grpc/grpc</PackageProjectUrl>
<PackageTags>gRPC test testing</PackageTags>
<VersionPrefix>$(GrpcCsharpVersion)</VersionPrefix>
</PropertyGroup>
<PropertyGroup>
<TargetFrameworks>net45;netstandard1.5;netstandard2.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<Import Project="..\Grpc.Core\SourceLink.csproj.include" />
<ItemGroup>
<None Include="../packageIcon.png" Pack="true" PackagePath="\"/>
</ItemGroup>
<ItemGroup>
<Compile Include="..\Grpc.Core.Api\Version.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../Grpc.Core/Grpc.Core.csproj">
<PrivateAssets>None</PrivateAssets>
</ProjectReference>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
<Reference Include="System" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
</Project>

@ -1,29 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System.Reflection;
using System.Runtime.CompilerServices;
[assembly: AssemblyTitle("Grpc.Core.Testing")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("Google Inc. All rights reserved.")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

@ -1,10 +0,0 @@
<StyleCopSettings Version="105">
<SourceFileList>
<SourceFile>Health.cs</SourceFile>
<Settings>
<GlobalSettings>
<BooleanProperty Name="RulesEnabledByDefault">False</BooleanProperty>
</GlobalSettings>
</Settings>
</SourceFileList>
</StyleCopSettings>

@ -1,76 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Threading.Tasks;
using Grpc.Core;
namespace Grpc.Core.Testing
{
/// <summary>
/// Test doubles for client-side call objects.
/// </summary>
public static class TestCalls
{
/// <summary>
/// Creates a test double for <c>AsyncUnaryCall</c>. Only for testing.
/// Note: experimental API that can change or be removed without any prior notice.
/// </summary>
public static AsyncUnaryCall<TResponse> AsyncUnaryCall<TResponse> (
Task<TResponse> responseAsync, Task<Metadata> responseHeadersAsync, Func<Status> getStatusFunc,
Func<Metadata> getTrailersFunc, Action disposeAction)
{
return new AsyncUnaryCall<TResponse>(responseAsync, responseHeadersAsync, getStatusFunc, getTrailersFunc, disposeAction);
}
/// <summary>
/// Creates a test double for <c>AsyncClientStreamingCall</c>. Only for testing.
/// Note: experimental API that can change or be removed without any prior notice.
/// </summary>
public static AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(
IClientStreamWriter<TRequest> requestStream, Task<TResponse> responseAsync,
Task<Metadata> responseHeadersAsync, Func<Status> getStatusFunc,
Func<Metadata> getTrailersFunc, Action disposeAction)
{
return new AsyncClientStreamingCall<TRequest, TResponse>(requestStream, responseAsync, responseHeadersAsync, getStatusFunc, getTrailersFunc, disposeAction);
}
/// <summary>
/// Creates a test double for <c>AsyncServerStreamingCall</c>. Only for testing.
/// Note: experimental API that can change or be removed without any prior notice.
/// </summary>
public static AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TResponse>(
IAsyncStreamReader<TResponse> responseStream, Task<Metadata> responseHeadersAsync,
Func<Status> getStatusFunc, Func<Metadata> getTrailersFunc, Action disposeAction)
{
return new AsyncServerStreamingCall<TResponse>(responseStream, responseHeadersAsync, getStatusFunc, getTrailersFunc, disposeAction);
}
/// <summary>
/// Creates a test double for <c>AsyncDuplexStreamingCall</c>. Only for testing.
/// Note: experimental API that can change or be removed without any prior notice.
/// </summary>
public static AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(
IClientStreamWriter<TRequest> requestStream, IAsyncStreamReader<TResponse> responseStream,
Task<Metadata> responseHeadersAsync, Func<Status> getStatusFunc,
Func<Metadata> getTrailersFunc, Action disposeAction)
{
return new AsyncDuplexStreamingCall<TRequest, TResponse>(requestStream, responseStream, responseHeadersAsync, getStatusFunc, getTrailersFunc, disposeAction);
}
}
}

@ -1,107 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Grpc.Core.Testing
{
/// <summary>
/// Creates test doubles for <c>ServerCallContext</c>.
/// </summary>
public static class TestServerCallContext
{
/// <summary>
/// Creates a test double for <c>ServerCallContext</c>. Only for testing.
/// Note: experimental API that can change or be removed without any prior notice.
/// </summary>
public static ServerCallContext Create(string method, string host, DateTime deadline, Metadata requestHeaders, CancellationToken cancellationToken,
string peer, AuthContext authContext, ContextPropagationToken contextPropagationToken,
Func<Metadata, Task> writeHeadersFunc, Func<WriteOptions> writeOptionsGetter, Action<WriteOptions> writeOptionsSetter)
{
return new TestingServerCallContext(method, host, deadline, requestHeaders, cancellationToken, peer,
authContext, contextPropagationToken, writeHeadersFunc, writeOptionsGetter, writeOptionsSetter);
}
private class TestingServerCallContext : ServerCallContext
{
private readonly string method;
private readonly string host;
private readonly DateTime deadline;
private readonly Metadata requestHeaders;
private readonly CancellationToken cancellationToken;
private readonly Metadata responseTrailers = new Metadata();
private Status status;
private readonly string peer;
private readonly AuthContext authContext;
private readonly ContextPropagationToken contextPropagationToken;
private readonly Func<Metadata, Task> writeHeadersFunc;
private readonly Func<WriteOptions> writeOptionsGetter;
private readonly Action<WriteOptions> writeOptionsSetter;
public TestingServerCallContext(string method, string host, DateTime deadline, Metadata requestHeaders, CancellationToken cancellationToken,
string peer, AuthContext authContext, ContextPropagationToken contextPropagationToken,
Func<Metadata, Task> writeHeadersFunc, Func<WriteOptions> writeOptionsGetter, Action<WriteOptions> writeOptionsSetter)
{
this.method = method;
this.host = host;
this.deadline = deadline;
this.requestHeaders = requestHeaders;
this.cancellationToken = cancellationToken;
this.responseTrailers = new Metadata();
this.status = Status.DefaultSuccess;
this.peer = peer;
this.authContext = authContext;
this.contextPropagationToken = contextPropagationToken;
this.writeHeadersFunc = writeHeadersFunc;
this.writeOptionsGetter = writeOptionsGetter;
this.writeOptionsSetter = writeOptionsSetter;
}
protected override string MethodCore => method;
protected override string HostCore => host;
protected override string PeerCore => peer;
protected override DateTime DeadlineCore => deadline;
protected override Metadata RequestHeadersCore => requestHeaders;
protected override CancellationToken CancellationTokenCore => cancellationToken;
protected override Metadata ResponseTrailersCore => responseTrailers;
protected override Status StatusCore { get => status; set => status = value; }
protected override WriteOptions WriteOptionsCore { get => writeOptionsGetter(); set => writeOptionsSetter(value); }
protected override AuthContext AuthContextCore => authContext;
protected override ContextPropagationToken CreatePropagationTokenCore(ContextPropagationOptions options)
{
return contextPropagationToken;
}
protected override Task WriteResponseHeadersAsyncCore(Metadata responseHeaders)
{
return writeHeadersFunc(responseHeaders);
}
}
}
}

@ -1,3 +0,0 @@
test-results
bin
obj

@ -1,77 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Threading.Tasks;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Tests
{
public class AppDomainUnloadTest
{
#if NETCOREAPP
[Test]
[Ignore("Not supported for CoreCLR")]
public void AppDomainUnloadHookCanCleanupAbandonedCall()
{
}
#else
[Test]
public void AppDomainUnloadHookCanCleanupAbandonedCall()
{
var setup = new AppDomainSetup
{
ApplicationBase = AppDomain.CurrentDomain.BaseDirectory
};
var childDomain = AppDomain.CreateDomain("test", null, setup);
var remoteObj = childDomain.CreateInstance(typeof(AppDomainTestClass).Assembly.GetName().Name, typeof(AppDomainTestClass).FullName);
// Try to unload the appdomain once we've created a server and a channel inside the appdomain.
AppDomain.Unload(childDomain);
}
public class AppDomainTestClass
{
const string Host = "127.0.0.1";
/// <summary>
/// Creates a server and a channel and initiates a call. The code is invoked from inside of an AppDomain
/// to test if AppDomain.Unload() work if Grpc is being used.
/// </summary>
public AppDomainTestClass()
{
var helper = new MockServiceHelper(Host);
var readyToShutdown = new TaskCompletionSource<object>();
helper.DuplexStreamingHandler = new DuplexStreamingServerMethod<string, string>(async (requestStream, responseStream, context) =>
{
readyToShutdown.SetResult(null);
await requestStream.ToListAsync();
});
var server = helper.GetServer();
server.Start();
var channel = helper.GetChannel();
var call = Calls.AsyncDuplexStreamingCall(helper.CreateDuplexStreamingCall());
readyToShutdown.Task.Wait(); // make sure handler is running
}
}
#endif
}
}

@ -1,71 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Collections.Generic;
using NUnit.Framework;
using Grpc.Core;
using System.Linq;
namespace Grpc.Core.Tests
{
public class AuthContextTest
{
[Test]
public void EmptyContext()
{
var context = new AuthContext(null, new Dictionary<string, List<AuthProperty>>());
Assert.IsFalse(context.IsPeerAuthenticated);
Assert.IsNull(context.PeerIdentityPropertyName);
Assert.AreEqual(0, context.PeerIdentity.Count());
Assert.AreEqual(0, context.Properties.Count());
Assert.AreEqual(0, context.FindPropertiesByName("nonexistent").Count());
}
[Test]
public void AuthenticatedContext()
{
var property1 = AuthProperty.Create("abc", new byte[] { 68, 69, 70 });
var context = new AuthContext("some_identity", new Dictionary<string, List<AuthProperty>>
{
{"some_identity", new List<AuthProperty> {property1}}
});
Assert.IsTrue(context.IsPeerAuthenticated);
Assert.AreEqual("some_identity", context.PeerIdentityPropertyName);
Assert.AreEqual(1, context.PeerIdentity.Count());
}
[Test]
public void FindPropertiesByName()
{
var property1 = AuthProperty.Create("abc", new byte[] {68, 69, 70});
var property2 = AuthProperty.Create("abc", new byte[] {71, 72, 73 });
var property3 = AuthProperty.Create("abc", new byte[] {});
var context = new AuthContext(null, new Dictionary<string, List<AuthProperty>>
{
{"existent", new List<AuthProperty> {property1, property2}},
{"foobar", new List<AuthProperty> {property3}},
});
Assert.AreEqual(3, context.Properties.Count());
Assert.AreEqual(0, context.FindPropertiesByName("nonexistent").Count());
var existentProperties = new List<AuthProperty>(context.FindPropertiesByName("existent"));
Assert.AreEqual(2, existentProperties.Count);
}
}
}

@ -1,67 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using NUnit.Framework;
namespace Grpc.Core.Tests
{
public class AuthPropertyTest
{
[Test]
public void Create_NameIsNotNull()
{
Assert.Throws(typeof(ArgumentNullException), () => AuthProperty.Create(null, new byte[0]));
Assert.Throws(typeof(ArgumentNullException), () => AuthProperty.CreateUnsafe(null, new byte[0]));
}
[Test]
public void Create_ValueIsNotNull()
{
Assert.Throws(typeof(ArgumentNullException), () => AuthProperty.Create("abc", null));
Assert.Throws(typeof(ArgumentNullException), () => AuthProperty.CreateUnsafe("abc", null));
}
[Test]
public void Create()
{
var valueBytes = new byte[] { 68, 69, 70 };
var authProperty = AuthProperty.Create("abc", valueBytes);
Assert.AreEqual("abc", authProperty.Name);
Assert.AreNotSame(valueBytes, authProperty.ValueBytesUnsafe);
CollectionAssert.AreEqual(valueBytes, authProperty.ValueBytes);
CollectionAssert.AreEqual(valueBytes, authProperty.ValueBytesUnsafe);
Assert.AreEqual("DEF", authProperty.Value);
}
[Test]
public void CreateUnsafe()
{
var valueBytes = new byte[] { 68, 69, 70 };
var authProperty = AuthProperty.CreateUnsafe("abc", valueBytes);
Assert.AreEqual("abc", authProperty.Name);
Assert.AreSame(valueBytes, authProperty.ValueBytesUnsafe);
Assert.AreNotSame(valueBytes, authProperty.ValueBytes);
CollectionAssert.AreEqual(valueBytes, authProperty.ValueBytes);
CollectionAssert.AreEqual(valueBytes, authProperty.ValueBytesUnsafe);
Assert.AreEqual("DEF", authProperty.Value);
}
}
}

@ -1,48 +0,0 @@
#region Copyright notice and license
// Copyright 2020 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using NUnit.Framework;
namespace Grpc.Core.Tests
{
public class CallAfterShutdownTest
{
Method<string, string> dummyUnaryMethod = new Method<string, string>(MethodType.Unary, "fooservice", "dummyMethod", Marshallers.StringMarshaller, Marshallers.StringMarshaller);
[Test]
public void StartBlockingUnaryCallAfterChannelShutdown()
{
// create a channel and immediately shut it down.
var channel = new Channel("127.0.0.1", 1000, ChannelCredentials.Insecure);
channel.ShutdownAsync().Wait(); // also shuts down GrpcEnvironment
Assert.Throws(typeof(ObjectDisposedException), () => Calls.BlockingUnaryCall(new CallInvocationDetails<string, string>(channel, dummyUnaryMethod, new CallOptions()), "THE REQUEST"));
}
[Test]
public void StartAsyncUnaryCallAfterChannelShutdown()
{
// create a channel and immediately shut it down.
var channel = new Channel("127.0.0.1", 1000, ChannelCredentials.Insecure);
channel.ShutdownAsync().Wait(); // also shuts down GrpcEnvironment
Assert.Throws(typeof(ObjectDisposedException), () => Calls.AsyncUnaryCall(new CallInvocationDetails<string, string>(channel, dummyUnaryMethod, new CallOptions()), "THE REQUEST"));
}
}
}

@ -1,193 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Profiling;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Tests
{
public class CallCancellationTest
{
const string Host = "127.0.0.1";
MockServiceHelper helper;
Server server;
Channel channel;
[SetUp]
public void Init()
{
helper = new MockServiceHelper(Host);
server = helper.GetServer();
server.Start();
channel = helper.GetChannel();
}
[TearDown]
public void Cleanup()
{
channel.ShutdownAsync().Wait();
server.ShutdownAsync().Wait();
}
[Test]
public async Task ClientStreamingCall_CancelAfterBegin()
{
var barrier = new TaskCompletionSource<object>();
helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) =>
{
barrier.SetResult(null);
await requestStream.ToListAsync();
return "";
});
var cts = new CancellationTokenSource();
var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall(new CallOptions(cancellationToken: cts.Token)));
await barrier.Task; // make sure the handler has started.
cts.Cancel();
try
{
// cannot use Assert.ThrowsAsync because it uses Task.Wait and would deadlock.
await call.ResponseAsync;
Assert.Fail();
}
catch (RpcException ex)
{
Assert.AreEqual(StatusCode.Cancelled, ex.Status.StatusCode);
}
}
[Test]
public async Task ClientStreamingCall_ServerSideReadAfterCancelNotificationReturnsNull()
{
var handlerStartedBarrier = new TaskCompletionSource<object>();
var cancelNotificationReceivedBarrier = new TaskCompletionSource<object>();
var successTcs = new TaskCompletionSource<string>();
helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) =>
{
handlerStartedBarrier.SetResult(null);
// wait for cancellation to be delivered.
context.CancellationToken.Register(() => cancelNotificationReceivedBarrier.SetResult(null));
await cancelNotificationReceivedBarrier.Task;
var moveNextResult = await requestStream.MoveNext();
successTcs.SetResult(!moveNextResult ? "SUCCESS" : "FAIL");
return "";
});
var cts = new CancellationTokenSource();
var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall(new CallOptions(cancellationToken: cts.Token)));
await handlerStartedBarrier.Task;
cts.Cancel();
try
{
await call.ResponseAsync;
Assert.Fail();
}
catch (RpcException ex)
{
Assert.AreEqual(StatusCode.Cancelled, ex.Status.StatusCode);
}
Assert.AreEqual("SUCCESS", await successTcs.Task);
}
[Test]
public async Task ClientStreamingCall_CancelServerSideRead()
{
helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) =>
{
var cts = new CancellationTokenSource();
var moveNextTask = requestStream.MoveNext(cts.Token);
cts.Cancel();
await moveNextTask;
return "";
});
var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall());
try
{
// cannot use Assert.ThrowsAsync because it uses Task.Wait and would deadlock.
await call.ResponseAsync;
Assert.Fail();
}
catch (RpcException ex)
{
Assert.AreEqual(StatusCode.Cancelled, ex.Status.StatusCode);
}
}
[Test]
public async Task ServerStreamingCall_CancelClientSideRead()
{
helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>(async (request, responseStream, context) =>
{
await responseStream.WriteAsync("abc");
while (!context.CancellationToken.IsCancellationRequested)
{
await Task.Delay(10);
}
});
var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), "");
await call.ResponseStream.MoveNext();
Assert.AreEqual("abc", call.ResponseStream.Current);
var cts = new CancellationTokenSource();
var moveNextTask = call.ResponseStream.MoveNext(cts.Token);
cts.Cancel();
try
{
// cannot use Assert.ThrowsAsync because it uses Task.Wait and would deadlock.
await moveNextTask;
Assert.Fail();
}
catch (RpcException ex)
{
Assert.AreEqual(StatusCode.Cancelled, ex.Status.StatusCode);
}
}
[Test]
public void CanDisposeDefaultCancellationRegistration()
{
// prove that we're fine to dispose default CancellationTokenRegistration
// values without boxing them to IDisposable for a null-check
var obj = default(CancellationTokenRegistration);
obj.Dispose();
using (obj) {}
}
}
}

@ -1,50 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Tests
{
public class CallCredentialsTest
{
[Test]
public void CallCredentials_ComposeAtLeastTwo()
{
Assert.Throws(typeof(ArgumentException), () => CallCredentials.Compose(new FakeCallCredentials()));
}
[Test]
public void CallCredentials_ToNativeCredentials()
{
var composite = CallCredentials.Compose(
CallCredentials.FromInterceptor(async (uri, m) => { await Task.Delay(1); }),
CallCredentials.FromInterceptor(async (uri, m) => { await Task.Delay(2); }));
using (var nativeComposite = composite.ToNativeCredentials())
{
}
}
}
}

@ -1,98 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Collections.Generic;
using System.Threading;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Tests
{
public class CallOptionsTest
{
[Test]
public void WithMethods()
{
var options = new CallOptions();
var metadata = new Metadata();
Assert.AreSame(metadata, options.WithHeaders(metadata).Headers);
var deadline = DateTime.UtcNow;
Assert.AreEqual(deadline, options.WithDeadline(deadline).Deadline.Value);
var cancellationToken = new CancellationTokenSource().Token;
Assert.AreEqual(cancellationToken, options.WithCancellationToken(cancellationToken).CancellationToken);
var writeOptions = new WriteOptions();
Assert.AreSame(writeOptions, options.WithWriteOptions(writeOptions).WriteOptions);
var propagationToken = new ContextPropagationTokenImpl(CallSafeHandle.NullInstance, DateTime.UtcNow,
CancellationToken.None, ContextPropagationOptions.Default);
Assert.AreSame(propagationToken, options.WithPropagationToken(propagationToken).PropagationToken);
var credentials = new FakeCallCredentials();
Assert.AreSame(credentials, options.WithCredentials(credentials).Credentials);
var flags = CallFlags.WaitForReady | CallFlags.CacheableRequest;
Assert.AreEqual(flags, options.WithFlags(flags).Flags);
// Check that the original instance is unchanged.
Assert.IsNull(options.Headers);
Assert.IsNull(options.Deadline);
Assert.AreEqual(CancellationToken.None, options.CancellationToken);
Assert.IsNull(options.WriteOptions);
Assert.IsNull(options.PropagationToken);
Assert.IsNull(options.Credentials);
Assert.AreEqual(default(CallFlags), options.Flags);
}
[Test]
public void Normalize()
{
Assert.AreSame(Metadata.Empty, new CallOptions().Normalize().Headers);
Assert.AreEqual(DateTime.MaxValue, new CallOptions().Normalize().Deadline.Value);
var deadline = DateTime.UtcNow;
var propagationToken1 = new ContextPropagationTokenImpl(CallSafeHandle.NullInstance, deadline, CancellationToken.None,
new ContextPropagationOptions(propagateDeadline: true, propagateCancellation: false));
Assert.AreEqual(deadline, new CallOptions(propagationToken: propagationToken1).Normalize().Deadline.Value);
Assert.Throws(typeof(ArgumentException), () => new CallOptions(deadline: deadline, propagationToken: propagationToken1).Normalize());
var token = new CancellationTokenSource().Token;
var propagationToken2 = new ContextPropagationTokenImpl(CallSafeHandle.NullInstance, deadline, token,
new ContextPropagationOptions(propagateDeadline: false, propagateCancellation: true));
Assert.AreEqual(token, new CallOptions(propagationToken: propagationToken2).Normalize().CancellationToken);
Assert.Throws(typeof(ArgumentException), () => new CallOptions(cancellationToken: token, propagationToken: propagationToken2).Normalize());
}
[Test]
public void WaitForReady()
{
var callOptions = new CallOptions();
Assert.IsFalse(callOptions.IsWaitForReady);
Assert.AreEqual(CallFlags.WaitForReady, callOptions.WithWaitForReady().Flags);
Assert.IsTrue(callOptions.WithWaitForReady().IsWaitForReady);
Assert.IsFalse(callOptions.WithWaitForReady(true).WithWaitForReady(false).IsWaitForReady);
}
}
}

@ -1,90 +0,0 @@
#region Copyright notice and license
// Copyright 2017 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Profiling;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Tests
{
public class ChannelConnectivityTest
{
const string Host = "127.0.0.1";
MockServiceHelper helper;
Server server;
Channel channel;
[SetUp]
public void Init()
{
helper = new MockServiceHelper(Host);
server = helper.GetServer();
server.Start();
channel = helper.GetChannel();
}
[TearDown]
public void Cleanup()
{
channel.ShutdownAsync().Wait();
server.ShutdownAsync().Wait();
}
[Test]
public async Task Channel_WaitForStateChangedAsync()
{
Assert.ThrowsAsync(typeof(TaskCanceledException),
async () => await channel.WaitForStateChangedAsync(channel.State, DateTime.UtcNow.AddMilliseconds(0)));
var stateChangedTask = channel.WaitForStateChangedAsync(channel.State);
await channel.ConnectAsync(DateTime.UtcNow.AddMilliseconds(5000));
await stateChangedTask;
Assert.AreEqual(ChannelState.Ready, channel.State);
}
[Test]
public async Task Channel_TryWaitForStateChangedAsync()
{
Assert.IsFalse(await channel.TryWaitForStateChangedAsync(channel.State, DateTime.UtcNow.AddMilliseconds(0)));
var stateChangedTask = channel.TryWaitForStateChangedAsync(channel.State);
await channel.ConnectAsync(DateTime.UtcNow.AddMilliseconds(5000));
Assert.IsTrue(await stateChangedTask);
Assert.AreEqual(ChannelState.Ready, channel.State);
}
[Test]
public async Task Channel_ConnectAsync()
{
await channel.ConnectAsync();
Assert.AreEqual(ChannelState.Ready, channel.State);
await channel.ConnectAsync(DateTime.UtcNow.AddMilliseconds(1000));
Assert.AreEqual(ChannelState.Ready, channel.State);
}
}
}

@ -1,67 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using Grpc.Core.Internal;
using NUnit.Framework;
namespace Grpc.Core.Tests
{
public class ChannelCredentialsTest
{
[Test]
public void InsecureCredentials_IsNonComposable()
{
Assert.IsFalse(ChannelCredentials.Insecure.IsComposable);
}
[Test]
public void SecureCredentials_IsComposable()
{
Assert.IsTrue(ChannelCredentials.SecureSsl.IsComposable);
}
[Test]
public void ChannelCredentials_CreateComposite()
{
var composite = ChannelCredentials.Create(new FakeChannelCredentials(true), new FakeCallCredentials());
Assert.IsFalse(composite.IsComposable);
Assert.Throws(typeof(ArgumentNullException), () => ChannelCredentials.Create(null, new FakeCallCredentials()));
Assert.Throws(typeof(ArgumentNullException), () => ChannelCredentials.Create(new FakeChannelCredentials(true), null));
// forbid composing non-composable
var ex = Assert.Throws(typeof(ArgumentException), () => ChannelCredentials.Create(new FakeChannelCredentials(false), new FakeCallCredentials()));
Assert.AreEqual("CallCredentials can't be composed with FakeChannelCredentials. CallCredentials must be used with secure channel credentials like SslCredentials.", ex.Message);
}
[Test]
public void ChannelCredentials_NativeCredentialsAreReused()
{
// always returning the same native object is critical for subchannel sharing to work with secure channels
var creds = new SslCredentials();
var nativeCreds1 = creds.ToNativeCredentials();
var nativeCreds2 = creds.ToNativeCredentials();
Assert.AreSame(nativeCreds1, nativeCreds2);
var nativeCreds3 = ChannelCredentials.SecureSsl.ToNativeCredentials();
var nativeCreds4 = ChannelCredentials.SecureSsl.ToNativeCredentials();
Assert.AreSame(nativeCreds3, nativeCreds4);
}
}
}

@ -1,90 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Collections.Generic;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Tests
{
public class ChannelOptionsTest
{
[Test]
public void IntOption()
{
var option = new ChannelOption("somename", 1);
Assert.AreEqual(ChannelOption.OptionType.Integer, option.Type);
Assert.AreEqual("somename", option.Name);
Assert.AreEqual(1, option.IntValue);
Assert.Throws(typeof(InvalidOperationException), () => { var s = option.StringValue; });
}
[Test]
public void StringOption()
{
var option = new ChannelOption("somename", "ABCDEF");
Assert.AreEqual(ChannelOption.OptionType.String, option.Type);
Assert.AreEqual("somename", option.Name);
Assert.AreEqual("ABCDEF", option.StringValue);
Assert.Throws(typeof(InvalidOperationException), () => { var s = option.IntValue; });
}
[Test]
public void ConstructorPreconditions()
{
Assert.Throws(typeof(ArgumentNullException), () => { new ChannelOption(null, "abc"); });
Assert.Throws(typeof(ArgumentNullException), () => { new ChannelOption(null, 1); });
Assert.Throws(typeof(ArgumentNullException), () => { new ChannelOption("abc", null); });
}
[Test]
public void CreateChannelArgsNull()
{
var channelArgs = ChannelOptions.CreateChannelArgs(null);
Assert.IsTrue(channelArgs.IsInvalid);
}
[Test]
public void CreateChannelArgsEmpty()
{
var options = new List<ChannelOption>();
var channelArgs = ChannelOptions.CreateChannelArgs(options);
channelArgs.Dispose();
}
[Test]
public void CreateChannelArgs()
{
var options = new List<ChannelOption>
{
new ChannelOption("ABC", "XYZ"),
new ChannelOption("somename", "IJKLM"),
new ChannelOption("intoption", 12345),
new ChannelOption("GHIJK", 12345),
};
var channelArgs = ChannelOptions.CreateChannelArgs(options);
channelArgs.Dispose();
}
}
}

@ -1,127 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Tests
{
public class ChannelTest
{
[Test]
public void Constructor_RejectsInvalidParams()
{
Assert.Throws(typeof(ArgumentNullException), () => new Channel(null, ChannelCredentials.Insecure));
}
[Test]
public void Constructor_RejectsDuplicateOptions()
{
var options = new ChannelOption[]
{
new ChannelOption(ChannelOptions.PrimaryUserAgentString, "ABC"),
new ChannelOption(ChannelOptions.PrimaryUserAgentString, "XYZ")
};
Assert.Throws(typeof(ArgumentException), () => new Channel("127.0.0.1", ChannelCredentials.Insecure, options));
}
[Test]
public void State_IdleAfterCreation()
{
var channel = new Channel("localhost", ChannelCredentials.Insecure);
Assert.AreEqual(ChannelState.Idle, channel.State);
channel.ShutdownAsync().Wait();
}
[Test]
public void WaitForStateChangedAsync_InvalidArgument()
{
var channel = new Channel("localhost", ChannelCredentials.Insecure);
Assert.ThrowsAsync(typeof(ArgumentException), async () => await channel.WaitForStateChangedAsync(ChannelState.Shutdown));
channel.ShutdownAsync().Wait();
}
[Test]
public void ResolvedTarget()
{
var channel = new Channel("127.0.0.1", ChannelCredentials.Insecure);
Assert.IsTrue(channel.ResolvedTarget.Contains("127.0.0.1"));
channel.ShutdownAsync().Wait();
}
[Test]
public void Shutdown_AllowedOnlyOnce()
{
var channel = new Channel("localhost", ChannelCredentials.Insecure);
channel.ShutdownAsync().Wait();
Assert.ThrowsAsync(typeof(InvalidOperationException), async () => await channel.ShutdownAsync());
}
[Test]
public async Task ShutdownTokenCancelledAfterShutdown()
{
var channel = new Channel("localhost", ChannelCredentials.Insecure);
Assert.IsFalse(channel.ShutdownToken.IsCancellationRequested);
var shutdownTask = channel.ShutdownAsync();
Assert.IsTrue(channel.ShutdownToken.IsCancellationRequested);
await shutdownTask;
}
[Test]
public async Task StateIsShutdownAfterShutdown()
{
var channel = new Channel("localhost", ChannelCredentials.Insecure);
await channel.ShutdownAsync();
Assert.AreEqual(ChannelState.Shutdown, channel.State);
}
[Test]
public async Task ShutdownFinishesWaitForStateChangedAsync()
{
var channel = new Channel("localhost", ChannelCredentials.Insecure);
var stateChangedTask = channel.WaitForStateChangedAsync(ChannelState.Idle);
var shutdownTask = channel.ShutdownAsync();
await stateChangedTask;
await shutdownTask;
}
[Test]
public async Task OperationsThrowAfterShutdown()
{
var channel = new Channel("localhost", ChannelCredentials.Insecure);
await channel.ShutdownAsync();
Assert.ThrowsAsync(typeof(ObjectDisposedException), async () => await channel.WaitForStateChangedAsync(ChannelState.Idle));
Assert.Throws(typeof(ObjectDisposedException), () => { var x = channel.ResolvedTarget; });
Assert.ThrowsAsync(typeof(TaskCanceledException), async () => await channel.ConnectAsync());
}
[Test]
public async Task ChannelBaseShutdownAsyncInvokesShutdownAsync()
{
var channel = new Channel("localhost", ChannelCredentials.Insecure);
ChannelBase channelBase = channel;
await channelBase.ShutdownAsync();
// check that Channel.ShutdownAsync has run
Assert.AreEqual(ChannelState.Shutdown, channel.State);
}
}
}

@ -1,410 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Profiling;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Tests
{
public class ClientServerTest
{
const string Host = "127.0.0.1";
MockServiceHelper helper;
Server server;
Channel channel;
[SetUp]
public void Init()
{
helper = new MockServiceHelper(Host);
server = helper.GetServer();
server.Start();
channel = helper.GetChannel();
}
[TearDown]
public void Cleanup()
{
channel.ShutdownAsync().Wait();
server.ShutdownAsync().Wait();
}
[Test]
public async Task UnaryCall()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
return Task.FromResult(request);
});
Assert.AreEqual("ABC", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "ABC"));
Assert.AreEqual("ABC", await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "ABC"));
Assert.AreEqual("ABC", await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "ABC").ConfigureAwait(false));
}
[Test]
public void UnaryCall_ServerHandlerThrows()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
throw new Exception("This was thrown on purpose by a test");
});
var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unknown, ex.Status.StatusCode);
var ex2 = Assert.ThrowsAsync<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unknown, ex2.Status.StatusCode);
}
[Test]
public void UnaryCall_ServerHandlerThrowsRpcException()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
throw new RpcException(new Status(StatusCode.Unauthenticated, ""));
});
var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode);
Assert.AreEqual(0, ex.Trailers.Count);
var ex2 = Assert.ThrowsAsync<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode);
Assert.AreEqual(0, ex.Trailers.Count);
}
[Test]
public void UnaryCall_ServerHandlerThrowsRpcExceptionWithTrailers()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
var trailers = new Metadata { {"xyz", "xyz-value"} };
throw new RpcException(new Status(StatusCode.Unauthenticated, ""), trailers);
});
var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode);
Assert.AreEqual(1, ex.Trailers.Count);
Assert.AreEqual("xyz", ex.Trailers[0].Key);
Assert.AreEqual("xyz-value", ex.Trailers[0].Value);
var ex2 = Assert.ThrowsAsync<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode);
Assert.AreEqual(1, ex2.Trailers.Count);
Assert.AreEqual("xyz", ex2.Trailers[0].Key);
Assert.AreEqual("xyz-value", ex2.Trailers[0].Value);
}
[Test]
public void UnaryCall_ServerHandlerSetsStatus()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
context.Status = new Status(StatusCode.Unauthenticated, "");
return Task.FromResult("");
});
var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode);
Assert.AreEqual(0, ex.Trailers.Count);
var ex2 = Assert.ThrowsAsync<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode);
Assert.AreEqual(0, ex2.Trailers.Count);
}
[Test]
public void UnaryCall_StatusDebugErrorStringNotTransmittedFromServer()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
context.Status = new Status(StatusCode.Unauthenticated, "", new CoreErrorDetailException("this DebugErrorString value should not be transmitted to the client"));
return Task.FromResult("");
});
var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode);
StringAssert.Contains("Error received from peer", ex.Status.DebugException.Message, "Is \"Error received from peer\" still a valid substring to search for in the client-generated error message from C-core?");
Assert.AreEqual(0, ex.Trailers.Count);
var ex2 = Assert.ThrowsAsync<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode);
StringAssert.Contains("Error received from peer", ex2.Status.DebugException.Message, "Is \"Error received from peer\" still a valid substring to search for in the client-generated error message from C-core?");
Assert.AreEqual(0, ex2.Trailers.Count);
}
[Test]
public void UnaryCall_ServerHandlerSetsStatusAndTrailers()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
context.Status = new Status(StatusCode.Unauthenticated, "");
context.ResponseTrailers.Add("xyz", "xyz-value");
return Task.FromResult("");
});
var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode);
Assert.AreEqual(1, ex.Trailers.Count);
Assert.AreEqual("xyz", ex.Trailers[0].Key);
Assert.AreEqual("xyz-value", ex.Trailers[0].Value);
var ex2 = Assert.ThrowsAsync<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode);
Assert.AreEqual(1, ex2.Trailers.Count);
Assert.AreEqual("xyz", ex2.Trailers[0].Key);
Assert.AreEqual("xyz-value", ex2.Trailers[0].Value);
}
[Test]
public async Task ClientStreamingCall()
{
helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) =>
{
string result = "";
await requestStream.ForEachAsync((request) =>
{
result += request;
return TaskUtils.CompletedTask;
});
await Task.Delay(100);
return result;
});
{
var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall());
await call.RequestStream.WriteAllAsync(new string[] { "A", "B", "C" });
Assert.AreEqual("ABC", await call);
Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode);
Assert.IsNotNull(call.GetTrailers());
}
{
var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall());
await call.RequestStream.WriteAllAsync(new string[] { "A", "B", "C" });
Assert.AreEqual("ABC", await call.ConfigureAwait(false));
Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode);
Assert.IsNotNull(call.GetTrailers());
}
}
[Test]
public async Task ServerStreamingCall()
{
helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>(async (request, responseStream, context) =>
{
await responseStream.WriteAllAsync(request.Split(new []{' '}));
context.ResponseTrailers.Add("xyz", "");
});
var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), "A B C");
CollectionAssert.AreEqual(new string[] { "A", "B", "C" }, await call.ResponseStream.ToListAsync());
Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode);
Assert.AreEqual("xyz", call.GetTrailers()[0].Key);
}
[Test]
public async Task ServerStreamingCall_EndOfStreamIsIdempotent()
{
helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>((request, responseStream, context) => TaskUtils.CompletedTask);
var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), "");
Assert.IsFalse(await call.ResponseStream.MoveNext());
Assert.IsFalse(await call.ResponseStream.MoveNext());
}
[Test]
public void ServerStreamingCall_ErrorCanBeAwaitedTwice()
{
helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>((request, responseStream, context) =>
{
context.Status = new Status(StatusCode.InvalidArgument, "");
return TaskUtils.CompletedTask;
});
var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), "");
var ex = Assert.ThrowsAsync<RpcException>(async () => await call.ResponseStream.MoveNext());
Assert.AreEqual(StatusCode.InvalidArgument, ex.Status.StatusCode);
// attempting MoveNext again should result in throwing the same exception.
var ex2 = Assert.ThrowsAsync<RpcException>(async () => await call.ResponseStream.MoveNext());
Assert.AreEqual(StatusCode.InvalidArgument, ex2.Status.StatusCode);
}
[Test]
public void ServerStreamingCall_TrailersFromMultipleSourcesGetConcatenated()
{
helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>((request, responseStream, context) =>
{
context.ResponseTrailers.Add("xyz", "xyz-value");
throw new RpcException(new Status(StatusCode.InvalidArgument, ""), new Metadata { {"abc", "abc-value"} });
});
var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), "");
var ex = Assert.ThrowsAsync<RpcException>(async () => await call.ResponseStream.MoveNext());
Assert.AreEqual(StatusCode.InvalidArgument, ex.Status.StatusCode);
Assert.AreEqual(2, call.GetTrailers().Count);
Assert.AreEqual(2, ex.Trailers.Count);
Assert.AreEqual("xyz", ex.Trailers[0].Key);
Assert.AreEqual("xyz-value", ex.Trailers[0].Value);
Assert.AreEqual("abc", ex.Trailers[1].Key);
Assert.AreEqual("abc-value", ex.Trailers[1].Value);
}
[Test]
public async Task DuplexStreamingCall()
{
helper.DuplexStreamingHandler = new DuplexStreamingServerMethod<string, string>(async (requestStream, responseStream, context) =>
{
while (await requestStream.MoveNext())
{
await responseStream.WriteAsync(requestStream.Current);
}
context.ResponseTrailers.Add("xyz", "xyz-value");
});
var call = Calls.AsyncDuplexStreamingCall(helper.CreateDuplexStreamingCall());
await call.RequestStream.WriteAllAsync(new string[] { "A", "B", "C" });
CollectionAssert.AreEqual(new string[] { "A", "B", "C" }, await call.ResponseStream.ToListAsync());
Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode);
Assert.AreEqual("xyz-value", call.GetTrailers()[0].Value);
}
[Test]
public async Task AsyncUnaryCall_EchoMetadata()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
foreach (Metadata.Entry metadataEntry in context.RequestHeaders)
{
if (metadataEntry.Key != "user-agent")
{
context.ResponseTrailers.Add(metadataEntry);
}
}
return Task.FromResult("");
});
var headers = new Metadata
{
{ "ascii-header", "abcdefg" },
{ "binary-header-bin", new byte[] { 1, 2, 3, 0, 0xff } }
};
var call = Calls.AsyncUnaryCall(helper.CreateUnaryCall(new CallOptions(headers: headers)), "ABC");
await call;
Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode);
var trailers = call.GetTrailers();
Assert.AreEqual(2, trailers.Count);
Assert.AreEqual(headers[0].Key, trailers[0].Key);
Assert.AreEqual(headers[0].Value, trailers[0].Value);
Assert.AreEqual(headers[1].Key, trailers[1].Key);
CollectionAssert.AreEqual(headers[1].ValueBytes, trailers[1].ValueBytes);
}
[Test]
public void UnknownMethodHandler()
{
var nonexistentMethod = new Method<string, string>(
MethodType.Unary,
MockServiceHelper.ServiceName,
"NonExistentMethod",
Marshallers.StringMarshaller,
Marshallers.StringMarshaller);
var callDetails = new CallInvocationDetails<string, string>(channel, nonexistentMethod, new CallOptions());
var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(callDetails, "abc"));
Assert.AreEqual(StatusCode.Unimplemented, ex.Status.StatusCode);
}
[Test]
public void StatusDetailIsUtf8()
{
// some japanese and chinese characters
var nonAsciiString = "\u30a1\u30a2\u30a3 \u62b5\u6297\u662f\u5f92\u52b3\u7684";
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
context.Status = new Status(StatusCode.Unknown, nonAsciiString);
return Task.FromResult("");
});
var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unknown, ex.Status.StatusCode);
Assert.AreEqual(nonAsciiString, ex.Status.Detail);
}
[Test]
public void ServerCallContext_PeerInfoPresent()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
return Task.FromResult(context.Peer);
});
string peer = Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc");
Assert.IsTrue(peer.Contains(Host));
}
[Test]
public void ServerCallContext_HostAndMethodPresent()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
Assert.IsTrue(context.Host.Contains(Host));
Assert.AreEqual("/tests.Test/Unary", context.Method);
return Task.FromResult("PASS");
});
Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
}
[Test]
public void ServerCallContext_AuthContextNotPopulated()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
Assert.IsFalse(context.AuthContext.IsPeerAuthenticated);
// 1) security_level: TSI_SECURITY_NONE
// 2) transport_security_type: 'insecure'
Assert.AreEqual(2, context.AuthContext.Properties.Count());
return Task.FromResult("PASS");
});
Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
}
}
}

@ -1,153 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Tests
{
public class CompressionTest
{
MockServiceHelper helper;
Server server;
Channel channel;
[SetUp]
public void Init()
{
helper = new MockServiceHelper();
server = helper.GetServer();
server.Start();
channel = helper.GetChannel();
}
[TearDown]
public void Cleanup()
{
channel.ShutdownAsync().Wait();
server.ShutdownAsync().Wait();
}
[Test]
public void WriteOptions_Unary()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
context.WriteOptions = new WriteOptions(WriteFlags.NoCompress);
return Task.FromResult(request);
});
var callOptions = new CallOptions(writeOptions: new WriteOptions(WriteFlags.NoCompress));
Calls.BlockingUnaryCall(helper.CreateUnaryCall(callOptions), "abc");
}
[Test]
public async Task WriteOptions_DuplexStreaming()
{
helper.DuplexStreamingHandler = new DuplexStreamingServerMethod<string, string>(async (requestStream, responseStream, context) =>
{
await requestStream.ToListAsync();
context.WriteOptions = new WriteOptions(WriteFlags.NoCompress);
await context.WriteResponseHeadersAsync(new Metadata { { "ascii-header", "abcdefg" } });
await responseStream.WriteAsync("X");
responseStream.WriteOptions = null;
await responseStream.WriteAsync("Y");
responseStream.WriteOptions = new WriteOptions(WriteFlags.NoCompress);
await responseStream.WriteAsync("Z");
});
var callOptions = new CallOptions(writeOptions: new WriteOptions(WriteFlags.NoCompress));
var call = Calls.AsyncDuplexStreamingCall(helper.CreateDuplexStreamingCall(callOptions));
// check that write options from call options are propagated to request stream.
Assert.IsTrue((call.RequestStream.WriteOptions.Flags & WriteFlags.NoCompress) != 0);
call.RequestStream.WriteOptions = new WriteOptions();
await call.RequestStream.WriteAsync("A");
call.RequestStream.WriteOptions = null;
await call.RequestStream.WriteAsync("B");
call.RequestStream.WriteOptions = new WriteOptions(WriteFlags.NoCompress);
await call.RequestStream.WriteAsync("C");
await call.RequestStream.CompleteAsync();
await call.ResponseStream.ToListAsync();
}
[Test]
public void CanReadCompressedMessages()
{
var compressionMetadata = new Metadata
{
{ new Metadata.Entry(Metadata.CompressionRequestAlgorithmMetadataKey, "gzip") }
};
helper.UnaryHandler = new UnaryServerMethod<string, string>(async (req, context) =>
{
await context.WriteResponseHeadersAsync(compressionMetadata);
return req;
});
var stringBuilder = new StringBuilder();
for (int i = 0; i < 200000; i++)
{
stringBuilder.Append('a');
}
var request = stringBuilder.ToString();
var response = Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(compressionMetadata)), request);
Assert.AreEqual(request, response);
}
[Test]
public void CanReadCompressedMessages_EmptyPayload()
{
var compressionMetadata = new Metadata
{
{ new Metadata.Entry(Metadata.CompressionRequestAlgorithmMetadataKey, "gzip") }
};
helper.UnaryHandler = new UnaryServerMethod<string, string>(async (req, context) =>
{
await context.WriteResponseHeadersAsync(compressionMetadata);
return req;
});
var request = "";
var response = Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(compressionMetadata)), request);
Assert.AreEqual(request, response);
}
}
}

@ -1,181 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Tests
{
public class ContextPropagationTest
{
MockServiceHelper helper;
Server server;
Channel channel;
[SetUp]
public void Init()
{
helper = new MockServiceHelper();
server = helper.GetServer();
server.Start();
channel = helper.GetChannel();
}
[TearDown]
public void Cleanup()
{
channel.ShutdownAsync().Wait();
server.ShutdownAsync().Wait();
}
[Test]
public async Task PropagateCancellation()
{
var readyToCancelTcs = new TaskCompletionSource<object>();
var successTcs = new TaskCompletionSource<string>();
helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
{
readyToCancelTcs.SetResult(null); // child call running, ready to parent call
while (!context.CancellationToken.IsCancellationRequested)
{
await Task.Delay(10);
}
successTcs.SetResult("CHILD_CALL_CANCELLED");
return "";
});
helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) =>
{
var propagationToken = context.CreatePropagationToken();
Assert.IsNotNull(propagationToken.AsImplOrNull().ParentCall);
var callOptions = new CallOptions(propagationToken: propagationToken);
try
{
await Calls.AsyncUnaryCall(helper.CreateUnaryCall(callOptions), "xyz");
}
catch(RpcException)
{
// Child call will get cancelled, eat the exception.
}
return "";
});
var cts = new CancellationTokenSource();
var parentCall = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall(new CallOptions(cancellationToken: cts.Token)));
await readyToCancelTcs.Task;
cts.Cancel();
try
{
// cannot use Assert.ThrowsAsync because it uses Task.Wait and would deadlock.
await parentCall;
Assert.Fail();
}
catch (RpcException)
{
}
Assert.AreEqual("CHILD_CALL_CANCELLED", await successTcs.Task);
}
[Test]
public async Task PropagateDeadline()
{
var deadline = DateTime.UtcNow.AddDays(7);
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
Assert.IsTrue(context.Deadline < deadline.AddHours(1));
Assert.IsTrue(context.Deadline > deadline.AddHours(-1));
return Task.FromResult("PASS");
});
helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) =>
{
Assert.Throws(typeof(ArgumentException), () =>
{
// Trying to override deadline while propagating deadline from parent call will throw.
Calls.BlockingUnaryCall(helper.CreateUnaryCall(
new CallOptions(deadline: DateTime.UtcNow.AddDays(8),
propagationToken: context.CreatePropagationToken())), "");
});
var callOptions = new CallOptions(propagationToken: context.CreatePropagationToken());
return await Calls.AsyncUnaryCall(helper.CreateUnaryCall(callOptions), "xyz");
});
var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall(new CallOptions(deadline: deadline)));
await call.RequestStream.CompleteAsync();
Assert.AreEqual("PASS", await call);
}
[Test]
public async Task SuppressDeadlinePropagation()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
Assert.AreEqual(DateTime.MaxValue, context.Deadline);
return Task.FromResult("PASS");
});
helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) =>
{
Assert.IsTrue(context.CancellationToken.CanBeCanceled);
var callOptions = new CallOptions(propagationToken: context.CreatePropagationToken(new ContextPropagationOptions(propagateDeadline: false)));
return await Calls.AsyncUnaryCall(helper.CreateUnaryCall(callOptions), "xyz");
});
var cts = new CancellationTokenSource();
var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall(new CallOptions(deadline: DateTime.UtcNow.AddDays(7))));
await call.RequestStream.CompleteAsync();
Assert.AreEqual("PASS", await call);
}
[Test]
public void ForeignPropagationTokenInterpretedAsNull()
{
Assert.IsNull(new ForeignContextPropagationToken().AsImplOrNull());
}
[Test]
public async Task ForeignPropagationTokenIsIgnored()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
return Task.FromResult("PASS");
});
var callOptions = new CallOptions(propagationToken: new ForeignContextPropagationToken());
await Calls.AsyncUnaryCall(helper.CreateUnaryCall(callOptions), "xyz");
}
// For testing, represents context propagation token that's not generated by Grpc.Core
private class ForeignContextPropagationToken : ContextPropagationToken
{
}
}
}

@ -1,121 +0,0 @@
#region Copyright notice and license
// Copyright 2018 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Tests
{
public class ContextualMarshallerTest
{
const string Host = "127.0.0.1";
MockServiceHelper helper;
Server server;
Channel channel;
[SetUp]
public void Init()
{
var contextualMarshaller = new Marshaller<string>(
(str, serializationContext) =>
{
if (str == "UNSERIALIZABLE_VALUE")
{
// Google.Protobuf throws exception inherited from IOException
throw new IOException("Error serializing the message.");
}
if (str == "SERIALIZE_TO_NULL")
{
// for contextual marshaller, serializing to null payload corresponds
// to not calling the Complete() method in the serializer.
return;
}
var bytes = System.Text.Encoding.UTF8.GetBytes(str);
serializationContext.Complete(bytes);
},
(deserializationContext) =>
{
var buffer = deserializationContext.PayloadAsNewBuffer();
Assert.AreEqual(buffer.Length, deserializationContext.PayloadLength);
var s = System.Text.Encoding.UTF8.GetString(buffer);
if (s == "UNPARSEABLE_VALUE")
{
// Google.Protobuf throws exception inherited from IOException
throw new IOException("Error parsing the message.");
}
return s;
});
helper = new MockServiceHelper(Host, contextualMarshaller);
server = helper.GetServer();
server.Start();
channel = helper.GetChannel();
}
[TearDown]
public void Cleanup()
{
channel.ShutdownAsync().Wait();
server.ShutdownAsync().Wait();
}
[Test]
public void UnaryCall()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
return Task.FromResult(request);
});
Assert.AreEqual("ABC", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "ABC"));
}
[Test]
public void ResponseParsingError_UnaryResponse()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
return Task.FromResult("UNPARSEABLE_VALUE");
});
var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "REQUEST"));
Assert.AreEqual(StatusCode.Internal, ex.Status.StatusCode);
}
[Test]
public void RequestSerializationError_BlockingUnary()
{
Assert.Throws<IOException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "UNSERIALIZABLE_VALUE"));
}
[Test]
public void SerializationResultIsNull_BlockingUnary()
{
Assert.Throws<NullReferenceException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "SERIALIZE_TO_NULL"));
}
}
}

@ -1,50 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using Grpc.Core.Internal;
namespace Grpc.Core.Tests
{
internal class FakeChannelCredentials : ChannelCredentials
{
readonly bool composable;
public FakeChannelCredentials(bool composable)
{
this.composable = composable;
}
internal override bool IsComposable
{
get { return composable; }
}
public override void InternalPopulateConfiguration(ChannelCredentialsConfiguratorBase configurator, object state)
{
// not invoking configuration on purpose
}
}
internal class FakeCallCredentials : CallCredentials
{
public override void InternalPopulateConfiguration(CallCredentialsConfiguratorBase configurator, object state)
{
// not invoking the configurator on purpose
}
}
}

@ -1,30 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net45;netcoreapp3.1</TargetFrameworks>
<OutputType>Exe</OutputType>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../Grpc.Core/Grpc.Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
<PackageReference Include="NUnit" Version="3.10.1" />
<PackageReference Include="NUnitLite" Version="3.10.1" />
<PackageReference Include="OpenCover" Version="4.6.519" />
<PackageReference Include="ReportGenerator" Version="2.4.4.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
<Reference Include="System" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Grpc.Core.Api\Version.cs" />
</ItemGroup>
</Project>

@ -1,94 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Linq;
using System.Threading;
using Grpc.Core;
using NUnit.Framework;
namespace Grpc.Core.Tests
{
public class GrpcEnvironmentTest
{
[Test]
public void InitializeAndShutdownGrpcEnvironment()
{
var env = GrpcEnvironment.AddRef();
Assert.IsTrue(env.CompletionQueues.Count > 0);
for (int i = 0; i < env.CompletionQueues.Count; i++)
{
Assert.IsNotNull(env.CompletionQueues.ElementAt(i));
}
GrpcEnvironment.ReleaseAsync().Wait();
}
[Test]
public void SubsequentInvocations()
{
var env1 = GrpcEnvironment.AddRef();
var env2 = GrpcEnvironment.AddRef();
Assert.AreSame(env1, env2);
GrpcEnvironment.ReleaseAsync().Wait();
GrpcEnvironment.ReleaseAsync().Wait();
}
[Test]
public void InitializeAfterShutdown()
{
Assert.AreEqual(0, GrpcEnvironment.GetRefCount());
var env1 = GrpcEnvironment.AddRef();
GrpcEnvironment.ReleaseAsync().Wait();
var env2 = GrpcEnvironment.AddRef();
GrpcEnvironment.ReleaseAsync().Wait();
Assert.AreNotSame(env1, env2);
}
[Test]
public void ReleaseWithoutAddRef()
{
Assert.AreEqual(0, GrpcEnvironment.GetRefCount());
Assert.ThrowsAsync(typeof(InvalidOperationException), async () => await GrpcEnvironment.ReleaseAsync());
}
[Test]
public void GetCoreVersionString()
{
var coreVersion = GrpcEnvironment.GetCoreVersionString();
var parts = coreVersion.Split('.');
Assert.AreEqual(3, parts.Length);
}
[Test]
public void ShuttingDownEventIsFired()
{
var cts = new CancellationTokenSource();
var handler = new EventHandler((sender, args) => { cts.Cancel(); });
GrpcEnvironment.ShuttingDown += handler;
var env = GrpcEnvironment.AddRef();
GrpcEnvironment.ReleaseAsync().Wait();
GrpcEnvironment.ShuttingDown -= handler;
Assert.IsTrue(cts.Token.IsCancellationRequested);
}
}
}

@ -1,82 +0,0 @@
#region Copyright notice and license
// Copyright 2016 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Tests
{
public class HalfcloseTest
{
MockServiceHelper helper;
Server server;
Channel channel;
[SetUp]
public void Init()
{
helper = new MockServiceHelper();
server = helper.GetServer();
server.Start();
channel = helper.GetChannel();
}
[TearDown]
public void Cleanup()
{
channel.ShutdownAsync().Wait();
server.ShutdownAsync().Wait();
}
/// <summary>
/// For client streaming and duplex streaming calls, if server does a full close
/// before we halfclose the request stream, an attempt to halfclose
/// (complete the request stream) shouldn't be treated as an error.
/// </summary>
[Test]
public async Task HalfcloseAfterFullclose_ClientStreamingCall()
{
helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>((requestStream, context) =>
{
return Task.FromResult("PASS");
});
var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall());
// make sure server has fullclosed on us
Assert.AreEqual("PASS", await call.ResponseAsync);
// sending close from client should be still fine because server can finish
// the call anytime and we cannot do anything about it on the client side.
await call.RequestStream.CompleteAsync();
// Second attempt to close from client is not allowed.
Assert.ThrowsAsync(typeof(InvalidOperationException), async () => await call.RequestStream.CompleteAsync());
}
}
}

@ -1,228 +0,0 @@
#region Copyright notice and license
// Copyright 2018 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Interceptors;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using Grpc.Core.Tests;
using NUnit.Framework;
namespace Grpc.Core.Interceptors.Tests
{
public class ClientInterceptorTest
{
const string Host = "127.0.0.1";
[Test]
public void AddRequestHeaderInClientInterceptor()
{
const string HeaderKey = "x-client-interceptor";
const string HeaderValue = "hello-world";
var helper = new MockServiceHelper(Host);
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
var interceptorHeader = context.RequestHeaders.Last(m => (m.Key == HeaderKey)).Value;
Assert.AreEqual(interceptorHeader, HeaderValue);
return Task.FromResult("PASS");
});
var server = helper.GetServer();
server.Start();
var callInvoker = helper.GetChannel().Intercept(metadata =>
{
metadata = metadata ?? new Metadata();
metadata.Add(new Metadata.Entry(HeaderKey, HeaderValue));
return metadata;
});
Assert.AreEqual("PASS", callInvoker.BlockingUnaryCall(new Method<string, string>(MethodType.Unary, MockServiceHelper.ServiceName, "Unary", Marshallers.StringMarshaller, Marshallers.StringMarshaller), Host, new CallOptions(), ""));
}
[Test]
public void CheckInterceptorOrderInClientInterceptors()
{
var helper = new MockServiceHelper(Host);
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
return Task.FromResult("PASS");
});
var server = helper.GetServer();
server.Start();
var stringBuilder = new StringBuilder();
var callInvoker = helper.GetChannel().Intercept(metadata => {
stringBuilder.Append("interceptor1");
return metadata;
}).Intercept(new CallbackInterceptor(() => stringBuilder.Append("array1")),
new CallbackInterceptor(() => stringBuilder.Append("array2")),
new CallbackInterceptor(() => stringBuilder.Append("array3")))
.Intercept(metadata =>
{
stringBuilder.Append("interceptor2");
return metadata;
}).Intercept(metadata =>
{
stringBuilder.Append("interceptor3");
return metadata;
});
Assert.AreEqual("PASS", callInvoker.BlockingUnaryCall(new Method<string, string>(MethodType.Unary, MockServiceHelper.ServiceName, "Unary", Marshallers.StringMarshaller, Marshallers.StringMarshaller), Host, new CallOptions(), ""));
Assert.AreEqual("interceptor3interceptor2array1array2array3interceptor1", stringBuilder.ToString());
}
[Test]
public void CheckNullInterceptorRegistrationFails()
{
var helper = new MockServiceHelper(Host);
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
return Task.FromResult("PASS");
});
Assert.Throws<ArgumentNullException>(() => helper.GetChannel().Intercept(default(Interceptor)));
Assert.Throws<ArgumentNullException>(() => helper.GetChannel().Intercept(new[]{default(Interceptor)}));
Assert.Throws<ArgumentNullException>(() => helper.GetChannel().Intercept(new[]{new CallbackInterceptor(()=>{}), null}));
Assert.Throws<ArgumentNullException>(() => helper.GetChannel().Intercept(default(Interceptor[])));
}
[Test]
public async Task CountNumberOfRequestsInClientInterceptors()
{
var helper = new MockServiceHelper(Host);
helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) =>
{
var stringBuilder = new StringBuilder();
await requestStream.ForEachAsync(request =>
{
stringBuilder.Append(request);
return TaskUtils.CompletedTask;
});
await Task.Delay(100);
return stringBuilder.ToString();
});
var callInvoker = helper.GetChannel().Intercept(new ClientStreamingCountingInterceptor());
var server = helper.GetServer();
server.Start();
var call = callInvoker.AsyncClientStreamingCall(new Method<string, string>(MethodType.ClientStreaming, MockServiceHelper.ServiceName, "ClientStreaming", Marshallers.StringMarshaller, Marshallers.StringMarshaller), Host, new CallOptions());
await call.RequestStream.WriteAllAsync(new string[] { "A", "B", "C" });
Assert.AreEqual("3", await call.ResponseAsync);
Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode);
Assert.IsNotNull(call.GetTrailers());
}
private class CallbackInterceptor : Interceptor
{
readonly Action callback;
public CallbackInterceptor(Action callback)
{
this.callback = GrpcPreconditions.CheckNotNull(callback, nameof(callback));
}
public override TResponse BlockingUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, BlockingUnaryCallContinuation<TRequest, TResponse> continuation)
{
callback();
return continuation(request, context);
}
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
{
callback();
return continuation(request, context);
}
public override AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncServerStreamingCallContinuation<TRequest, TResponse> continuation)
{
callback();
return continuation(request, context);
}
public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)
{
callback();
return continuation(context);
}
public override AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncDuplexStreamingCallContinuation<TRequest, TResponse> continuation)
{
callback();
return continuation(context);
}
}
private class ClientStreamingCountingInterceptor : Interceptor
{
public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)
{
var response = continuation(context);
int counter = 0;
var requestStream = new WrappedClientStreamWriter<TRequest>(response.RequestStream,
message => { counter++; return message; }, null);
var responseAsync = response.ResponseAsync.ContinueWith(
unaryResponse => (TResponse)(object)counter.ToString() // Cast to object first is needed to satisfy the type-checker
);
return new AsyncClientStreamingCall<TRequest, TResponse>(requestStream, responseAsync, response.ResponseHeadersAsync, response.GetStatus, response.GetTrailers, response.Dispose);
}
}
private class WrappedClientStreamWriter<T> : IClientStreamWriter<T>
{
readonly IClientStreamWriter<T> writer;
readonly Func<T, T> onMessage;
readonly Action onResponseStreamEnd;
public WrappedClientStreamWriter(IClientStreamWriter<T> writer, Func<T, T> onMessage, Action onResponseStreamEnd)
{
this.writer = writer;
this.onMessage = onMessage;
this.onResponseStreamEnd = onResponseStreamEnd;
}
public Task CompleteAsync()
{
if (onResponseStreamEnd != null)
{
return writer.CompleteAsync().ContinueWith(x => onResponseStreamEnd());
}
return writer.CompleteAsync();
}
public Task WriteAsync(T message)
{
if (onMessage != null)
{
message = onMessage(message);
}
return writer.WriteAsync(message);
}
public WriteOptions WriteOptions
{
get
{
return writer.WriteOptions;
}
set
{
writer.WriteOptions = value;
}
}
}
}
}

@ -1,173 +0,0 @@
#region Copyright notice and license
// Copyright 2018 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Interceptors;
using Grpc.Core.Internal;
using Grpc.Core.Tests;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Interceptors.Tests
{
public class ServerInterceptorTest
{
const string Host = "127.0.0.1";
[Test]
public void AddRequestHeaderInServerInterceptor()
{
var helper = new MockServiceHelper(Host);
const string MetadataKey = "x-interceptor";
const string MetadataValue = "hello world";
var interceptor = new ServerCallContextInterceptor(ctx => ctx.RequestHeaders.Add(new Metadata.Entry(MetadataKey, MetadataValue)));
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
var interceptorHeader = context.RequestHeaders.Last(m => (m.Key == MetadataKey)).Value;
Assert.AreEqual(interceptorHeader, MetadataValue);
return Task.FromResult("PASS");
});
helper.ServiceDefinition = helper.ServiceDefinition.Intercept(interceptor);
var server = helper.GetServer();
server.Start();
var channel = helper.GetChannel();
Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), ""));
}
[Test]
public void VerifyInterceptorOrdering()
{
var helper = new MockServiceHelper(Host);
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
return Task.FromResult("PASS");
});
var stringBuilder = new StringBuilder();
helper.ServiceDefinition = helper.ServiceDefinition
.Intercept(new ServerCallContextInterceptor(ctx => stringBuilder.Append("A")))
.Intercept(new ServerCallContextInterceptor(ctx => stringBuilder.Append("B1")),
new ServerCallContextInterceptor(ctx => stringBuilder.Append("B2")),
new ServerCallContextInterceptor(ctx => stringBuilder.Append("B3")))
.Intercept(new ServerCallContextInterceptor(ctx => stringBuilder.Append("C")));
var server = helper.GetServer();
server.Start();
var channel = helper.GetChannel();
Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), ""));
Assert.AreEqual("CB1B2B3A", stringBuilder.ToString());
}
[Test]
public void UserStateVisibleToAllInterceptors()
{
object key1 = new object();
object value1 = new object();
const string key2 = "Interceptor #2";
const string value2 = "Important state";
var interceptor1 = new ServerCallContextInterceptor(ctx => {
// state starts off empty
Assert.AreEqual(0, ctx.UserState.Count);
ctx.UserState.Add(key1, value1);
});
var interceptor2 = new ServerCallContextInterceptor(ctx => {
// second interceptor can see state set by the first
bool found = ctx.UserState.TryGetValue(key1, out object storedValue1);
Assert.IsTrue(found);
Assert.AreEqual(value1, storedValue1);
ctx.UserState.Add(key2, value2);
});
var helper = new MockServiceHelper(Host);
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) => {
// call handler can see all the state
bool found = context.UserState.TryGetValue(key1, out object storedValue1);
Assert.IsTrue(found);
Assert.AreEqual(value1, storedValue1);
found = context.UserState.TryGetValue(key2, out object storedValue2);
Assert.IsTrue(found);
Assert.AreEqual(value2, storedValue2);
return Task.FromResult("PASS");
});
helper.ServiceDefinition = helper.ServiceDefinition
.Intercept(interceptor2)
.Intercept(interceptor1);
var server = helper.GetServer();
server.Start();
var channel = helper.GetChannel();
Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), ""));
}
[Test]
public void CheckNullInterceptorRegistrationFails()
{
var helper = new MockServiceHelper(Host);
var sd = helper.ServiceDefinition;
Assert.Throws<ArgumentNullException>(() => sd.Intercept(default(Interceptor)));
Assert.Throws<ArgumentNullException>(() => sd.Intercept(new[]{default(Interceptor)}));
Assert.Throws<ArgumentNullException>(() => sd.Intercept(new[]{new ServerCallContextInterceptor(ctx=>{}), null}));
Assert.Throws<ArgumentNullException>(() => sd.Intercept(default(Interceptor[])));
}
private class ServerCallContextInterceptor : Interceptor
{
readonly Action<ServerCallContext> interceptor;
public ServerCallContextInterceptor(Action<ServerCallContext> interceptor)
{
GrpcPreconditions.CheckNotNull(interceptor, nameof(interceptor));
this.interceptor = interceptor;
}
public override Task<TResponse> UnaryServerHandler<TRequest, TResponse>(TRequest request, ServerCallContext context, UnaryServerMethod<TRequest, TResponse> continuation)
{
interceptor(context);
return continuation(request, context);
}
public override Task<TResponse> ClientStreamingServerHandler<TRequest, TResponse>(IAsyncStreamReader<TRequest> requestStream, ServerCallContext context, ClientStreamingServerMethod<TRequest, TResponse> continuation)
{
interceptor(context);
return continuation(requestStream, context);
}
public override Task ServerStreamingServerHandler<TRequest, TResponse>(TRequest request, IServerStreamWriter<TResponse> responseStream, ServerCallContext context, ServerStreamingServerMethod<TRequest, TResponse> continuation)
{
interceptor(context);
return continuation(request, responseStream, context);
}
public override Task DuplexStreamingServerHandler<TRequest, TResponse>(IAsyncStreamReader<TRequest> requestStream, IServerStreamWriter<TResponse> responseStream, ServerCallContext context, DuplexStreamingServerMethod<TRequest, TResponse> continuation)
{
interceptor(context);
return continuation(requestStream, responseStream, context);
}
}
}
}

@ -1,194 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Grpc.Core.Internal;
using NUnit.Framework;
namespace Grpc.Core.Internal.Tests
{
/// <summary>
/// Uses fake native call to test interaction of <c>AsyncCallServer</c> wrapping code with C core in different situations.
/// </summary>
public class AsyncCallServerTest
{
Server server;
FakeNativeCall fakeCall;
AsyncCallServer<string, string> asyncCallServer;
FakeBufferReaderManager fakeBufferReaderManager;
[SetUp]
public void Init()
{
// Create a fake server just so we have an instance to refer to.
// The server won't actually be used at all.
server = new Server()
{
Ports = { { "localhost", 0, ServerCredentials.Insecure } }
};
server.Start();
fakeCall = new FakeNativeCall();
asyncCallServer = new AsyncCallServer<string, string>(
Marshallers.StringMarshaller.ContextualSerializer, Marshallers.StringMarshaller.ContextualDeserializer,
server);
asyncCallServer.InitializeForTesting(fakeCall);
fakeBufferReaderManager = new FakeBufferReaderManager();
}
[TearDown]
public void Cleanup()
{
fakeBufferReaderManager.Dispose();
server.ShutdownAsync().Wait();
}
[Test]
public void CancelNotificationAfterStartDisposes()
{
var finishedTask = asyncCallServer.ServerSideCallAsync();
fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true);
AssertFinished(asyncCallServer, fakeCall, finishedTask);
}
[Test]
public void CancelNotificationAfterStartDisposesAfterPendingReadFinishes()
{
var finishedTask = asyncCallServer.ServerSideCallAsync();
var requestStream = new ServerRequestStream<string, string>(asyncCallServer);
var moveNextTask = requestStream.MoveNext();
fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true);
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
Assert.IsFalse(moveNextTask.Result);
AssertFinished(asyncCallServer, fakeCall, finishedTask);
}
[Test]
public void ReadAfterCancelNotificationCanSucceed()
{
var finishedTask = asyncCallServer.ServerSideCallAsync();
var requestStream = new ServerRequestStream<string, string>(asyncCallServer);
fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true);
// Check that starting a read after cancel notification has been processed is legal.
var moveNextTask = requestStream.MoveNext();
Assert.IsFalse(moveNextTask.Result);
AssertFinished(asyncCallServer, fakeCall, finishedTask);
}
[Test]
public void ReadCompletionFailureClosesRequestStream()
{
var finishedTask = asyncCallServer.ServerSideCallAsync();
var requestStream = new ServerRequestStream<string, string>(asyncCallServer);
// if a read completion's success==false, the request stream will silently finish
// and we rely on C core cancelling the call.
var moveNextTask = requestStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(false, CreateNullResponse());
Assert.IsFalse(moveNextTask.Result);
fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true);
AssertFinished(asyncCallServer, fakeCall, finishedTask);
}
[Test]
public void WriteAfterCancelNotificationFails()
{
var finishedTask = asyncCallServer.ServerSideCallAsync();
var responseStream = new ServerResponseStream<string, string>(asyncCallServer);
fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true);
// TODO(jtattermusch): should we throw a different exception type instead?
Assert.Throws(typeof(InvalidOperationException), () => responseStream.WriteAsync("request1"));
AssertFinished(asyncCallServer, fakeCall, finishedTask);
}
[Test]
public void WriteCompletionFailureThrows()
{
var finishedTask = asyncCallServer.ServerSideCallAsync();
var responseStream = new ServerResponseStream<string, string>(asyncCallServer);
var writeTask = responseStream.WriteAsync("request1");
fakeCall.SendCompletionCallback.OnSendCompletion(false);
Assert.ThrowsAsync(typeof(IOException), async () => await writeTask);
fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true);
AssertFinished(asyncCallServer, fakeCall, finishedTask);
}
[Test]
public void WriteAndWriteStatusCanRunConcurrently()
{
var finishedTask = asyncCallServer.ServerSideCallAsync();
var responseStream = new ServerResponseStream<string, string>(asyncCallServer);
var writeTask = responseStream.WriteAsync("request1");
var writeStatusTask = asyncCallServer.SendStatusFromServerAsync(Status.DefaultSuccess, new Metadata(), null);
fakeCall.SendCompletionCallback.OnSendCompletion(true);
fakeCall.SendStatusFromServerCallback.OnSendStatusFromServerCompletion(true);
Assert.DoesNotThrowAsync(async () => await writeTask);
Assert.DoesNotThrowAsync(async () => await writeStatusTask);
fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true);
AssertFinished(asyncCallServer, fakeCall, finishedTask);
}
[Test]
public void WriteAfterWriteStatusThrowsInvalidOperationException()
{
var finishedTask = asyncCallServer.ServerSideCallAsync();
var responseStream = new ServerResponseStream<string, string>(asyncCallServer);
asyncCallServer.SendStatusFromServerAsync(Status.DefaultSuccess, new Metadata(), null);
Assert.ThrowsAsync(typeof(InvalidOperationException), async () => await responseStream.WriteAsync("request1"));
fakeCall.SendStatusFromServerCallback.OnSendStatusFromServerCompletion(true);
fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true);
AssertFinished(asyncCallServer, fakeCall, finishedTask);
}
static void AssertFinished(AsyncCallServer<string, string> asyncCallServer, FakeNativeCall fakeCall, Task finishedTask)
{
Assert.IsTrue(fakeCall.IsDisposed);
Assert.IsTrue(finishedTask.IsCompleted);
Assert.DoesNotThrow(() => finishedTask.Wait());
}
IBufferReader CreateNullResponse()
{
return fakeBufferReaderManager.CreateNullPayloadBufferReader();
}
}
}

@ -1,82 +0,0 @@
#region Copyright notice and license
// Copyright 2019 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System.Threading.Tasks;
using NUnit.Framework;
namespace Grpc.Core.Internal.Tests
{
public class AsyncCallStateTest
{
[Test]
public void Stateless()
{
bool disposed = false;
Task<Metadata> responseHeaders = Task.FromResult(new Metadata());
Metadata trailers = new Metadata();
var state = new AsyncCallState(responseHeaders, () => new Status(StatusCode.DataLoss, "oops"),
() => trailers, () => disposed = true);
Assert.AreSame(responseHeaders, state.ResponseHeadersAsync());
var status = state.GetStatus();
Assert.AreEqual(StatusCode.DataLoss, status.StatusCode);
Assert.AreEqual("oops", status.Detail);
Assert.AreSame(trailers, state.GetTrailers());
Assert.False(disposed);
state.Dispose();
Assert.True(disposed);
}
class State
{
public bool disposed = false;
public Task<Metadata> responseHeaders = Task.FromResult(new Metadata());
public Metadata trailers = new Metadata();
public Status status = new Status(StatusCode.DataLoss, "oops");
public void Dispose() { disposed = true; }
}
[Test]
public void WithState()
{
var callbackState = new State();
var state = new AsyncCallState(
obj => ((State)obj).responseHeaders,
obj => ((State)obj).status,
obj => ((State)obj).trailers,
obj => ((State)obj).Dispose(),
callbackState);
Assert.AreSame(callbackState.responseHeaders, state.ResponseHeadersAsync());
var status = state.GetStatus();
Assert.AreEqual(StatusCode.DataLoss, status.StatusCode);
Assert.AreEqual("oops", status.Detail);
Assert.AreSame(callbackState.trailers, state.GetTrailers());
Assert.False(callbackState.disposed);
state.Dispose();
Assert.True(callbackState.disposed);
}
}
}

@ -1,766 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Internal.Tests
{
/// <summary>
/// Uses fake native call to test interaction of <c>AsyncCall</c> wrapping code with C core in different situations.
/// </summary>
public class AsyncCallTest
{
Channel channel;
FakeNativeCall fakeCall;
AsyncCall<string, string> asyncCall;
FakeBufferReaderManager fakeBufferReaderManager;
[SetUp]
public void Init()
{
channel = new Channel("localhost", ChannelCredentials.Insecure);
fakeCall = new FakeNativeCall();
var callDetails = new CallInvocationDetails<string, string>(channel, "someMethod", null, Marshallers.StringMarshaller, Marshallers.StringMarshaller, new CallOptions());
asyncCall = new AsyncCall<string, string>(callDetails, fakeCall);
fakeBufferReaderManager = new FakeBufferReaderManager();
}
[TearDown]
public void Cleanup()
{
channel.ShutdownAsync().Wait();
fakeBufferReaderManager.Dispose();
}
[Test]
public void AsyncUnary_CanBeStartedOnlyOnce()
{
asyncCall.UnaryCallAsync("request1");
Assert.Throws(typeof(InvalidOperationException),
() => asyncCall.UnaryCallAsync("abc"));
}
[Test]
public void AsyncUnary_StreamingOperationsNotAllowed()
{
asyncCall.UnaryCallAsync("request1");
Assert.ThrowsAsync(typeof(InvalidOperationException),
async () => await asyncCall.ReadMessageAsync());
Assert.Throws(typeof(InvalidOperationException),
() => asyncCall.SendMessageAsync("abc", new WriteFlags()));
}
[Test]
public void AsyncUnary_Success()
{
var resultTask = asyncCall.UnaryCallAsync("request1");
fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
new ClientSideStatus(Status.DefaultSuccess, new Metadata()),
CreateResponsePayload(),
new Metadata());
AssertUnaryResponseSuccess(asyncCall, fakeCall, resultTask);
}
[Test]
public void AsyncUnary_NonSuccessStatusCode()
{
var resultTask = asyncCall.UnaryCallAsync("request1");
fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
CreateClientSideStatus(StatusCode.InvalidArgument),
CreateNullResponse(),
new Metadata());
AssertUnaryResponseError(asyncCall, fakeCall, resultTask, StatusCode.InvalidArgument);
}
[Test]
public void AsyncUnary_NullResponsePayload()
{
var resultTask = asyncCall.UnaryCallAsync("request1");
fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
new ClientSideStatus(Status.DefaultSuccess, new Metadata()),
null,
new Metadata());
// failure to deserialize will result in InvalidArgument status.
AssertUnaryResponseError(asyncCall, fakeCall, resultTask, StatusCode.Internal);
}
[Test]
public void AsyncUnary_RequestSerializationExceptionDoesntLeakResources()
{
string nullRequest = null; // will throw when serializing
Assert.Throws(typeof(ArgumentNullException), () => asyncCall.UnaryCallAsync(nullRequest));
Assert.AreEqual(0, channel.GetCallReferenceCount());
Assert.IsTrue(fakeCall.IsDisposed);
}
[Test]
public void AsyncUnary_StartCallFailureDoesntLeakResources()
{
fakeCall.MakeStartCallFail();
Assert.Throws(typeof(InvalidOperationException), () => asyncCall.UnaryCallAsync("request1"));
Assert.AreEqual(0, channel.GetCallReferenceCount());
Assert.IsTrue(fakeCall.IsDisposed);
}
[Test]
public void SyncUnary_RequestSerializationExceptionDoesntLeakResources()
{
string nullRequest = null; // will throw when serializing
Assert.Throws(typeof(ArgumentNullException), () => asyncCall.UnaryCall(nullRequest));
Assert.AreEqual(0, channel.GetCallReferenceCount());
Assert.IsTrue(fakeCall.IsDisposed);
}
[Test]
public void SyncUnary_StartCallFailureDoesntLeakResources()
{
fakeCall.MakeStartCallFail();
Assert.Throws(typeof(InvalidOperationException), () => asyncCall.UnaryCall("request1"));
Assert.AreEqual(0, channel.GetCallReferenceCount());
Assert.IsTrue(fakeCall.IsDisposed);
}
[Test]
public void ClientStreaming_StreamingReadNotAllowed()
{
asyncCall.ClientStreamingCallAsync();
Assert.ThrowsAsync(typeof(InvalidOperationException),
async () => await asyncCall.ReadMessageAsync());
}
[Test]
public void ClientStreaming_NoRequest_Success()
{
var resultTask = asyncCall.ClientStreamingCallAsync();
fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
new ClientSideStatus(Status.DefaultSuccess, new Metadata()),
CreateResponsePayload(),
new Metadata());
AssertUnaryResponseSuccess(asyncCall, fakeCall, resultTask);
}
[Test]
public void ClientStreaming_NoRequest_NonSuccessStatusCode()
{
var resultTask = asyncCall.ClientStreamingCallAsync();
fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
CreateClientSideStatus(StatusCode.InvalidArgument),
CreateNullResponse(),
new Metadata());
AssertUnaryResponseError(asyncCall, fakeCall, resultTask, StatusCode.InvalidArgument);
}
[Test]
public void ClientStreaming_MoreRequests_Success()
{
var resultTask = asyncCall.ClientStreamingCallAsync();
var requestStream = new ClientRequestStream<string, string>(asyncCall);
var writeTask = requestStream.WriteAsync("request1");
fakeCall.SendCompletionCallback.OnSendCompletion(true);
writeTask.Wait();
var writeTask2 = requestStream.WriteAsync("request2");
fakeCall.SendCompletionCallback.OnSendCompletion(true);
writeTask2.Wait();
var completeTask = requestStream.CompleteAsync();
fakeCall.SendCompletionCallback.OnSendCompletion(true);
completeTask.Wait();
fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
new ClientSideStatus(Status.DefaultSuccess, new Metadata()),
CreateResponsePayload(),
new Metadata());
AssertUnaryResponseSuccess(asyncCall, fakeCall, resultTask);
}
[Test]
public void ClientStreaming_WriteFailureThrowsRpcException()
{
var resultTask = asyncCall.ClientStreamingCallAsync();
var requestStream = new ClientRequestStream<string, string>(asyncCall);
var writeTask = requestStream.WriteAsync("request1");
fakeCall.SendCompletionCallback.OnSendCompletion(false);
// The write will wait for call to finish to receive the status code.
Assert.IsFalse(writeTask.IsCompleted);
fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
CreateClientSideStatus(StatusCode.Internal),
CreateNullResponse(),
new Metadata());
var ex = Assert.ThrowsAsync<RpcException>(async () => await writeTask);
Assert.AreEqual(StatusCode.Internal, ex.Status.StatusCode);
AssertUnaryResponseError(asyncCall, fakeCall, resultTask, StatusCode.Internal);
}
[Test]
public void ClientStreaming_WriteFailureThrowsRpcException2()
{
var resultTask = asyncCall.ClientStreamingCallAsync();
var requestStream = new ClientRequestStream<string, string>(asyncCall);
var writeTask = requestStream.WriteAsync("request1");
fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
CreateClientSideStatus(StatusCode.Internal),
CreateNullResponse(),
new Metadata());
fakeCall.SendCompletionCallback.OnSendCompletion(false);
var ex = Assert.ThrowsAsync<RpcException>(async () => await writeTask);
Assert.AreEqual(StatusCode.Internal, ex.Status.StatusCode);
AssertUnaryResponseError(asyncCall, fakeCall, resultTask, StatusCode.Internal);
}
[Test]
public void ClientStreaming_WriteFailureThrowsRpcException3()
{
var resultTask = asyncCall.ClientStreamingCallAsync();
var requestStream = new ClientRequestStream<string, string>(asyncCall);
var writeTask = requestStream.WriteAsync("request1");
fakeCall.SendCompletionCallback.OnSendCompletion(false);
// Until the delayed write completion has been triggered,
// we still act as if there was an active write.
Assert.Throws(typeof(InvalidOperationException), () => requestStream.WriteAsync("request2"));
fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
CreateClientSideStatus(StatusCode.Internal),
CreateNullResponse(),
new Metadata());
var ex = Assert.ThrowsAsync<RpcException>(async () => await writeTask);
Assert.AreEqual(StatusCode.Internal, ex.Status.StatusCode);
// Following attempts to write keep delivering the same status
var ex2 = Assert.ThrowsAsync<RpcException>(async () => await requestStream.WriteAsync("after call has finished"));
Assert.AreEqual(StatusCode.Internal, ex2.Status.StatusCode);
AssertUnaryResponseError(asyncCall, fakeCall, resultTask, StatusCode.Internal);
}
[Test]
public void ClientStreaming_WriteAfterReceivingStatusThrowsRpcException()
{
var resultTask = asyncCall.ClientStreamingCallAsync();
var requestStream = new ClientRequestStream<string, string>(asyncCall);
fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
new ClientSideStatus(Status.DefaultSuccess, new Metadata()),
CreateResponsePayload(),
new Metadata());
AssertUnaryResponseSuccess(asyncCall, fakeCall, resultTask);
var writeTask = requestStream.WriteAsync("request1");
var ex = Assert.ThrowsAsync<RpcException>(async () => await writeTask);
Assert.AreEqual(Status.DefaultSuccess, ex.Status);
}
[Test]
public void ClientStreaming_WriteAfterReceivingStatusThrowsRpcException2()
{
var resultTask = asyncCall.ClientStreamingCallAsync();
var requestStream = new ClientRequestStream<string, string>(asyncCall);
fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
new ClientSideStatus(new Status(StatusCode.OutOfRange, ""), new Metadata()),
CreateResponsePayload(),
new Metadata());
AssertUnaryResponseError(asyncCall, fakeCall, resultTask, StatusCode.OutOfRange);
var writeTask = requestStream.WriteAsync("request1");
var ex = Assert.ThrowsAsync<RpcException>(async () => await writeTask);
Assert.AreEqual(StatusCode.OutOfRange, ex.Status.StatusCode);
}
[Test]
public void ClientStreaming_WriteAfterCompleteThrowsInvalidOperationException()
{
var resultTask = asyncCall.ClientStreamingCallAsync();
var requestStream = new ClientRequestStream<string, string>(asyncCall);
requestStream.CompleteAsync();
Assert.Throws(typeof(InvalidOperationException), () => requestStream.WriteAsync("request1"));
fakeCall.SendCompletionCallback.OnSendCompletion(true);
fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
new ClientSideStatus(Status.DefaultSuccess, new Metadata()),
CreateResponsePayload(),
new Metadata());
AssertUnaryResponseSuccess(asyncCall, fakeCall, resultTask);
}
[Test]
public void ClientStreaming_CompleteAfterReceivingStatusSucceeds()
{
var resultTask = asyncCall.ClientStreamingCallAsync();
var requestStream = new ClientRequestStream<string, string>(asyncCall);
fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
new ClientSideStatus(Status.DefaultSuccess, new Metadata()),
CreateResponsePayload(),
new Metadata());
AssertUnaryResponseSuccess(asyncCall, fakeCall, resultTask);
Assert.DoesNotThrowAsync(async () => await requestStream.CompleteAsync());
}
[Test]
public void ClientStreaming_WriteAfterCancellationRequestThrowsTaskCanceledException()
{
var resultTask = asyncCall.ClientStreamingCallAsync();
var requestStream = new ClientRequestStream<string, string>(asyncCall);
asyncCall.Cancel();
Assert.IsTrue(fakeCall.IsCancelled);
var writeTask = requestStream.WriteAsync("request1");
Assert.ThrowsAsync(typeof(TaskCanceledException), async () => await writeTask);
fakeCall.UnaryResponseClientCallback.OnUnaryResponseClient(true,
CreateClientSideStatus(StatusCode.Cancelled),
CreateNullResponse(),
new Metadata());
AssertUnaryResponseError(asyncCall, fakeCall, resultTask, StatusCode.Cancelled);
}
[Test]
public void ClientStreaming_StartCallFailureDoesntLeakResources()
{
fakeCall.MakeStartCallFail();
Assert.Throws(typeof(InvalidOperationException), () => asyncCall.ClientStreamingCallAsync());
Assert.AreEqual(0, channel.GetCallReferenceCount());
Assert.IsTrue(fakeCall.IsDisposed);
}
[Test]
public void ServerStreaming_StreamingSendNotAllowed()
{
asyncCall.StartServerStreamingCall("request1");
Assert.Throws(typeof(InvalidOperationException),
() => asyncCall.SendMessageAsync("abc", new WriteFlags()));
}
[Test]
public void ServerStreaming_NoResponse_Success1()
{
asyncCall.StartServerStreamingCall("request1");
var responseStream = new ClientResponseStream<string, string>(asyncCall);
var readTask = responseStream.MoveNext();
fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(true, new Metadata());
Assert.AreEqual(0, asyncCall.ResponseHeadersAsync.Result.Count);
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()));
AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask);
}
[Test]
public void ServerStreaming_NoResponse_Success2()
{
asyncCall.StartServerStreamingCall("request1");
var responseStream = new ClientResponseStream<string, string>(asyncCall);
var readTask = responseStream.MoveNext();
// try alternative order of completions
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()));
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(true, new Metadata());
AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask);
}
[Test]
public void ServerStreaming_NoResponse_Success3()
{
asyncCall.StartServerStreamingCall("request1");
var responseStream = new ClientResponseStream<string, string>(asyncCall);
var readTask = responseStream.MoveNext();
// try alternative order of completions
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()));
fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(true, new Metadata());
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask);
}
[Test]
public void ServerStreaming_NoResponse_ReadFailure()
{
asyncCall.StartServerStreamingCall("request1");
var responseStream = new ClientResponseStream<string, string>(asyncCall);
var readTask = responseStream.MoveNext();
fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(true, new Metadata());
Assert.AreEqual(0, asyncCall.ResponseHeadersAsync.Result.Count);
fakeCall.ReceivedMessageCallback.OnReceivedMessage(false, CreateNullResponse()); // after a failed read, we rely on C core to deliver appropriate status code.
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.Internal));
AssertStreamingResponseError(asyncCall, fakeCall, readTask, StatusCode.Internal);
}
[Test]
public void ServerStreaming_MoreResponses_Success()
{
asyncCall.StartServerStreamingCall("request1");
var responseStream = new ClientResponseStream<string, string>(asyncCall);
fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(true, new Metadata());
Assert.AreEqual(0, asyncCall.ResponseHeadersAsync.Result.Count);
var readTask1 = responseStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateResponsePayload());
Assert.IsTrue(readTask1.Result);
Assert.AreEqual("response1", responseStream.Current);
var readTask2 = responseStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateResponsePayload());
Assert.IsTrue(readTask2.Result);
Assert.AreEqual("response1", responseStream.Current);
var readTask3 = responseStream.MoveNext();
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()));
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask3);
}
[Test]
public void ServerStreaming_RequestSerializationExceptionDoesntLeakResources()
{
string nullRequest = null; // will throw when serializing
Assert.Throws(typeof(ArgumentNullException), () => asyncCall.StartServerStreamingCall(nullRequest));
Assert.AreEqual(0, channel.GetCallReferenceCount());
Assert.IsTrue(fakeCall.IsDisposed);
var responseStream = new ClientResponseStream<string, string>(asyncCall);
var readTask = responseStream.MoveNext();
}
[Test]
public void ServerStreaming_StartCallFailureDoesntLeakResources()
{
fakeCall.MakeStartCallFail();
Assert.Throws(typeof(InvalidOperationException), () => asyncCall.StartServerStreamingCall("request1"));
Assert.AreEqual(0, channel.GetCallReferenceCount());
Assert.IsTrue(fakeCall.IsDisposed);
}
[Test]
public void DuplexStreaming_NoRequestNoResponse_Success1()
{
asyncCall.StartDuplexStreamingCall();
var requestStream = new ClientRequestStream<string, string>(asyncCall);
var responseStream = new ClientResponseStream<string, string>(asyncCall);
fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(true, new Metadata());
Assert.AreEqual(0, asyncCall.ResponseHeadersAsync.Result.Count);
var writeTask1 = requestStream.CompleteAsync();
fakeCall.SendCompletionCallback.OnSendCompletion(true);
Assert.DoesNotThrowAsync(async () => await writeTask1);
var readTask = responseStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()));
AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask);
}
[Test]
public void DuplexStreaming_NoRequestNoResponse_Success2()
{
asyncCall.StartDuplexStreamingCall();
var requestStream = new ClientRequestStream<string, string>(asyncCall);
var responseStream = new ClientResponseStream<string, string>(asyncCall);
var writeTask1 = requestStream.CompleteAsync();
fakeCall.SendCompletionCallback.OnSendCompletion(true);
Assert.DoesNotThrowAsync(async () => await writeTask1);
var readTask = responseStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()));
fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(true, new Metadata());
Assert.AreEqual(0, asyncCall.ResponseHeadersAsync.Result.Count);
AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask);
}
[Test]
public void DuplexStreaming_WriteAfterReceivingStatusThrowsRpcException()
{
asyncCall.StartDuplexStreamingCall();
var requestStream = new ClientRequestStream<string, string>(asyncCall);
var responseStream = new ClientResponseStream<string, string>(asyncCall);
fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(true, new Metadata());
Assert.AreEqual(0, asyncCall.ResponseHeadersAsync.Result.Count);
var readTask = responseStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()));
AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask);
var writeTask = requestStream.WriteAsync("request1");
var ex = Assert.ThrowsAsync<RpcException>(async () => await writeTask);
Assert.AreEqual(Status.DefaultSuccess, ex.Status);
}
[Test]
public void DuplexStreaming_CompleteAfterReceivingStatusSuceeds()
{
asyncCall.StartDuplexStreamingCall();
var requestStream = new ClientRequestStream<string, string>(asyncCall);
var responseStream = new ClientResponseStream<string, string>(asyncCall);
fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(true, new Metadata());
Assert.AreEqual(0, asyncCall.ResponseHeadersAsync.Result.Count);
var readTask = responseStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, new ClientSideStatus(Status.DefaultSuccess, new Metadata()));
AssertStreamingResponseSuccess(asyncCall, fakeCall, readTask);
Assert.DoesNotThrowAsync(async () => await requestStream.CompleteAsync());
}
[Test]
public void DuplexStreaming_WriteFailureThrowsRpcException()
{
asyncCall.StartDuplexStreamingCall();
var requestStream = new ClientRequestStream<string, string>(asyncCall);
var responseStream = new ClientResponseStream<string, string>(asyncCall);
fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(true, new Metadata());
Assert.AreEqual(0, asyncCall.ResponseHeadersAsync.Result.Count);
var writeTask = requestStream.WriteAsync("request1");
fakeCall.SendCompletionCallback.OnSendCompletion(false);
// The write will wait for call to finish to receive the status code.
Assert.IsFalse(writeTask.IsCompleted);
var readTask = responseStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.PermissionDenied));
var ex = Assert.ThrowsAsync<RpcException>(async () => await writeTask);
Assert.AreEqual(StatusCode.PermissionDenied, ex.Status.StatusCode);
AssertStreamingResponseError(asyncCall, fakeCall, readTask, StatusCode.PermissionDenied);
}
[Test]
public void DuplexStreaming_WriteFailureThrowsRpcException2()
{
asyncCall.StartDuplexStreamingCall();
var requestStream = new ClientRequestStream<string, string>(asyncCall);
var responseStream = new ClientResponseStream<string, string>(asyncCall);
fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(true, new Metadata());
Assert.AreEqual(0, asyncCall.ResponseHeadersAsync.Result.Count);
var writeTask = requestStream.WriteAsync("request1");
var readTask = responseStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.PermissionDenied));
fakeCall.SendCompletionCallback.OnSendCompletion(false);
var ex = Assert.ThrowsAsync<RpcException>(async () => await writeTask);
Assert.AreEqual(StatusCode.PermissionDenied, ex.Status.StatusCode);
AssertStreamingResponseError(asyncCall, fakeCall, readTask, StatusCode.PermissionDenied);
}
[Test]
public void DuplexStreaming_WriteAfterCancellationRequestThrowsTaskCanceledException()
{
asyncCall.StartDuplexStreamingCall();
var requestStream = new ClientRequestStream<string, string>(asyncCall);
var responseStream = new ClientResponseStream<string, string>(asyncCall);
fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(true, new Metadata());
Assert.AreEqual(0, asyncCall.ResponseHeadersAsync.Result.Count);
asyncCall.Cancel();
Assert.IsTrue(fakeCall.IsCancelled);
var writeTask = requestStream.WriteAsync("request1");
Assert.ThrowsAsync(typeof(TaskCanceledException), async () => await writeTask);
var readTask = responseStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.Cancelled));
AssertStreamingResponseError(asyncCall, fakeCall, readTask, StatusCode.Cancelled);
}
[Test]
public void DuplexStreaming_ReadAfterCancellationRequestCanSucceed()
{
asyncCall.StartDuplexStreamingCall();
var responseStream = new ClientResponseStream<string, string>(asyncCall);
fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(true, new Metadata());
Assert.AreEqual(0, asyncCall.ResponseHeadersAsync.Result.Count);
asyncCall.Cancel();
Assert.IsTrue(fakeCall.IsCancelled);
var readTask1 = responseStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateResponsePayload());
Assert.IsTrue(readTask1.Result);
Assert.AreEqual("response1", responseStream.Current);
var readTask2 = responseStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.Cancelled));
AssertStreamingResponseError(asyncCall, fakeCall, readTask2, StatusCode.Cancelled);
}
[Test]
public void DuplexStreaming_ReadStartedBeforeCancellationRequestCanSucceed()
{
asyncCall.StartDuplexStreamingCall();
var responseStream = new ClientResponseStream<string, string>(asyncCall);
fakeCall.ReceivedResponseHeadersCallback.OnReceivedResponseHeaders(true, new Metadata());
Assert.AreEqual(0, asyncCall.ResponseHeadersAsync.Result.Count);
var readTask1 = responseStream.MoveNext(); // initiate the read before cancel request
asyncCall.Cancel();
Assert.IsTrue(fakeCall.IsCancelled);
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateResponsePayload());
Assert.IsTrue(readTask1.Result);
Assert.AreEqual("response1", responseStream.Current);
var readTask2 = responseStream.MoveNext();
fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse());
fakeCall.ReceivedStatusOnClientCallback.OnReceivedStatusOnClient(true, CreateClientSideStatus(StatusCode.Cancelled));
AssertStreamingResponseError(asyncCall, fakeCall, readTask2, StatusCode.Cancelled);
}
[Test]
public void DuplexStreaming_StartCallFailureDoesntLeakResources()
{
fakeCall.MakeStartCallFail();
Assert.Throws(typeof(InvalidOperationException), () => asyncCall.StartDuplexStreamingCall());
Assert.AreEqual(0, channel.GetCallReferenceCount());
Assert.IsTrue(fakeCall.IsDisposed);
}
ClientSideStatus CreateClientSideStatus(StatusCode statusCode)
{
return new ClientSideStatus(new Status(statusCode, ""), new Metadata());
}
IBufferReader CreateResponsePayload()
{
return fakeBufferReaderManager.CreateSingleSegmentBufferReader(Marshallers.StringMarshaller.Serializer("response1"));
}
IBufferReader CreateNullResponse()
{
return fakeBufferReaderManager.CreateNullPayloadBufferReader();
}
static void AssertUnaryResponseSuccess(AsyncCall<string, string> asyncCall, FakeNativeCall fakeCall, Task<string> resultTask)
{
Assert.IsTrue(resultTask.IsCompleted);
Assert.IsTrue(fakeCall.IsDisposed);
Assert.AreEqual(Status.DefaultSuccess, asyncCall.GetStatus());
Assert.AreEqual(0, asyncCall.ResponseHeadersAsync.Result.Count);
Assert.AreEqual(0, asyncCall.GetTrailers().Count);
Assert.AreEqual("response1", resultTask.Result);
}
static void AssertStreamingResponseSuccess(AsyncCall<string, string> asyncCall, FakeNativeCall fakeCall, Task<bool> moveNextTask)
{
Assert.IsTrue(moveNextTask.IsCompleted);
Assert.IsTrue(fakeCall.IsDisposed);
Assert.IsFalse(moveNextTask.Result);
Assert.AreEqual(Status.DefaultSuccess, asyncCall.GetStatus());
Assert.AreEqual(0, asyncCall.GetTrailers().Count);
}
static void AssertUnaryResponseError(AsyncCall<string, string> asyncCall, FakeNativeCall fakeCall, Task<string> resultTask, StatusCode expectedStatusCode)
{
Assert.IsTrue(resultTask.IsCompleted);
Assert.IsTrue(fakeCall.IsDisposed);
Assert.AreEqual(expectedStatusCode, asyncCall.GetStatus().StatusCode);
var ex = Assert.ThrowsAsync<RpcException>(async () => await resultTask);
Assert.AreEqual(expectedStatusCode, ex.Status.StatusCode);
Assert.AreEqual(0, asyncCall.ResponseHeadersAsync.Result.Count);
Assert.AreEqual(0, asyncCall.GetTrailers().Count);
}
static void AssertStreamingResponseError(AsyncCall<string, string> asyncCall, FakeNativeCall fakeCall, Task<bool> moveNextTask, StatusCode expectedStatusCode)
{
Assert.IsTrue(moveNextTask.IsCompleted);
Assert.IsTrue(fakeCall.IsDisposed);
var ex = Assert.ThrowsAsync<RpcException>(async () => await moveNextTask);
Assert.AreEqual(expectedStatusCode, ex.Status.StatusCode);
Assert.AreEqual(expectedStatusCode, asyncCall.GetStatus().StatusCode);
Assert.AreEqual(0, asyncCall.GetTrailers().Count);
}
}
}

@ -1,60 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Internal.Tests
{
public class ChannelArgsSafeHandleTest
{
[Test]
public void CreateEmptyAndDestroy()
{
var channelArgs = ChannelArgsSafeHandle.Create(0);
channelArgs.Dispose();
}
[Test]
public void CreateNonEmptyAndDestroy()
{
var channelArgs = ChannelArgsSafeHandle.Create(5);
channelArgs.Dispose();
}
[Test]
public void CreateNullAndDestroy()
{
var channelArgs = ChannelArgsSafeHandle.CreateNull();
channelArgs.Dispose();
}
[Test]
public void CreateFillAndDestroy()
{
var channelArgs = ChannelArgsSafeHandle.Create(3);
channelArgs.SetInteger(0, "somekey", 12345);
channelArgs.SetString(1, "somekey", "abcdefghijkl");
channelArgs.SetString(2, "somekey", "XYZ");
channelArgs.Dispose();
}
}
}

@ -1,40 +0,0 @@
#region Copyright notice and license
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Internal.Tests
{
public class CompletionQueueEventTest
{
[Test]
public void CompletionQueueEventSizeIsNativeSize()
{
#pragma warning disable 0618
// We need to use the obsolete non-generic version of Marshal.SizeOf because the generic version is not available in net45
Assert.AreEqual(CompletionQueueEvent.NativeSize, Marshal.SizeOf(typeof(CompletionQueueEvent)));
#pragma warning restore 0618
}
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save