# Cadente

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

Cadente 是为 Sisk 提供的实验性托管 HTTP/1.1 监听器实现。它作为默认的 `System.Net.HttpListener` 的替代品，提供更大的控制力和灵活性，尤其在非 Windows 平台上。

## 概述

默认情况下，Sisk 使用 `HttpListener`（来自 `System.Net`）作为底层 HTTP 服务器引擎。虽然 `HttpListener` 在 Windows 上（使用内核模式的 HTTP.sys 驱动）稳定且性能良好，但其在 Linux 和 macOS 上的实现是托管的，历史上存在一些限制，例如缺乏原生 SSL 支持（需要使用 Nginx 等反向代理）以及性能特性各异。

Cadente 旨在通过提供一个用 C# 编写的完全托管的 HTTP/1.1 服务器来解决这些问题。其主要目标包括：

- **Native SSL Support:** 在所有平台上工作，无需外部代理或复杂配置。
- **Cross-Platform Consistency:** 在 Windows、Linux 和 macOS 上表现一致。
- **Performance:** 旨在成为托管 `HttpListener` 的高性能替代方案。
- **Independence:** 与 `System.Net.HttpListener` 解耦，使 Sisk 免受该组件未来可能被弃用或缺乏维护的影响。

> [!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` 支持 .NET 8。

## 在 Sisk 中使用

要将 Cadente 作为 Sisk 应用的 HTTP 引擎，需要将 `HttpServer` 配置为使用 `CadenteHttpServerEngine` 而非默认引擎。

`CadenteHttpServerEngine` 将 Cadente 的 `HttpHost` 适配为 Sisk 所需的 `HttpServerEngine` 抽象。

```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` 方案报告其地址。如果客户端向配置了 SSL 的端口发送普通 HTTP，Cadente 会返回 `301 Moved Permanently` 重定向到同一主机和路径的 `https` 地址。

### 高级配置

可以通过向 `CadenteHttpServerEngine` 构造函数传递设置操作来自定义底层的 `HttpHost` 实例。这对于配置超时或其他底层设置非常有用。

```csharp
using var engine = new CadenteHttpServerEngine(host =>
{
    // 配置客户端读/写超时
    host.TimeoutManager.ClientReadTimeout = TimeSpan.FromSeconds(30);
    host.TimeoutManager.ClientWriteTimeout = TimeSpan.FromSeconds(30);
});
```

## 独立使用

虽然主要为 Sisk 设计，Cadente 也可以作为独立的 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` 头的请求；
- 当请求行和头部超过 8 KB 时，返回 `431 Request Header Fields Too Large` 并关闭连接。

无效请求会返回错误并关闭连接，且不会调用处理程序。
