Skip to content

HTTPS / TLS / HTTP2

ACE Framework 通过 ace-httptlsFromPemFiles() 函数与 ListenOptions.tls 字段启用 HTTPS,底层基于 stdx.net.tls,支持 RSA / ECDSA / SM2 证书。启用 TLS 后默认同时启用 HTTP/2(经 ALPN 协商,不支持 h2 的客户端自动回落 HTTP/1.1)。

方式一:配置文件驱动(推荐)

application.toml 中配置证书路径,AceApplication.run() 自动启用 TLS:

toml
# application.toml
[server]
host = "0.0.0.0"
port = 8443

[server.tls]
cert = "/etc/ssl/certs/server.crt"   # 证书链 PEM(叶子在前,CA 在后)
key  = "/etc/ssl/private/server.key" # 私钥 PEM
# keyPassword = "changeit"           # 可选,加密私钥的口令
# http2 = false                      # 可选,关闭 HTTP/2(默认开启)

应用代码无需任何改动:

cangjie
main(): Int64 {
    AceApplication.run()   // 自动读取 server.tls.cert / server.tls.key
    return 0
}

服务启动后输出:

ACE serving on https://0.0.0.0:8443 (env=local)

方式二:编程式配置

使用 tlsFromPemFiles() 构造 TLS 配置,通过 ListenOptions 传入:

cangjie
import ace_http.*

main(): Int64 {
    let app = AceApplication.buildApp()
    var opts = ListenOptions()
    opts.tls = tlsFromPemFiles("server.crt", "server.key")
    listenWith(app, "0.0.0.0", 8443, opts)
    return 0
}

加密私钥(带口令):

cangjie
opts.tls = tlsFromPemFiles("server.crt", "server.key", "p@ssw0rd")

HTTP/2

配置 TLS 后 HTTP/2 即自动可用,无需额外代码:tlsFromPemFiles 默认把 ALPN 协商列表设为 ["h2", "http/1.1"],stdx 服务端按握手协商结果自动选择 HTTP/2 或 HTTP/1.1 引擎。所有能力(路由、中间件、SSE 流式、二进制响应、gzip 等)在两个协议下行为一致。

bash
# 验证协商结果(自签名证书加 -k)
curl -k --http2 https://localhost:8443/api/ping -w '\nhttp_version=%{http_version}\n'
# => pong
# => http_version=2

如需关闭(仅 HTTP/1.1):

toml
[server.tls]
http2 = false

编程式等价写法:

cangjie
opts.tls = tlsFromPemFiles("server.crt", "server.key", http2: false)

注意事项:

  • HTTP/2 仅在 TLS 上可用(stdx 服务端不支持明文 h2c),明文端口恒为 HTTP/1.1——浏览器同样只支持 TLS 上的 h2,因此这不构成实际限制。
  • 流式响应(SSE 等)在 HTTP/1.1 下走 Transfer-Encoding: chunked,HTTP/2 下由 DATA 帧天然分块(框架自动区分,h2 响应不携带 connection 级头)。
  • WebSocket 两个协议都支持:HTTP/1.1 走 Upgrade: websocket 握手(RFC 6455),HTTP/2 走 extended CONNECT(RFC 8441,服务端已下发 SETTINGS_ENABLE_CONNECT_PROTOCOL),同一 @WsController 端点无差别服务两种客户端。

证书格式

证书链 PEM

若使用 CA 签发的证书,cert 文件须包含完整证书链(叶子证书在前,中间 CA 在后):

-----BEGIN CERTIFICATE-----
(你的域名证书)
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
(中间 CA 证书)
-----END CERTIFICATE-----

支持的密钥类型

tlsFromPemFiles 使用 GeneralPrivateKey.decodeFromPem 自动识别密钥类型:

类型PEM 头部
RSA-----BEGIN RSA PRIVATE KEY----- 或 PKCS#8
ECDSA-----BEGIN EC PRIVATE KEY----- 或 PKCS#8
SM2Cangjie stdx 支持的 SM2 格式

自签名证书(开发环境)

bash
# 生成 ECDSA 自签名证书(有效期 365 天)
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 \
  -keyout server.key -out server.crt \
  -days 365 -nodes \
  -subj "/CN=localhost"

开发配置:

toml
# application-local.toml
[server.tls]
cert = "server.crt"
key  = "server.key"

浏览器访问 https://localhost:8443 需手动信任自签名证书,或使用 curl -k

连接超时与请求大小限制

通过 ListenOptions 同时配置 TLS、超时与请求大小上限:

cangjie
var opts = ListenOptions()
opts.tls                  = tlsFromPemFiles("server.crt", "server.key")
opts.readTimeoutMs        = 30000            // 30 秒读取超时
opts.writeTimeoutMs       = 30000            // 30 秒写入超时
opts.maxRequestBodySize   = Some(8_388_608)  // 请求体上限 8MB,超出回 413(防大上传 OOM)
opts.maxRequestHeaderSize = Some(16_384)     // 请求头上限 16KB,超出回 431(防恶意大头)
listenWith(app, "0.0.0.0", 8443, opts)

大小上限不设置(None)时用 stdx 默认(body 2MB);Some(0) 表示显式无限。 配置驱动启动(Application.run())对应配置键 server.maxRequestBodySize / server.maxRequestHeaderSize

HTTP + HTTPS 双端口

如需同时监听 HTTP(用于重定向)和 HTTPS,启动两个协程:

cangjie
import ace_http.*
import ace_web.*
import std.sync.*

main(): Int64 {
    let app = AceApplication.buildApp()

    // HTTP 端口:强制重定向到 HTTPS
    let redirectApp = App()
    redirectApp.use({ctx: Context, _: Next =>
        ctx.status = 301
        ctx.setHeader("Location", "https://yourdomain.com${ctx.path}")
    })

    let latch = CountDownLatch(2)

    spawn {
        listen(redirectApp, "0.0.0.0", 8080)
        latch.countDown()
    }

    spawn {
        var opts = ListenOptions()
        opts.tls = tlsFromPemFiles("server.crt", "server.key")
        listenWith(app, "0.0.0.0", 8443, opts)
        latch.countDown()
    }

    latch.await()
    return 0
}

反向代理(Let's Encrypt)

生产环境推荐在 Nginx / Caddy 等反向代理处终止 TLS,ACE 服务监听 HTTP:

nginx
# Nginx 配置
server {
    listen 443 ssl;
    ssl_certificate     /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

此场景 ACE 无需配置 TLS,应用代码读取 X-Forwarded-Proto 头判断请求协议。

生产检查清单

  • [ ] 证书链完整(叶子证书 + 中间 CA)
  • [ ] 私钥权限 chmod 600 server.key
  • [ ] 证书有效期监控(建议提前 30 天告警)
  • [ ] 仅启用 TLS 1.2+(stdx 默认已限制)
  • [ ] 生产环境 keyPassword 通过环境变量或密钥管理服务注入,勿写入配置文件

基于 Apache-2.0 许可证发布