How to detect a connection closure in Kestrel

Viewed 800

Once again, the Microsoft documentation leaves me wanting. I am trying to find the correct API where I can configure a callback to trap when a client closes their connection.

When I fire up a gRPC server, in the console I see all of the Kestrel configuration and startup log messages. When I fire up the gRPC client I can see in the server's console log messages indicating a connection has been made as follows:

dbug: Microsoft.AspNetCore.Server.Kestrel.Connections[39]
      Connection id "0HMCEE5LHGSKR" accepted.
dbug: Microsoft.AspNetCore.Server.Kestrel.Connections[1]
      Connection id "0HMCEE5LHGSKR" started.

When I close the client by clicking the Close Window button (X), I see the following:

dbug: Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets[19]
      Connection id "0HMCEE5LHGSKR" reset.
dbug: Microsoft.AspNetCore.Server.Kestrel.Http2[48]
      Connection id "0HMCEE5LHGSKR" is closed. The last processed stream ID was 1.
dbug: Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets[7]
      Connection id "0HMCEE5LHGSKR" sending FIN because: "The client closed the connection."
dbug: Microsoft.AspNetCore.Server.Kestrel.Connections[2]
      Connection id "0HMCEE5LHGSKR" stopped.

The option to use the ListenOptions.UseConnectionLogging(ListenOptions) extension method provides no callback option that I can find. Obviously, in the default middleware, the event is being captured, but I cannot find the path to that option. An examination of Microsoft.AspNetCore.Server.Kestrel namespace shows no way (that I can find) how to get to Microsoft.AspNetCore.Server.Kestrel.Connections or Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets data.

I am using Visual Studio 2022, .NET 6, C# 10 and gRPC. Here is my current Kestrel configuration:

// Configure Kestrel, the .NET Core web server.
var hostBuilder = webHostBuilder.ConfigureKestrel (kestrelServerOptions => {

    kestrelServerOptions.ConfigureHttpsDefaults (httpsConnectionAdapterOptions => httpsConnectionAdapterOptions.SslProtocols = SslProtocols.Tls12);

    // Read in the X.509 certificate file.
    var certPath = Path.Combine (builder.Environment.ContentRootPath, "Certs", $"{environment}.pfx");

    kestrelServerOptions.ConfigureEndpointDefaults (listenOptions => {

        _ = listenOptions.UseHttps (certPath, password);

        logger.Debug ($"Using {certPath} as the cert file.");
        logger.Debug ("Configuring host to use HTTP/2 protocol.");

        listenOptions.Protocols = HttpProtocols.Http2;
    });

    logger.Debug ("Reading config values for the server name and port.");

    // Get the host name and port number to bind the service to.
    var port = builder.Configuration.GetValue<int> ("AppSettings:OperationsServerPort");
    var address = IPAddress.Parse ("0.0.0.0");

    if (address != null) {
        logger.Debug ($"Host will listen at https://{address}:{port}");

        kestrelServerOptions.Listen (address, port);
    } else {
        logger.Error ("DNS address for service host cannot be determined!  Exiting...");

        Environment.Exit (-1);
    }
});

Any clues, guidance, examples will be greatly appreciated!

1 Answers

Well, I may be late to the game in understanding all of this ASP.NET Core configuration, but to trap connections coming and going is tooooooo simple... Adding a middleware delegate to the listener is all it took...

var ipEndpoint = new IPEndPoint (address, port);

kestrelServerOptions.Listen (ipEndpoint, action => action.Use (async (context, next) => {

    Console.WriteLine ($"INTERNAL ---------------- New Connection: {context.ConnectionId}");

    await next.Invoke ();

    Console.WriteLine ($"INTERNAL ---------------- Connection terminated: {context.ConnectionId}");
}));

This snippet modified my original post above by adding the middleware delegate. The required reading that got me beyond the logjam can be found here: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-5.0

I hope this helps somebody!!

Related