not implemented: No tls feature was specified
主线位置:请求主线里 TLS 那一步,下游是
Acceptor::tls_handshake,上游是连接器的 TLSconnect。一个后端都不开时这些名字由noop_tls提供,见怎么读这一卷的「一个 TLS 后端都不开」和 TLS。本页把它当成一个坑:怎么撞上、怎么发现。
flowchart TB
feat["没开 TLS feature"] --> noop["noop_tls 桩"]
noop --> st["intermediate 返回 Ok"]
st --> run["编译 启动都正常"]
run --> down{"哪个方向"}
down -->|下游 HTTPS| pan["tls_handshake panic"]
down -->|上游 TLS| fake["假 SslStream"]
fake --> eat["写入被吞掉"]
eat --> r502["502 ConnectionClosed"]
实验 crate exp-pingora/pita-notls:pingora 只开 proxy;它的 fix feature 是 ["pingora/openssl"]。M2 Max、macOS 27、rustc 1.99.0,debug,tokio 1.53.2。
现象
依赖只写了 pingora = { version = "=0.9.0", features = ["proxy"] },代码照着 TLS 的样子写:上游 HttpPeer::new("127.0.0.1:19211", true, "localhost")(tls = true),再加一个 HTTPS 监听,证书路径故意不存在:
let (cert, key) = (std::env::var("CERT").unwrap(), std::env::var("KEY").unwrap());
if let Err(e) = proxy.add_tls("127.0.0.1:19212", &cert, &key) {
eprintln!("add_tls: {e}");
std::process::exit(1);
}
编译没有任何警告。CERT=/nonexistent/cert.pem KEY=/nonexistent/key.pem 启动,打出 started,add_tls 返回了 Ok。19211 上是一个只收数据、不回应的 TCP 服务。先请求明文端口,再请求 HTTPS 端口,再请求一次明文端口:
http=502
http=000 exit=35
http=502
代理的日志:
[2026-10-29T07:00:05Z ERROR pingora_proxy] Fail to proxy: Upstream ConnectionClosed context: Peer: addr: 127.0.0.1:19211, scheme: HTTPS, sni: localhost cause: context: while reading response headers, bytes already read: 0, status: 502, tries: 1, retry: false, GET /x, Host: 127.0.0.1:19210
thread 'Pingora HTTP Proxy Service' (5958028) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pingora-core-0.9.0/src/protocols/tls/noop_tls/mod.rs:102:13:
not implemented: No tls feature was specified
上游那边:
upstream: connection from 61192, 0 bytes received, closed
upstream: connection from 61195, 0 bytes received, closed
- 上游方向不 panic,也不报 TLS 错误。 TCP 连上了,请求头写进了一个假的
SslStream,上游一个字节都没收到;读响应头立刻读到 0 字节,错误是ConnectionClosed。日志里写着scheme: HTTPS,看上去像上游把连接关了。 - 下游方向在握手时 panic。 每个 HTTPS 连接 panic 一次,curl 看到的是握手失败(35)。panic 发生在连接自己的任务里,监听循环不受影响,所以第三次明文请求还是 502,这一点和 tokio 1.54 的 panic 不同。
- 证书路径不存在,
add_tls却成功了:这个错误要到第一个 HTTPS 连接进来才暴露。
出处
pingora-core 在 not(any_tls) 时把 noop_tls 改名成 tls。这个模块的文档注释是「a set of stubs that provides the minimum types to let pingora work without any tls providers configured」:
TlsSettings::intermediate直接Ok(Self),参数被丢掉。Acceptor::tls_handshake是unimplemented!("No tls feature was specified"),就是上面第 102 行。- 连接器的
connect返回一个SslStream::default();它的poll_read立刻返回Ok、什么都不填,也就是 EOF,poll_write吞掉全部数据并报告写完。
pingora 门面 crate 和 pingora-core 的 [features] 都是 default = [],不写 TLS feature 就是这个状态。HttpPeer::new 的 tls 参数、add_tls 方法都不受 feature 控制,所以代码怎么写都编得过。
问: pingora-core 的 crate 文档写着 openssl 是「default if no TLS feature is specified」,怎么会落到 noop_tls?
答: 文档和代码不一致。lib.rs 的 crate 文档写的是「TLS backends (mutually exclusive)」和「openssl: ... (default if no TLS feature is specified)」,而 pingora-core/Cargo.toml 里是 default = [],没有任何 feature 会自动打开 openssl。上面 pita-notls 的 cargo tree -i pingora-core -f '{p} [{f}]' --depth 0 打出的是 pingora-core v0.9.0 []。「mutually exclusive」也只是约定,代码里没有互斥,见 the name tls is defined multiple times。
修法
开一个 TLS 后端。pita-notls 用 --features fix(即 pingora/openssl)重新编译,同样的不存在的路径:
add_tls: TLSConfigError context: fail to read key file /nonexistent/key.pem cause: error:80000002:system library:file_ctrl:reason(2):crypto/bio/bss_file.c:299:calling fopen(/nonexistent/key.pem, r), error:10080002:BIO routines:file_ctrl:system lib:crypto/bio/bss_file.c:302:, error:0A080002:SSL routines:SSL_CTX_use_PrivateKey_file:system lib:ssl/ssl_rsa.c:411:
exit=1
openssl 后端的 intermediate 当场读证书,add_tls 返回 Err,进程在启动时就退出。换成真实的自签名证书再请求 HTTPS 端口,下游握手成功,到上游那一步失败,这次错误说的是实话:
https http=502
[2026-10-29T07:00:25Z ERROR pingora_proxy] Fail to proxy: Upstream TLSHandshakeFailure context: TLS connect() failed: unexpected EOF, SNI: localhost cause: local address 127.0.0.1:61231, status: 502, tries: 1, retry: false, GET /y, Host: 127.0.0.1:19212
upstream: connection from 61231, 1487 bytes received, closed
上游这次收到了 1487 字节的 ClientHello,因为它不是 TLS 服务、不回应,握手以 TLSHandshakeFailure 失败。
如果选 rustls,注意它的 intermediate 也只记路径、不读文件,证书错误同样推迟到启动服务时,见 Failed to load provided certificates。
怎么确认没中招
cargo tree -i pingora-core -f '{p} [{f}]' --depth 0:方括号里要有any_tls,并且openssl、boringssl、rustls、s2n正好一个。pita-notls不加fix是[],加了是[any_tls,openssl,openssl_derived,pingora-openssl]。- 日志里出现
noop_tls/mod.rs或No tls feature was specified,就是这个坑。 - 上游明明是 HTTPS,却只看到
ConnectionClosed ... bytes already read: 0、从来没有TLSHandshakeFailure之类的 TLS 错误,也要怀疑是noop_tls在吞数据。
小结
- 不开 TLS feature 时 pingora 用
noop_tls:编译、启动都不报错,add_tls不读证书。 - 下游 HTTPS 连接在握手时 panic(
noop_tls/mod.rs:102),只杀那一个连接;上游 TLS 请求被假的SslStream吞掉,客户端拿到 502,日志说的是ConnectionClosed。 pingora-core的文档说 openssl 是默认,实际default = []。开一个后端,并用cargo tree的{f}确认。