Skip to content

Commit

Permalink
feat: support .NET Framework 4.61+ using gRPC-Web (#426)
Browse files Browse the repository at this point in the history
Because .NET Framework does not support gRPC over HTTP/2, we fall back
to using gRPC for those older versions. To do this we:

Add another target framework for the library, net461. This is the
lowest .NET Framework version also supported by netstandard2.0.

Conditional on a net461 build, link the gRPC-Web client
dependency.

Conditional on a net461 build, use a gRPC-Web HttpHandler for
the control and data gRPC channels.

Update the test projects to exercise the net461 build as well.
  • Loading branch information
malandis authored Apr 27, 2023
1 parent e3cead4 commit 6b87c89
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 72 deletions.
24 changes: 12 additions & 12 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,17 @@ on:

jobs:
build_csharp:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- os: ubuntu-latest
target-framework: net6.0
- os: windows-latest
target-framework: net461
runs-on: ${{ matrix.os }}
env:
TEST_AUTH_TOKEN: ${{ secrets.ALPHA_TEST_AUTH_TOKEN }}
TEST_CACHE_NAME: client-sdk-dotnet-${{ github.sha }}
TEST_CACHE_NAME: client-sdk-dotnet-${{ github.sha }}-${{ matrix.target-framework }}

steps:
- name: Get current time
Expand Down Expand Up @@ -37,19 +44,13 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Build
run: |
dotnet build
shell: bash
run: dotnet build

- name: Unit Test
run: |
dotnet test tests/Unit/Momento.Sdk.Tests
shell: bash
run: dotnet test -f ${{ matrix.target-framework }} tests/Unit/Momento.Sdk.Tests

- name: Integration Test
run: |
dotnet test tests/Integration/Momento.Sdk.Tests
shell: bash
run: dotnet test -f ${{ matrix.target-framework }} tests/Integration/Momento.Sdk.Tests

build_examples:
runs-on: ubuntu-latest
Expand All @@ -75,7 +76,6 @@ jobs:
dotnet build MomentoApplication
dotnet run --project MomentoApplication
popd
shell: bash
- name: Send CI failure mail
if: ${{ steps.validation.outcome == 'failure' }}
Expand Down
23 changes: 12 additions & 11 deletions .github/workflows/on-push-to-main-branch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,17 @@ on:

jobs:
build_csharp:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- os: ubuntu-latest
target-framework: net6.0
- os: windows-latest
target-framework: net461
runs-on: ${{ matrix.os }}
env:
TEST_AUTH_TOKEN: ${{ secrets.ALPHA_TEST_AUTH_TOKEN }}
TEST_CACHE_NAME: client-sdk-dotnet-${{ github.sha }}
TEST_CACHE_NAME: client-sdk-dotnet-${{ github.sha }}-${{ matrix.target-framework }}

steps:
- name: Get current time
Expand All @@ -26,19 +33,13 @@ jobs:
dotnet-version: "6.0.x"

- name: Build
run: |
dotnet build
shell: bash
run: dotnet build

- name: Unit Test
run: |
dotnet test tests/Unit/Momento.Sdk.Tests
shell: bash
run: dotnet test -f ${{ matrix.target-framework }} tests/Unit/Momento.Sdk.Tests

- name: Integration Test
run: |
dotnet test tests/Integration/Momento.Sdk.Tests
shell: bash
run: dotnet test -f ${{ matrix.target-framework }} tests/Integration/Momento.Sdk.Tests

- name: Generate README
uses: momentohq/standards-and-practices/github-actions/generate-and-commit-oss-readme@gh-actions-v1
Expand Down
40 changes: 2 additions & 38 deletions .github/workflows/on-push-to-release-branch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,42 +29,9 @@ jobs:
id: release
run: echo "::set-output name=release::${{ steps.semrel.outputs.version }}"


test:
runs-on: ubuntu-latest
env:
TEST_AUTH_TOKEN: ${{ secrets.ALPHA_TEST_AUTH_TOKEN }}
TEST_CACHE_NAME: client-sdk-dotnet-${{ github.sha }}

steps:
- name: Get current time
uses: gerred/actions/current-time@master
id: current-time

- uses: actions/checkout@v3

- uses: actions/setup-dotnet@v1
with:
dotnet-version: "6.0.x"

- name: Build
run: |
dotnet build
shell: bash

- name: Unit Test
run: |
dotnet test tests/Unit/Momento.Sdk.Tests
shell: bash

- name: Integration Test
run: |
dotnet test tests/Integration/Momento.Sdk.Tests
shell: bash

publish:
runs-on: ubuntu-latest
needs: [release, test]
needs: release

steps:
- name: Get current time
Expand All @@ -78,9 +45,7 @@ jobs:
dotnet-version: "6.0.x"

- name: Build
run: |
dotnet build
shell: bash
run: dotnet build

- name: Pack and Publish
run: |
Expand All @@ -92,4 +57,3 @@ jobs:
dotnet pack -c Release -p:Version=${VERSION}
dotnet nuget push ./bin/Release/Momento.Sdk.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key=${{secrets.NUGET_API_KEY}}
popd
shell: bash
12 changes: 11 additions & 1 deletion src/Momento.Sdk/Internal/ControlGrpcManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Net.Client;
#if USE_GRPC_WEB
using System.Net.Http;
using Grpc.Net.Client.Web;
#endif
using Microsoft.Extensions.Logging;
using Momento.Protos.ControlClient;
using Momento.Sdk.Config.Middleware;
Expand Down Expand Up @@ -79,7 +83,13 @@ internal sealed class ControlGrpcManager : IDisposable
public ControlGrpcManager(ILoggerFactory loggerFactory, string authToken, string endpoint)
{
var uri = $"https://{endpoint}";
this.channel = GrpcChannel.ForAddress(uri, new GrpcChannelOptions() { Credentials = ChannelCredentials.SecureSsl });
this.channel = GrpcChannel.ForAddress(uri, new GrpcChannelOptions()
{
Credentials = ChannelCredentials.SecureSsl,
#if USE_GRPC_WEB
HttpHandler = new GrpcWebHandler(new HttpClientHandler())
#endif
});
List<Header> headers = new List<Header> { new Header(name: Header.AuthorizationKey, value: authToken), new Header(name: Header.AgentKey, value: version), new Header(name: Header.RuntimeVersionKey, value: runtimeVersion) };
CallInvoker invoker = this.channel.CreateCallInvoker();

Expand Down
7 changes: 7 additions & 0 deletions src/Momento.Sdk/Internal/DataGrpcManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Net.Client;
#if USE_GRPC_WEB
using System.Net.Http;
using Grpc.Net.Client.Web;
#endif
using Microsoft.Extensions.Logging;
using Momento.Protos.CacheClient;
using Momento.Protos.CachePing;
Expand Down Expand Up @@ -230,6 +234,9 @@ internal DataGrpcManager(IConfiguration config, string authToken, string host)
channelOptions.LoggerFactory = config.LoggerFactory;
}
channelOptions.Credentials = ChannelCredentials.SecureSsl;
#if USE_GRPC_WEB
channelOptions.HttpHandler = new GrpcWebHandler(new HttpClientHandler());
#endif

this.channel = GrpcChannel.ForAddress(url, channelOptions);
List<Header> headers = new List<Header> { new Header(name: Header.AuthorizationKey, value: authToken), new Header(name: Header.AgentKey, value: version), new Header(name: Header.RuntimeVersionKey, value: runtimeVersion) };
Expand Down
14 changes: 13 additions & 1 deletion src/Momento.Sdk/Momento.Sdk.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Build Configuration -->
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<!-- Include documentation in build -->
Expand All @@ -29,6 +29,14 @@
<PackageProjectUrl>https://github.com/momentohq/client-sdk-dotnet</PackageProjectUrl>
<RepositoryUrl>https://github.com/momentohq/client-sdk-dotnet</RepositoryUrl>
</PropertyGroup>

<!-- Because .NET Framework doesn't support HTTP/2, we use gRPC-Web over HTTP/1.1.
Dependency resolution: .NET Framework binaries or libraries >= v4.61 that link
to Momento.Sdk will link to the .NET Framework 4.61 build. -->
<PropertyGroup Condition="'$(TargetFramework)' == 'net461' ">
<DefineConstants>USE_GRPC_WEB</DefineConstants>
</PropertyGroup>

<ItemGroup>
<None Remove="System.Threading.Channels" />
<None Remove="Internal\Middleware\" />
Expand Down Expand Up @@ -56,6 +64,10 @@
</PackageReference>
<PackageReference Include="System.Threading" Version="4.3.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
<!-- Because .NET Framework does not support HTTP/2, we fall back to gRPC-Web over HTTP/1.1 -->
<PackageReference Include="Grpc.Net.Client.Web" Version="2.52.0" />
</ItemGroup>
<ProjectExtensions>
<MonoDevelop>
<Properties>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFrameworks>net6.0;net461</TargetFrameworks>

<IsPackable>false</IsPackable>
<Nullable>enable</Nullable>
<AssemblyName>Momento.Sdk.Tests</AssemblyName>
<LangVersion>latest</LangVersion>
<!-- We get some warnings for the logger (used only in the tests) on net461.
These are safe to ignore -->
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<!-- Because this assembly targets multiple versions, and the test runner runs
the target frameworks sequentially, we get spurious warnings about multiple
versions of dependencies -->
<NoWarn>MSB3277</NoWarn>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.0.2">
<PackageReference Include="coverlet.collector" Version="3.2.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
16 changes: 12 additions & 4 deletions tests/Unit/Momento.Sdk.Tests/Momento.Sdk.Tests.Unit.csproj
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFrameworks>net6.0;net461</TargetFrameworks>

<IsPackable>false</IsPackable>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<!-- We get some warnings for the logger (used only in the tests) on net461.
These are safe to ignore -->
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<!-- Because this assembly targets multiple versions, and the test runner runs
the target frameworks sequentially, we get spurious warnings about multiple
versions of dependencies -->
<NoWarn>MSB3277</NoWarn>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.0.2">
<PackageReference Include="coverlet.collector" Version="3.2.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down

0 comments on commit 6b87c89

Please sign in to comment.