# Cadente

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

Cadente é uma implementação experimental gerenciada de listener HTTP/1.1 para o Sisk. Ela serve como substituta do `System.Net.HttpListener` padrão, oferecendo maior controle e flexibilidade, especialmente em plataformas não Windows.

## Visão geral

Por padrão, o Sisk usa `HttpListener` (do `System.Net`) como seu mecanismo subjacente de servidor HTTP. Embora o `HttpListener` seja estável e performático no Windows (onde utiliza o driver kernel-mode HTTP.sys), sua implementação no Linux e macOS é gerenciada e historicamente apresenta limitações, como a falta de suporte nativo a SSL (exigindo um proxy reverso como o Nginx) e características de desempenho variáveis.

Cadente tem como objetivo resolver esses problemas fornecendo um servidor HTTP/1.1 totalmente gerenciado escrito em C#. Seus principais objetivos são:

- **Suporte nativo a SSL:** Funciona em todas as plataformas sem necessidade de proxies externos ou configuração complexa.
- **Consistência multiplataforma:** Comportamento idêntico no Windows, Linux e macOS.
- **Desempenho:** Projetado para ser uma alternativa de alto desempenho ao `HttpListener` gerenciado.
- **Independência:** Desacoplado do `System.Net.HttpListener`, isolando o Sisk de possíveis futuras descontinuações ou falta de manutenção desse componente no .NET.

> [!WARNING]
> **Status Experimental**
> 
> Cadente está atualmente em estágio experimental (Beta). Ainda não é recomendado para ambientes de produção críticos. A API e o comportamento podem mudar.

## Instalação

Cadente está disponível como um pacote separado. Para usá-lo com o Sisk, você precisa do pacote `Sisk.Cadente.CoreEngine`.

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

`Sisk.Cadente` e `Sisk.Cadente.CoreEngine` têm como alvo .NET 9 e .NET 10. Eles não podem ser usados em projetos que visam .NET 8, embora `Sisk.HttpServer` o suporte.

## Usando com Sisk

Para usar o Cadente como o mecanismo HTTP da sua aplicação Sisk, você precisa configurar o `HttpServer` para usar `CadenteHttpServerEngine` em vez do mecanismo padrão.

O `CadenteHttpServerEngine` adapta o `HttpHost` do Cadente à abstração `HttpServerEngine` exigida pelo 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();
```

Quando um host de escuta está configurado com SSL, [HttpServer.ListeningPrefixes](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpServer.ListeningPrefixes.md) relata seus endereços com o esquema `https`. Se um cliente enviar HTTP simples para uma porta configurada com SSL, o Cadente responde com um redirecionamento `301 Moved Permanently` para o endereço `https` do mesmo host e caminho.

### Configuração avançada

Você pode personalizar a instância subjacente de `HttpHost` passando uma ação de configuração ao construtor `CadenteHttpServerEngine`. Isso é útil para configurar tempos limite ou outras definições de baixo nível.

```csharp
using var engine = new CadenteHttpServerEngine(host =>
{
    // Configura tempos limite de leitura/gravação do cliente
    host.TimeoutManager.ClientReadTimeout = TimeSpan.FromSeconds(30);
    host.TimeoutManager.ClientWriteTimeout = TimeSpan.FromSeconds(30);
});
```

## Uso autônomo

Embora seja projetado principalmente para o Sisk, o Cadente pode ser usado como um servidor HTTP autônomo (similar ao `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!");
    }
}
```

Quando o corpo da resposta é pequeno e já está na memória, `WriteInlineContent` e `WriteInlineContentAsync` escrevem os cabeçalhos e o corpo em uma única operação. A resposta deve ter um cabeçalho `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);
```

## Validação de requisição

Cadente valida a estrutura da requisição antes que ela chegue ao manipulador. Desde a versão 1.7, ele:

- suporta requisições em pipeline em conexões keep-alive;
- rejeita métodos de requisição que não são tokens HTTP válidos e alvos de requisição que contêm bytes fora do intervalo ASCII visível (`%x21-7E`);
- rejeita requisições cujo `Transfer-Encoding` seja diferente de um único valor `chunked`, e requisições com cabeçalhos `Content-Length` conflitantes;
- responde com `431 Request Header Fields Too Large` e fecha a conexão quando a linha de requisição e os cabeçalhos excedem 8 KB.

Requisições inválidas são respondidas com um erro e a conexão é fechada, sem chamar o manipulador.
