grpc / grpc/grpc-dotnet

.NET 6 Client Intermittent issue crashes client

Open
#2,200 5 comments 1 reaction 0 assignees View on GitHub
question
Dominant language
C#
Stars
4.5k
Forks
836
Avg merge
6d 3h
Merged PRs (30d)
7

Description

I'm getting an intermittent error that is causing my one client to crash. This client is running as a background service calling the gRPC server using Parallel.ForEach. This client is also writing to request streams and subscribing to response streams.

Client Exception:
```
Grpc.Core.RpcException: Status(StatusCode="Unavailable", Detail="Error starting gRPC call. HttpRequestException: An error occurred while sending the request. IOException: The request was aborted. IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.. SocketException: An existing connection was forcibly closed by the remote host.", DebugException="System.Net.Http.HttpRequestException: An error occurred while sending the request.")
---> System.Net.Http.HttpRequestException: An error occurred while sending the request.
---> System.IO.IOException: The request was aborted.
---> System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host..
---> System.Net.Sockets.SocketException (10054): An existing connection was forcibly closed by the remote host.
--- End of inner exception stack trace ---
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token)
at System.Net.Security.SslStream.EnsureFullTlsFrameAsync[TIOAdapter](TIOAdapter adapter)
at System.Net.Security.SslStream.ReadAsyncInternal[TIOAdapter](TIOAdapter adapter, Memory`1 buffer)
at System.Net.Http.Http2Connection.ProcessIncomingFramesAsync()
--- End of inner exception stack trace ---
at System.Net.Http.Http2Connection.ThrowRequestAborted(Exception innerException)
at System.Net.Http.Http2Connection.Http2Stream.CheckResponseBodyState()
at System.Net.Http.Http2Connection.Http2Stream.TryEnsureHeaders()
at System.Net.Http.Http2Connection.Http2Stream.ReadResponseHeadersAsync(CancellationToken cancellationToken)
at System.Net.Http.Http2Connection.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at System.Net.Http.Http2Connection.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at Grpc.Net.Client.Balancer.Internal.BalancerHttpHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at Grpc.Net.Client.Internal.GrpcCall`2.RunCall(HttpRequestMessage request, Nullable`1 timeout)
--- End of inner exception stack trace ---
at Grpc.Net.Client.Internal.GrpcCall`2.GetResponseHeadersCoreAsync()
```

Server code:
```
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseKestrel(options =>
{
options.Limits.Http2.MaxFrameSize = 16777215;
options.ListenAnyIP(portNumber, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
listenOptions.UseHttps(httpsOptions =>
{
// get certificate
});
});
});
webBuilder.UseStartup();
});
```

Client Code:
```
private void BuildGrpcNetClientChannel()
{
var credentials = CallCredentials.FromInterceptor(AddTokenToHeaderAsync);
var channelOptions = new GrpcChannelOptions
{
MaxReceiveMessageSize = null,
MaxSendMessageSize = null,
HttpHandler = GetHttpMessageHandler(),
Credentials = ChannelCredentials.Create(ChannelCredentials.SecureSsl, credentials)
};

GrpcChannel = GrpcChannel.ForAddress($"https://{ServerAddress}:{ServerPort}", channelOptions);
}

private HttpMessageHandler GetHttpMessageHandler()
{
return new SocketsHttpHandler()
{
PooledConnectionIdleTimeout = TimeSpan.FromMinutes(1),
SslOptions = new SslClientAuthenticationOptions()
{
RemoteCertificateValidationCallback = (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) =>
{
// cert validation
}
},
};
}

private async Task AddTokenToHeaderAsync(AuthInterceptorContext context, Metadata metadata)
{
if (!string.IsNullOrEmpty(_token))
{
try
{
await _semaphore.WaitAsync(context.CancellationToken);

if (DateTime.UtcNow >= _tokenExpiration)
{
// call to server to refresh token
_token = await RefreshTokensAsync(context.CancellationToken);
}

metadata.Add("Authorization", $"Bearer {_token}");
}
catch (OperationCanceledException)
{
// do nothing
}
catch (Exception ex)
{
log.Fatal(ex.ToString());
}
finally
{
_semaphore.Release();
}
}
}
```

Any help would be appreciated. Every time I think I fix it, the problem shows up in a few weeks.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.