# Cadente

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

Cadente — экспериментальная управляемая реализация HTTP/1.1‑слушателя для Sisk. Она заменяет стандартный `System.Net.HttpListener`, предоставляя больший контроль и гибкость, особенно на платформах, отличных от Windows.

## Обзор

По умолчанию Sisk использует `HttpListener` (из `System.Net`) в качестве базового HTTP‑движка. Хотя `HttpListener` стабилен и производителен в Windows (где он работает через драйвер ядра HTTP.sys), его реализация в Linux и macOS управляемая и исторически имела ограничения, такие как отсутствие нативной поддержки SSL (требуется обратный прокси, например Nginx) и различия в производительности.

Cadente стремится решить эти проблемы, предоставляя полностью управляемый HTTP/1.1‑сервер, написанный на C#. Его ключевые цели:

- **Нативная поддержка SSL:** Работает на всех платформах без внешних прокси и сложных настроек.
- **Кроссплатформенная согласованность:** Идентичное поведение в Windows, Linux и macOS.
- **Производительность:** Спроектирован как высокопроизводительная альтернатива управляемому `HttpListener`.
- **Независимость:** Отделён от `System.Net.HttpListener`, защищая Sisk от возможных будущих устареваний или прекращения поддержки этого компонента в .NET.

> [!WARNING]
> **Экспериментальный статус**
> 
> Cadente находится в экспериментальной (Beta) стадии. Пока не рекомендуется для критически важных продакшн‑окружений. API и поведение могут измениться.

## Установка

Cadente доступен как отдельный пакет. Чтобы использовать его с Sisk, необходимо установить пакет `Sisk.Cadente.CoreEngine`.

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

`Sisk.Cadente` и `Sisk.Cadente.CoreEngine` нацелены на .NET 9 и .NET 10. Их нельзя использовать в проектах, таргетирующих .NET 8, хотя `Sisk.HttpServer` поддерживает эту версию.

## Использование с Sisk

Чтобы задействовать Cadente в качестве HTTP‑движка вашего приложения Sisk, необходимо сконфигурировать `HttpServer` для использования `CadenteHttpServerEngine` вместо движка по умолчанию.

`CadenteHttpServerEngine` адаптирует `HttpHost` из Cadente к абстракции `HttpServerEngine`, требуемой 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();
```

Когда настроенный хост прослушивает с SSL, [HttpServer.ListeningPrefixes](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpServer.ListeningPrefixes.md) возвращает его адреса со схемой `https`. Если клиент отправит обычный HTTP на порт, сконфигурированный для SSL, Cadente ответит редиректом `301 Moved Permanently` на `https`‑адрес того же хоста и пути.

### Расширенная конфигурация

Можно настроить базовый объект `HttpHost`, передав действие настройки в конструктор `CadenteHttpServerEngine`. Это удобно для задания тайм‑аутов и других низкоуровневых параметров.

```csharp
using var engine = new CadenteHttpServerEngine(host =>
{
    // Настройка тайм‑аутов чтения/записи клиента
    host.TimeoutManager.ClientReadTimeout = TimeSpan.FromSeconds(30);
    host.TimeoutManager.ClientWriteTimeout = TimeSpan.FromSeconds(30);
});
```

## Автономное использование

Хотя Cadente в первую очередь предназначен для Sisk, его можно использовать как самостоятельный HTTP‑сервер (аналог `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!");
    }
}
```

Когда тело ответа небольшое и уже находится в памяти, `WriteInlineContent` и `WriteInlineContentAsync` записывают заголовки и тело за одну операцию. Ответ должен содержать заголовок `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);
```

## Проверка запросов

Cadente проверяет корректность запросов до того, как они попадают в обработчик. Начиная с версии 1.7, он:

- поддерживает конвейеризацию запросов на соединениях keep‑alive;
- отклоняет методы запросов, не являющиеся валидными HTTP‑токенами, и цели запросов, содержащие байты за пределами видимого диапазона ASCII (`%x21-7E`);
- отклоняет запросы, у которых `Transfer-Encoding` отличается от единственного значения `chunked`, а также запросы с конфликтующими заголовками `Content-Length`;
- отвечает `431 Request Header Fields Too Large` и закрывает соединение, если строка запроса и заголовки превышают 8 KB.

Невалидные запросы получают ошибку, соединение закрывается, и обработчик не вызывается.
