Cysharp / GrpcWebSocketBridge

Yet Another gRPC over HTTP/1 using WebSocket implementation, primarily targets .NET platform.
MIT License
77 stars 12 forks source link

Error: The underlying HTTP transport must be a SocketsHttpHandler #27

Open truegoodwill opened 6 months ago

truegoodwill commented 6 months ago

This exception is thrown when creating the channel:

System.InvalidOperationException: Channel is configured with an HTTP transport doesn't support client-side load balancing or connectivity state tracking. The underlying HTTP transport must be a SocketsHttpHandler with no SocketsHttpHandler.ConnectCallback configured. The HTTP transport must be configured on the channel using GrpcChannelOptions.HttpHandler.
   at Grpc.Net.Client.GrpcChannel.ValidateHttpHandlerSupportsConnectivity()
   at Grpc.Net.Client.GrpcChannel..ctor(Uri address, GrpcChannelOptions channelOptions)
   at Grpc.Net.Client.GrpcChannel.ForAddress(Uri address, GrpcChannelOptions channelOptions)
   at Grpc.Net.Client.GrpcChannel.ForAddress(String address, GrpcChannelOptions channelOptions)
   at Program.<Main>$(String[] args) in D:\FFT\Program.cs:line 24

Link to the source that is throwing the exception: https://github.com/grpc/grpc-dotnet/blob/master/src/Grpc.Net.Client/GrpcChannel.cs#L411

To reproduce use this Program.cs and .csproj

using Grpc.Core;
using Grpc.Net.Client;
using Grpc.Net.Client.Balancer;
using Grpc.Net.Client.Configuration;
using GrpcWebSocketBridge.Client;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

var services = new ServiceCollection();
services.AddSingleton<ResolverFactory>(new WSSResolverFactory());

using var channel = GrpcChannel.ForAddress("wss://myserver.com:443", new GrpcChannelOptions
{
  HttpHandler = new GrpcWebSocketBridgeHandler(),
  ServiceProvider = services.BuildServiceProvider(),
  Credentials = ChannelCredentials.SecureSsl,
});

internal class WSSResolverFactory : ResolverFactory
{
  public override string Name => "wss";

  public override Resolver Create(ResolverOptions options)
  {
    return new WSSResolver(options.Address, options.DefaultPort, options.LoggerFactory);
  }
}

internal class WSSResolver : PollingResolver
{
  private readonly Uri _address;
  private readonly int _port;

  public WSSResolver(Uri address, int defaultPort, ILoggerFactory loggerFactory)
      : base(loggerFactory)
  {
    _address = address;
    _port = defaultPort;
  }

  protected override async Task ResolveAsync(CancellationToken cancellationToken)
  {
    var address = new BalancerAddress(_address.Host, _port);
    Listener(ResolverResult.ForResult(new[] { address }));
  }
}
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Google.Protobuf" Version="3.26.0" />
    <PackageReference Include="Grpc.Core" Version="2.46.6" />
    <PackageReference Include="Grpc.Net.Client" Version="2.61.0" />
    <PackageReference Include="Grpc.Tools" Version="2.62.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="GrpcWebSocketBridge.Client" Version="1.2.2" />
    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
  </ItemGroup>
</Project>
truegoodwill commented 6 months ago

Thank you, as usual, I love using your packages. Great work always :)

mayuki commented 5 months ago

The exception seems to be thrown when the address passed to GrpcChannel is not http:// or https://. GrpcWebSocketBridge expects http/https scheme, so please specify https instead of wss.

truegoodwill commented 5 months ago

I did try that. The remote host refused the connection.

mayuki commented 5 months ago

Can you paste a detailed exception message or stack trace?