parent
207bcfdc31
commit
97f2b6d707
500 changed files with 0 additions and 74282 deletions
@ -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): |
||||
|
||||
 |
||||
|
||||
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: |
||||
|
||||
 |
||||
|
||||
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,2 +0,0 @@ |
||||
bin |
||||
obj |
@ -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,2 +0,0 @@ |
||||
bin |
||||
obj |
@ -1,2 +0,0 @@ |
||||
bin |
||||
obj |
@ -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…
Reference in new issue