Skip to content

Commit

Permalink
Add IProtobufClient abstraction
Browse files Browse the repository at this point in the history
  • Loading branch information
pepone committed Dec 1, 2023
1 parent 009a830 commit 029358a
Show file tree
Hide file tree
Showing 16 changed files with 489 additions and 3 deletions.
34 changes: 34 additions & 0 deletions examples/protobuf/GenericHost/Client/Client.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<!-- Copy the PDBs from the NuGet packages to get file names and line numbers in stack traces. -->
<CopyDebugSymbolFilesFromPackages>true</CopyDebugSymbolFilesFromPackages>
</PropertyGroup>
<ItemGroup>
<None Remove="appsettings.json" />
</ItemGroup>
<ItemGroup>
<Content Include="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<None Include="..\..\..\..\certs\cacert.der" Link="cacert.der">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<ProtoFile Include="../proto/greeter.proto" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.*" />
<PackageReference Include="Microsoft.Extensions.Options.DataAnnotations" Version="8.0.*" />
<PackageReference Include="IceRpc.Protobuf.Tools" Version="$(Version)" PrivateAssets="All" />
<PackageReference Include="IceRpc.Protobuf" Version="$(Version)" />
<PackageReference Include="IceRpc.Extensions.DependencyInjection" Version="$(Version)" />
<PackageReference Include="IceRpc.Logger" Version="$(Version)" />
<PackageReference Include="IceRpc.Telemetry" Version="$(Version)" />
</ItemGroup>
</Project>
48 changes: 48 additions & 0 deletions examples/protobuf/GenericHost/Client/ClientHostedService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) ZeroC, Inc.

using IceRpc;
using Microsoft.Extensions.Hosting;
using VisitorCenter;

namespace GenericHostClient;

/// <summary>The hosted client service is ran and managed by the .NET Generic Host.</summary>
public class ClientHostedService : BackgroundService
{
// The host application lifetime is used to stop the .NET Generic Host.
private readonly IHostApplicationLifetime _applicationLifetime;

private readonly ClientConnection _connection;

// The IGreeter managed by the DI container.
private readonly IGreeter _greeter;

// All the parameters are injected by the DI container.
public ClientHostedService(
IGreeter greeter,
ClientConnection connection,
IHostApplicationLifetime applicationLifetime)
{
_applicationLifetime = applicationLifetime;
_connection = connection;
_greeter = greeter;
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
var request = new GreetRequest { Name = Environment.UserName };
GreetResponse response = await _greeter.GreetAsync(request, cancellationToken: stoppingToken);
Console.WriteLine(response.Greeting);
await _connection.ShutdownAsync(stoppingToken);
}
catch (Exception exception)
{
Console.WriteLine($"Failed to connect to the server:\n{exception}");
}

// Stop the generic host once the invocation is done.
_applicationLifetime.StopApplication();
}
}
72 changes: 72 additions & 0 deletions examples/protobuf/GenericHost/Client/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) ZeroC, Inc.

using GenericHostClient;
using IceRpc;
using IceRpc.Extensions.DependencyInjection;
using IceRpc.Protobuf;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Diagnostics;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using VisitorCenter;

// Configure the host.
IHostBuilder hostBuilder = Host.CreateDefaultBuilder(args)
// Set the content root path to the build directory of the client (e.g.: Client/bin/Debug/net8.0)
.UseContentRoot(AppContext.BaseDirectory)

// Configures the .NET Generic Host services.
.ConfigureServices((hostContext, services) =>
{
// Add the ClientHostedService to the hosted services of the .NET Generic Host.
services.AddHostedService<ClientHostedService>();
// Bind the client connection options to the "appsettings.json" configuration "Client" section,
// and add a Configure callback to configure its authentication options.
services
.AddOptions<ClientConnectionOptions>()
.Bind(hostContext.Configuration.GetSection("Client"))
.Configure(options =>
{
// Configure the authentication options
var rootCA = new X509Certificate2(
Path.Combine(
hostContext.HostingEnvironment.ContentRootPath,
hostContext.Configuration.GetValue<string>("CertificateAuthoritiesFile")!));
options.ClientAuthenticationOptions = new SslClientAuthenticationOptions
{
// A certificate validation callback that uses the configured certificate authorities file to
// validate the peer certificates.
RemoteCertificateValidationCallback = (sender, certificate, chain, errors) =>
{
using var customChain = new X509Chain();
customChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
customChain.ChainPolicy.DisableCertificateDownloads = true;
customChain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
customChain.ChainPolicy.CustomTrustStore.Add(rootCA);
return customChain.Build((X509Certificate2)certificate!);
}
};
});
services
// The activity source used by the telemetry interceptor.
.AddSingleton(_ => new ActivitySource("IceRpc"))
// Add a ClientConnection singleton. This ClientConnections uses the ClientConnectionOptions provided by the
// the IOptions<ClientConnectionOptions> configured/bound above.
.AddIceRpcClientConnection()
// Add an invoker singleton; this invoker corresponds to the invocation pipeline. This invocation pipeline
// flows into the ClientConnection singleton.
.AddIceRpcInvoker(builder => builder.UseTelemetry().UseLogger().Into<ClientConnection>())
// Add an IGreeter singleton that uses the invoker singleton registered above.
.AddSingleton<IGreeter>(provider => provider.CreateProtobufClient<GreeterClient>());
});

// Build the host.
using IHost host = hostBuilder.Build();

// Run hosted program.
host.Run();
15 changes: 15 additions & 0 deletions examples/protobuf/GenericHost/Client/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"Logging": {
"Console": {
"IncludeScopes": true,
"LogLevel": {
"Default": "Trace"
}
}
},
"Client": {
"ServerAddress": "icerpc://localhost:10000",
"ConnectTimeout": "00:00:05"
},
"CertificateAuthoritiesFile": "cacert.der"
}
36 changes: 36 additions & 0 deletions examples/protobuf/GenericHost/GenericHost.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.4.33122.133
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Client", "Client\Client.csproj", "{BBF1199A-46A4-4AE9-AFFE-4D8DD59EB874}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server", "Server\Server.csproj", "{527EEA4D-77B9-4252-A2CD-C641A25CAD53}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F3B101B0-217D-4B27-9CA7-DA9CFDB2C47A}"
ProjectSection(SolutionItems) = preProject
README.md = README.md
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{BBF1199A-46A4-4AE9-AFFE-4D8DD59EB874}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BBF1199A-46A4-4AE9-AFFE-4D8DD59EB874}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BBF1199A-46A4-4AE9-AFFE-4D8DD59EB874}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BBF1199A-46A4-4AE9-AFFE-4D8DD59EB874}.Release|Any CPU.Build.0 = Release|Any CPU
{527EEA4D-77B9-4252-A2CD-C641A25CAD53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{527EEA4D-77B9-4252-A2CD-C641A25CAD53}.Debug|Any CPU.Build.0 = Debug|Any CPU
{527EEA4D-77B9-4252-A2CD-C641A25CAD53}.Release|Any CPU.ActiveCfg = Release|Any CPU
{527EEA4D-77B9-4252-A2CD-C641A25CAD53}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0B36F1E1-0592-4A15-9981-67BC4A653EC4}
EndGlobalSection
EndGlobal
23 changes: 23 additions & 0 deletions examples/protobuf/GenericHost/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# GenericHost

This example shows how to use dependency injection and the .NET Generic Host with IceRPC client and server applications.

You can build the client and server applications with:

``` shell
dotnet build
```

First start the Server program:

```shell
cd Server
dotnet run
```

In a separate window, start the Client program:

```shell
cd Client
dotnet run
```
21 changes: 21 additions & 0 deletions examples/protobuf/GenericHost/Server/Chatbot.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) ZeroC, Inc.

using IceRpc.Features;
using IceRpc.Protobuf;
using VisitorCenter;

namespace GenericHostServer;

/// <summary>A Chatbot is an IceRPC service that implements Protobuf service 'Greeter'.</summary>
[ProtobufService]
public partial class Chatbot : IGreeterService
{
public ValueTask<GreetResponse> GreetAsync(
GreetRequest message,
IFeatureCollection features,
CancellationToken cancellationToken)
{
Console.WriteLine($"Dispatching Greet request {{ name = '{message.Name}' }}");
return new(new GreetResponse { Greeting = $"Hello, {message.Name}!" });
}
}
63 changes: 63 additions & 0 deletions examples/protobuf/GenericHost/Server/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright (c) ZeroC, Inc.

using GenericHostServer;
using IceRpc;
using IceRpc.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Diagnostics;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using VisitorCenter;

// Configure the host.
IHostBuilder hostBuilder = Host.CreateDefaultBuilder(args)
// Set the content root path to the build directory of the server (e.g.: Server/bin/Debug/net8.0)
.UseContentRoot(AppContext.BaseDirectory)

// Configure the .NET Generic Host services.
.ConfigureServices((hostContext, services) =>
{
// Add the ServerHostedService to the hosted services of the .NET Generic Host.
services.AddHostedService<ServerHostedService>();
// The activity source used by the telemetry interceptor.
services.AddSingleton(sp => new ActivitySource("IceRpc"));
string workingDirectory = Directory.GetCurrentDirectory();
Console.WriteLine(workingDirectory);
// Bind the server options to the "appsettings.json" configuration "Server" section, and add a Configure
// callback to configure its authentication options.
services
.AddOptions<ServerOptions>()
.Bind(hostContext.Configuration.GetSection("Server"))
.Configure(options =>
{
options.ServerAuthenticationOptions = new SslServerAuthenticationOptions
{
ServerCertificate = new X509Certificate2(
Path.Combine(
hostContext.HostingEnvironment.ContentRootPath,
hostContext.Configuration.GetValue<string>("Certificate:File")!))
};
});
// Add the Slice service that implements Slice interface `Greeter`, as a singleton.
services.AddSingleton<IGreeterService, Chatbot>();
// Add a server and configure the dispatcher using a dispatcher builder. The server uses the ServerOptions
// provided by the IOptions<ServerOptions> singleton configured/bound above.
services.AddIceRpcServer(
builder => builder
.UseTelemetry()
.UseLogger()
.Map<IGreeterService>());
});

// Build the host.
using IHost host = hostBuilder.Build();

// Run hosted program.
host.Run();
34 changes: 34 additions & 0 deletions examples/protobuf/GenericHost/Server/Server.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<!-- Copy the PDBs from the NuGet packages to get file names and line numbers in stack traces. -->
<CopyDebugSymbolFilesFromPackages>true</CopyDebugSymbolFilesFromPackages>
</PropertyGroup>
<ItemGroup>
<None Remove="appsettings.json" />
</ItemGroup>
<ItemGroup>
<Content Include="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<None Include="..\..\..\..\certs\server.p12" Link="server.p12">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<ProtoFile Include="../proto/greeter.proto" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.*" />
<PackageReference Include="Microsoft.Extensions.Options.DataAnnotations" Version="8.0.*" />
<PackageReference Include="IceRpc.Protobuf.Tools" Version="$(Version)" PrivateAssets="All" />
<PackageReference Include="IceRpc.Protobuf" Version="$(Version)" />
<PackageReference Include="IceRpc.Extensions.DependencyInjection" Version="$(Version)" />
<PackageReference Include="IceRpc.Logger" Version="$(Version)" />
<PackageReference Include="IceRpc.Telemetry" Version="$(Version)" />
</ItemGroup>
</Project>
26 changes: 26 additions & 0 deletions examples/protobuf/GenericHost/Server/ServerHostedService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) ZeroC, Inc.

using IceRpc;
using Microsoft.Extensions.Hosting;

namespace GenericHostServer;

/// <summary>The server hosted service is ran and managed by the .NET Generic Host</summary>
public class ServerHostedService : IHostedService
{
// The IceRPC server accepts connections from IceRPC clients.
private readonly Server _server;

public ServerHostedService(Server server) => _server = server;

public Task StartAsync(CancellationToken cancellationToken)
{
// Start listening for client connections.
_server.Listen();
return Task.CompletedTask;
}

public Task StopAsync(CancellationToken cancellationToken) =>
// Shuts down the IceRPC server when the hosted service is stopped.
_server.ShutdownAsync(cancellationToken);
}
Loading

0 comments on commit 029358a

Please sign in to comment.