# Cadente

Source: https://docs.sisk-framework.org/es/docs/cadente.html

Cadente es una implementación experimental gestionada de un listener HTTP/1.1 para Sisk. Sirve como reemplazo del `System.Net.HttpListener` predeterminado, ofreciendo mayor control y flexibilidad, especialmente en plataformas que no son Windows.

## Visión general

Por defecto, Sisk utiliza `HttpListener` (de `System.Net`) como su motor subyacente de servidor HTTP. Mientras que `HttpListener` es estable y de buen rendimiento en Windows (donde usa el controlador kernel‑mode HTTP.sys), su implementación en Linux y macOS es gestionada y históricamente ha tenido limitaciones, como la falta de soporte nativo de SSL (requiriendo un proxy inverso como Nginx) y características de rendimiento variables.

Cadente pretende resolver estos problemas proporcionando un servidor HTTP/1.1 totalmente gestionado escrito en C#. Sus objetivos principales son:

- **Soporte nativo de SSL:** Funciona en todas las plataformas sin necesidad de proxies externos ni configuraciones complejas.
- **Consistencia multiplataforma:** Comportamiento idéntico en Windows, Linux y macOS.
- **Rendimiento:** Diseñado como una alternativa de alto rendimiento al `HttpListener` gestionado.
- **Independencia:** Desacoplado de `System.Net.HttpListener`, aislando a Sisk de posibles deprecaciones futuras o falta de mantenimiento de ese componente en .NET.

> [!WARNING]
> **Estado experimental**
> 
> Cadente se encuentra actualmente en una fase experimental (Beta). No se recomienda aún para entornos de producción críticos. La API y el comportamiento pueden cambiar.

## Instalación

Cadente está disponible como un paquete separado. Para usarlo con Sisk, necesitas el paquete `Sisk.Cadente.CoreEngine`.

```bash
dotnet add package Sisk.Cadente.CoreEngine
```

`Sisk.Cadente` y `Sisk.Cadente.CoreEngine` apuntan a .NET 9 y .NET 10. No pueden usarse en proyectos que apunten a .NET 8, aunque `Sisk.HttpServer` lo soporte.

## Uso con Sisk

Para usar Cadente como motor HTTP de tu aplicación Sisk, debes configurar el `HttpServer` para que utilice `CadenteHttpServerEngine` en lugar del motor predeterminado.

El `CadenteHttpServerEngine` adapta el `HttpHost` de Cadente a la abstracción `HttpServerEngine` requerida por Sisk.

```csharp
using Sisk.Core.Http;
using Sisk.Cadente.CoreEngine;

using var host = HttpServer.CreateBuilder()
    .UseEngine<CadenteHttpServerEngine>()
    .UseSsl(certificate: CertificateHelper.CreateTrustedDevelopmentCertificate("localhost"))
    .Build();

await host.StartAsync();
```

Cuando un host de escucha está configurado con SSL, [HttpServer.ListeningPrefixes](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpServer.ListeningPrefixes.md) informa sus direcciones con el esquema `https`. Si un cliente envía HTTP plano a un puerto configurado con SSL, Cadente responde con una redirección `301 Moved Permanently` a la dirección `https` del mismo host y ruta.

### Configuración avanzada

Puedes personalizar la instancia subyacente de `HttpHost` pasando una acción de configuración al constructor de `CadenteHttpServerEngine`. Esto es útil para ajustar tiempos de espera u otras configuraciones de bajo nivel.

```csharp
using var engine = new CadenteHttpServerEngine(host =>
{
    // Configurar tiempos de espera de lectura/escritura del cliente
    host.TimeoutManager.ClientReadTimeout = TimeSpan.FromSeconds(30);
    host.TimeoutManager.ClientWriteTimeout = TimeSpan.FromSeconds(30);
});
```

## Uso independiente

Aunque está diseñado principalmente para Sisk, Cadente puede usarse como un servidor HTTP independiente (similar a `HttpListener`).

```csharp
using Sisk.Cadente;

var host = new HttpHost(15000)
{
    Handler = new MyHostHandler()
};

host.Start();
Thread.Sleep(-1);

class MyHostHandler : HttpHostHandler
{
    public override async Task OnContextCreatedAsync(HttpHost host, HttpHostContext context)
    {
        context.Response.StatusCode = 200;

        using var stream = await context.Response.GetResponseStreamAsync(chunked: true);
        using var writer = new StreamWriter(stream);
        await writer.WriteLineAsync("Hello, world!");
    }
}
```

Cuando el cuerpo de la respuesta es pequeño y ya está en memoria, `WriteInlineContent` y `WriteInlineContentAsync` escriben los encabezados y el cuerpo en una única operación. La respuesta debe contener un encabezado `Content-Length`:

```csharp
byte[] body = "Hello, world!"u8.ToArray();

context.Response.StatusCode = 200;
context.Response.Headers.Set(new HttpHeader("Content-Length", body.Length.ToString()));
await context.Response.WriteInlineContentAsync(body);
```

## Validación de solicitudes

Cadente valida el encuadre de la solicitud antes de que llegue al manejador. Desde la versión 1.7, realiza lo siguiente:

- admite solicitudes en pipeline en conexiones keep-alive;
- rechaza métodos de solicitud que no sean tokens HTTP válidos y destinos de solicitud que contengan bytes fuera del rango ASCII visible (`%x21-7E`);
- rechaza solicitudes cuyo `Transfer-Encoding` sea distinto de un único valor `chunked`, y solicitudes con encabezados `Content-Length` conflictivos;
- responde `431 Request Header Fields Too Large` y cierra la conexión cuando la línea de solicitud y los encabezados superan los 8 KB.

Las solicitudes inválidas se responden con un error y la conexión se cierra, sin invocar al manejador.
