#5525·frp

[Feature Request] Add an HTTP request admission operation to server plugins / 为服务端插件增加 HTTP 请求准入操作

Author: ligb888Created Sep 8, 2026Updated Sep 10, 2026

中文说明

功能需求

NewUserConn 观察的是连接,而 HTTP 反向代理可能复用后端连接,因此它不能可靠地对每个 HTTP 请求执行准入检查。

建议新增只读的 NewHTTPRequest 服务端插件操作。每个 HTTP 请求应在 Host/Location 路由匹配和路由 HTTP Basic Auth 成功后、获取后端工作连接并转发请求前调用。HTTP Keep-Alive 连接上的每个请求都应独立触发。

首版请求内容保持最小且无歧义:

{
  "remote_addr": "203.0.113.10:52130",
  "host": "app.example.com",
  "method": "GET",
  "uri": "/api/orders",
  "route_domain": "app.example.com",
  "route_location": "/api"
}

uri 只包含 URL Path,不包含 Query。首版不发送请求正文、Cookie、Authorization、Proxy-Authorization 或完整 Header。负载均衡组在检查阶段尚未确定最终后端代理,因此也不发送可能错误的 userproxy_name

该操作只允许准入决策:reject=true 停止插件链并固定返回 HTTP 403,拒绝原因不暴露给客户端;允许时忽略插件返回的内容,不能修改待转发请求。插件调用继承入站请求的取消信号,但本提案不增加硬编码或可配置超时。

未配置该操作时不安装请求检查回调,现有请求路径只保留一次 nil 判断,配置、控制协议和转发行为不变。

验证范围

计划补充插件管理器、配置校验、HTTP GET/CONNECT、认证顺序、拒绝时后端零访问、真实 frps/frpc/plugin/backend E2E、竞态测试和无插件基准。实现保持独立,不包含业务策略、限流、缓存或异步调用。

已考虑的替代方案

  1. HTTPProxy.GetRealConn 中复用 NewUserConn:后端连接可能跨请求复用,无法保证逐请求检查。
  2. 在 frps 前增加反向代理:会增加数据面和部署依赖。
  3. 使用操作系统防火墙:依赖平台,且无法使用 HTTP Host 或 Path。

English Description

Feature request

NewUserConn observes connections, while the HTTP reverse proxy may reuse backend connections. It therefore cannot reliably make an admission decision for every HTTP request.

Add a read-only NewHTTPRequest server-plugin operation. Invoke it for every request after Host/Location routing and route HTTP Basic Auth succeed, but before acquiring a backend work connection or forwarding the request. Every request on an HTTP keep-alive connection must trigger it independently.

Keep the initial payload minimal and unambiguous:

{
  "remote_addr": "203.0.113.10:52130",
  "host": "app.example.com",
  "method": "GET",
  "uri": "/api/orders",
  "route_domain": "app.example.com",
  "route_location": "/api"
}

uri contains only the URL path, not the query. The initial version does not send the body, cookies, Authorization, Proxy-Authorization, or arbitrary headers. A load-balanced route has not selected its final backend proxy at this stage, so the payload also omits potentially incorrect user and proxy_name values.

The operation supports admission only. reject=true stops the plugin chain and returns a fixed HTTP 403 without exposing the rejection reason. Returned content is ignored for allowed requests and cannot mutate the forwarded request. The plugin call inherits cancellation from the incoming request, but this proposal adds no hard-coded or configurable timeout.

When the operation is not configured, no request-check callback is installed. Existing configuration, control protocol, and forwarding behavior remain unchanged, with only a nil callback check in the request path.

Validation scope

Tests cover plugin management, configuration validation, GET and CONNECT, authentication ordering, zero backend access after rejection, a real frps/frpc/plugin/backend E2E flow, race checks, and a disabled-path benchmark. The change is self-contained and contains no product policy, rate limiting, caching, or asynchronous execution.

Alternatives considered

  1. Reuse NewUserConn in HTTPProxy.GetRealConn: backend connection reuse cannot guarantee one check per request.
  2. Add another reverse proxy in front of frps: this adds a data plane and deployment dependency.
  3. Use an operating-system firewall: this is platform-specific and cannot use HTTP Host or Path.

Affected area

  • Security
  • Performance and Scalability
  • Server Plugin
  • Extensions