European ASP.NET 4.5 Hosting BLOG

BLOG about ASP.NET 4, ASP.NET 4.5 Hosting and Its Technology - Dedicated to European Windows Hosting Customer

European ASP.NET Core 3.1 Hosting - HostForLIFE.eu :: .NET Core Implementing .NET Core Health Checks

clock October 29, 2019 12:16 by author Peter

Generally, when we are using any uptime monitoring systems or load balancers, these systems will keep monitoring the health of the application and based on its health condition it will decide to send the request to serve it. For this earlier, we use to create a special endpoint where it will return any error message or code to indicate the health of the API/service.
 

Following is the sample, an endpoint /health where it verifying the database connection and returns the result accordingly.
    [Route("health")] 
    public ActionResult Health() 
    { 
        using (var connection = new SqlConnection(_connectionString)) 
        { 
            try 
            { 
                connection.Open(); 
            } 
            catch (SqlException) 
            { 
                return new HttpStatusCodeResult(503, "Database connection is unhealthy"); 
            } 
        } 
     
        return new EmptyResult(); 
    } 

When we ran the application with endpoint /health, it will display an empty message with 200 status code and 503 status code when there is any connectivity issue while connecting to the database.

Now, based on these resulted status codes, monitoring systems can take appropriate actions like removing this particular services instance from its list of healthy services so that no requests will be redirected to it until it becomes healthy (in our case, when database connectivity issue resolves).
 
We need to keep adding more external resource health checks accordingly.
 
Since .NET Core 2.2, we no need to add a special controller for health check endpoint, instead, the framework itself providing Health Check services as follows.
 
NuGet Package
You have to install following NuGet package
 
Install-Package Microsoft.Extensions.Diagnostics.HealthChecks
 
Once the package is installed, we need to add the following lines at ConfigureServices() and Configure() methods in Startup.cs file.
    public void ConfigureServices(IServiceCollection services) 
    { 
        services.AddHealthChecks(); 
    } 
      
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 
    { 
        app.UseHealthChecks("/Health"); 
    } 


As observed, we are providing the endpoint name in Configure() method. These lines of code will enable a dynamic endpoint "/Health" and display either Healthy/UnHealthy results based on its health state.
 
But, where can we write our custom logic to replicate the above? Yes, we have many features to customize our needs from logic to display results.
 
Adding Custom Logic
 
We can perform in two ways the following are those.
 
Option 1
 
In ConfigureServices method,
    public void ConfigureServices(IServiceCollection services) { 
     services.AddHealthChecks() 
      .AddCheck("sql", () => { 
     
       using(var connection = new SqlConnection(_connectionString)) { 
        try { 
         connection.Open(); 
        } catch (SqlException) { 
         return HealthCheckResult.Unhealthy(); 
        } 
       } 
     
       return HealthCheckResult.Healthy(); 
     
      }); 
    } 


Here, we can use an anonymous method to write the custom logic using AddCheck() method. This will expect a HealthCheckResult object as a result. This object will contain 3 options,
    Healthy
    Unhealthy
    Degraded

Based on the result we need to return appropriately so that runtime will return the status code accordingly. For example, in the above code, if database connection passes it will return a 200 status code (Healthy) and 503 status code (Unhealthy) if failed.
 
Option 2 - In a separate class
The class should implement an IHealthCheck interface and implement CheckHealthAsync() method as follows,
    public class DatabaseHealthCheck: IHealthCheck { 
     public Task < HealthCheckResult > CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = 
      default) { 
      using(var connection = new SqlConnection(_connectionString)) { 
       try { 
        connection.Open(); 
       } catch (SqlException) { 
        return HealthCheckResult.Healthy(); 
       } 
      } 
     
      return HealthCheckResult.Healthy(); 
     
     } 
    } 


Once we created the class, we need to mention this class in ConfigureServices() method using AddTask<T> method as follows by giving some valid unique names.
    public void ConfigureServices(IServiceCollection services) 
    { 
         services.AddControllers(); 
     
         services.AddHealthChecks() 
              .AddCheck<DatabaseHealthCheck>("sql"); 
    } 


Now, our code is clean and we can add any number of Health Tasks as above and it will be run in the order how we declared here.
 
Custom Status Code

As discussed above, by default it will send 200 status code if we return Healthy and 503 for Unhealthy. The Healthcheck service even provides scope for us to change this default behavior by providing custom status code using its options object as follows.
    var options = new HealthCheckOptions(); 
    options.ResultStatusCodes[HealthStatus.Unhealthy] = 420; 
    app.UseHealthChecks("/Health", options); 


In this example, I replace the status code for the Unhealthy state with 420.
 
Custom Response
The beauty of this tool is, we can even customize our output for more clear detailed information about each Health check task. This will be very useful in case we have multiple health check tasks to analyze which task made the complete service heath status to Unhealthy.
 
We can achieve this via HealthCheckOptions ResponseWriter property.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { 
     app.UseHealthChecks("/Health", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions() { 
      ResponseWriter = CustomResponseWriter 
     }); 
    } 
     
    private static Task CustomResponseWriter(HttpContext context, HealthReport healthReport) { 
     context.Response.ContentType = "application/json"; 
     
     var result = JsonConvert.SerializeObject(new { 
      status = healthReport.Status.ToString(), 
       errors = healthReport.Entries.Select(e => new { 
        key = e.Key, value = e.Value.Status.ToString() 
       }) 
     }); 
     return context.Response.WriteAsync(result); 
     
    } 


Now, the above code will display the result in JSON format will all the tasks information. Here, Key represents the Task name we have given (in our case "sql") and value is either Healthy/Unhealthy.
 
Health Check UI
We can even see the health check results on-screen visually by installing the following NuGet package
Install-Package AspNetCore.HealthChecks.UI
 
Once installed need to call respective service methods in ConfigureServices() and Configure() methods accordingly.
    public void ConfigureServices(IServiceCollection services) 
    { 
        services.AddHealthChecksUI(); 
    } 
     
    public void Configure(IApplicationBuilder app, IHostingEnvironment env) 
    { 
        app.UseHealthChecksUI(); 
    } 

Once configured, you can run the application and point to /healthchecks-ui endpoint which display a UI as follows,



European ASP.NET Core 3 Hosting - HostForLIFE.eu :: ASP.NET Core Denial of Service Vulnerability

clock October 22, 2019 12:09 by author Peter

In this blog, we are going to discuss vulnerable versions of .Net Core. Microsoft releases the information about security breaches in ASP.Net Core. It informs developers which version they need to update to remove this vulnerability. Microsoft is aware of DOS Attack in the OData library. If you are using OData library in your application in the sense attacker can exploit.

We have two types of dependencies in .net core,

  • Direct dependencies
  • transitive dependencies

Direct dependencies are dependencies where you specifically add a package to your project, transitive dependencies occur when you add a package to your project that in turn relies on another package.

Mitigation policy
Open your application through visual studio and go to package manager console and run the below command.
command :-  dotnet --info 

By running the above command you will come to know which package we need to update as per Microsoft security guidelines.
 
Direct dependencies
By editing your Cs.proj file we can fix the issue or we can update Nuget Package manager.
 
Transitive dependencies
Transitive dependencies occur when any vulnerable package is referring or relies on another package. By examining the project.asset.json file you can fix the issue.

In this blog, we have discussed vulnerable versions of .Net Core. As per Microsoft security advice it is better to update packages which are in your application.



European ASP.NET Core 3 Hosting :: How to Enable GRPC Compression in ASP.NET Core 3

clock October 22, 2019 09:41 by author Scott

How Do I Enable Response Compression with GRPC?

There are two main approaches that I’ve found so far to enable the compression of gRPC responses. You can either configure this at the server level so that all gRPC services apply compression to responses, or at a per-service level.

Server Level Options

services.AddGrpc(o =>
{
    o.ResponseCompressionLevel = CompressionLevel.Optimal;
    o.ResponseCompressionAlgorithm = "gzip";
});

When registering the gRPC services into the dependency injection container with the AddGrpc method inside ConfigureServices, it’s possible to set properties on the GrpcServiceOptions. At this level, the options affect all gRPC services which the server implements.

Using the overload of the AddGrpc extension method, we can supply an Action<GrpcServiceOptions>. In the above snippet we’ve set the compression algorithm to “gzip”. We can also optionally control the CompressionLevel which trades the time needed to compress the data against the final size that is achieved by compression. If not specified the current implementation defaults to using CompressionLevel.Fastest. In the preceding snippet, we’ve chosen to allow more time for the compression to reduce the bytes to the smallest possible size.

Service Level Options

services.AddGrpc()
    .AddServiceOptions<WeatherService>(o =>
        {
            o.ResponseCompressionLevel = CompressionLevel.Optimal;
            o.ResponseCompressionAlgorithm = "gzip";
        });

After calling AddGrpc, an IGrpcServerBuilder is returned. We can call an extension method on that builder called AddServiceOptions to provide per service options. This method is generic and accepts the type of the gRPC service that the options should apply.

In the preceding example, we have decided to provide options specifically for calls that are handled by the WeatherService implementation. The same options are available at this level as we discussed for the server level configuration. In this scenario, if we mapped other gRPC services within this server, they would not receive the compression options.

Making Requests from A GRPC Client

Now that response compression is enabled, we need to ensure our requests state that our client accepts compressed content. In fact, this is enabled by default when using a GrpcChannel created using the ForAddress method so we have nothing to do in our client code.

var channel = GrpcChannel.ForAddress("https://localhost:5005");

Channels created in this way already send a “grpc-accept-encoding” header which includes the gzip compression type. The server reads this header and determines that the client allows gzipped responses to be returned.

One way to visualise the effect of compression is to enable trace level logging for our application while in development. We can achieve this by modifying the appsettings.Development.json file as follows:

{
  "Logging": {
    "LogLevel": {
        "Default": "Debug",
        "System": "Information",
        "Grpc": "Trace",
        "Microsoft": "Trace"
    }
  }
}

When running our server, we now get much more verbose console logging.

info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
      Executing endpoint 'gRPC - /WeatherForecast.WeatherForecasts/GetWeather'
dbug: Grpc.AspNetCore.Server.ServerCallHandler[1]
      Reading message.
dbug: Microsoft.AspNetCore.Server.Kestrel[25]
      Connection id "0HLQB6EMBPUIA", Request id "0HLQB6EMBPUIA:00000001": started reading request body.
dbug: Microsoft.AspNetCore.Server.Kestrel[26]
      Connection id "0HLQB6EMBPUIA", Request id "0HLQB6EMBPUIA:00000001": done reading request body.
trce: Grpc.AspNetCore.Server.ServerCallHandler[3]
      Deserializing 0 byte message to 'Google.Protobuf.WellKnownTypes.Empty'.
trce: Grpc.AspNetCore.Server.ServerCallHandler[4]
      Received message.
dbug: Grpc.AspNetCore.Server.ServerCallHandler[6]
      Sending message.
trce: Grpc.AspNetCore.Server.ServerCallHandler[9]
      Serialized 'WeatherForecast.WeatherReply' to 2851 byte message.
trce: Microsoft.AspNetCore.Server.Kestrel[37]
      Connection id "0HLQB6EMBPUIA" sending HEADERS frame for stream ID 1 with length 104 and flags END_HEADERS
trce: Grpc.AspNetCore.Server.ServerCallHandler[10]
      Compressing message with 'gzip' encoding.
trce: Grpc.AspNetCore.Server.ServerCallHandler[7]
      Message sent.
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
      Executed endpoint 'gRPC - /WeatherForecast.WeatherForecasts/GetWeather'
trce: Microsoft.AspNetCore.Server.Kestrel[37]
      Connection id "0HLQB6EMBPUIA" sending DATA frame for stream ID 1 with length 978 and flags NONE
trce: Microsoft.AspNetCore.Server.Kestrel[37]
      Connection id "0HLQB6EMBPUIA" sending HEADERS frame for stream ID 1 with length 15 and flags END_STREAM, END_HEADERS
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished in 2158.9035ms 200 application/grpc

On line 16 of this log, we can see that the WeatherReply, essentially an array of 100 WeatherData items in this sample, has been serialised to protocol buffers and has a size of 2851 bytes.

Later, in line 20, we can see that the message has been compressed with gzip encoding and on line 26, we can see the size of the data frame for this call which is 978 bytes. The data, in this case, has compressed quite well (66% reduction) because the repeated WeatherData items contain text and many of the values repeat within the message.

In this example, gzip compression has a good effect on the over the wire size of the data.

Disable Response Compression within A Service Method Implementation

It’s possible to control the response compression on a per-method basis. At this time, I’ve only found a way to do this on an opt-out approach. When compression is enabled for a service or server, we can opt-out of compression within the service method implementation.

Let’s look at the server log output when calling a service method which streams WeatherData messages from the server.

info: WeatherForecast.Grpc.Server.Services.WeatherService[0]
      Sending WeatherData response
dbug: Grpc.AspNetCore.Server.ServerCallHandler[6]
      Sending message.
trce: Grpc.AspNetCore.Server.ServerCallHandler[9]
      Serialized 'WeatherForecast.WeatherData' to 30 byte message.
trce: Grpc.AspNetCore.Server.ServerCallHandler[10]
      Compressing message with 'gzip' encoding.
trce: Microsoft.AspNetCore.Server.Kestrel[37]
      Connection id "0HLQBMRRH10JQ" sending DATA frame for stream ID 1 with length 50 and flags NONE
trce: Grpc.AspNetCore.Server.ServerCallHandler[7]
      Message sent.

On line 6, we can see that an individual WeatherData message is 30 bytes in size. On line 8, this gets compressed, and on line 10, we can see that the data length is now 50 bytes, larger than the original message. In this case, there is no gain from gzip compression, and we see an increase in the overall message size sent over the wire.

We can avoid compression for a particular message by setting the WriteOptions for the call within the service method.

public override async Task GetWeatherStream(Empty _, IServerStreamWriter<WeatherData> responseStream, ServerCallContext context)
{
    context.WriteOptions = new WriteOptions(WriteFlags.NoCompress);

    // implementation of the method which writes to the stream
}

At the top of our service method, we can set the WriteOptions on the ServerCallContext. We pass in a new WriteOptions instance which the WriteFlags value set to NoCompress. These write options are used for the next write.

With streaming responses, it’s also possible to set this value on the IServerStreamWriter.

public override async Task GetWeatherStream(Empty _, IServerStreamWriter<WeatherData> responseStream, ServerCallContext context)
{   
    responseStream.WriteOptions = new WriteOptions(WriteFlags.NoCompress);

    // implementation of the method which writes to the stream
}

When this option is applied, the logs now show that compression for calls to this service method is not applied.

info: WeatherForecast.Grpc.Server.Services.WeatherService[0]
      Sending WeatherData response
dbug: Grpc.AspNetCore.Server.ServerCallHandler[6]
      Sending message.
trce: Grpc.AspNetCore.Server.ServerCallHandler[9]
      Serialized 'WeatherForecast.WeatherData' to 30 byte message.
trce: Microsoft.AspNetCore.Server.Kestrel[37]
      Connection id "0HLQBMTL1HLM8" sending DATA frame for stream ID 1 with length 35 and flags NONE
trce: Grpc.AspNetCore.Server.ServerCallHandler[7]
      Message sent.

Now the 30 byte message has a length of 35 bytes in the DATA frame. There is a small overhead which accounts for the extra 5 bytes which we don’t need to concern ourselves with here.

Disable Response Compression from A GRPC Client

By default, the gRPC channel includes options that control which encodings it accepts. It is possible to configure these when creating the channel if you wish to disable compression of responses from your client. Generally, I would avoid this and let the server decide what to do, since it knows best what can and cannot be compressed. That said, you may sometimes need to control this from the client.

The only way I’ve found to do this in my exploration of the API to date is to configure the channel by passing in a GrpcChannelOptions instance. One of the properties on this options class is for the CompressionProviders, an IList<ICompressionProvider>. By default, when this is null, the client implementation adds the Gzip compression provider for you automatically. This means that the server can choose to gzip the response message(s) as we have already seen.

private static async Task Main()
{
    using var channel = GrpcChannel.ForAddress("https://localhost:5005", new GrpcChannelOptions { CompressionProviders = new List<ICompressionProvider>() });

    var client = new WeatherForecastsClient(channel);

    var reply = await client.GetWeatherAsync(new Empty());

    foreach (var forecast in reply.WeatherData)
    {
        Console.WriteLine($"{forecast.DateTimeStamp.ToDateTime():s} | {forecast.Summary} | {forecast.TemperatureC} C");
    }

    Console.WriteLine("Press a key to exit");
    Console.ReadKey();
}

In this sample client code, we establish the GrpcChannel and pass in a new instance of GrpcChannelOptions. We set the CompressionProviders property to an empty list. Since we now specify no providers in our channel, when the calls are created and sent via this channel, they won’t include any compression encodings in the “grpc-accept-encoding” header. The server acknowledges this and not apply gzip compression to the response.

Summary

In this post, we’ve explored the possibility of compressing response messages from a gRPC server. We’ve identified that in some cases, but crucially not all, this may result in smaller payloads. We’ve seen that by default clients calls include the gzip “grpc-accept-encoding” value in the headers. If the server is configured to apply compression, it only does so if a supported encoding type is matched from the request header.

We can configure the GrpcChannelOptions when creating a channel for the client, to disable the inclusion of the gzip compression encoding. On the server, we can configure the whole server, or a specific service to enable compression for responses. We can override and disable that on a per service-method level as well.



About HostForLIFE.eu

HostForLIFE.eu is European Windows Hosting Provider which focuses on Windows Platform only. We deliver on-demand hosting solutions including Shared hosting, Reseller Hosting, Cloud Hosting, Dedicated Servers, and IT as a Service for companies of all sizes.

We have offered the latest Windows 2016 Hosting, ASP.NET Core 2.2.1 Hosting, ASP.NET MVC 6 Hosting and SQL 2017 Hosting.


Tag cloud

Sign in