When a client logs in again with the same run ID, frps replaces the old
control and closes its control stream, but the yamux session that carried it
stays open until keepalive reclaims it. The replaced control's work
connections and streams share that session, so they are left behind for up to
tcpMuxKeepaliveInterval plus the ping budget.
A mux session carries exactly one control connection, so the control now
records its session and closes it from the single teardown point shared by
replacement, fatal exit and shutdown. QUIC and non-multiplexed transports keep
the previous path.
Closing the session ends every stream that still belongs to the replaced
control. Those proxies are already being torn down, so the cost is that
in-flight connections of the old control are interrupted immediately instead
of at the keepalive deadline. A replacement control is not affected, and the
ownership rules for a still-online control do not change.
Tests: replacement and fatal exit close the session while the replacement
control keeps working, and concurrent logins with the same run ID leave
exactly one live session.
Add a separate HTTP listener for observability, configured through
metricsServer in both frps and frpc. It serves /healthz and, when
pprofEnable is set, the pprof handlers except /debug/pprof/cmdline. It
never serves the dashboard, static assets or the admin API, and it does
not touch the global assets filesystem.
frps also serves /metrics on this listener with both runtime and business
metrics, enabled by metricsServer.port alone. The in-memory collector
still starts only with the dashboard, and the Prometheus collector starts
either with metricsServer or with enablePrometheus plus webServer, so a
configuration that collected nothing before still collects nothing.
The /healthz, /metrics and pprof endpoints on webServer, and
enablePrometheus, keep working but are deprecated and log a hint at
startup. frpc does not serve /metrics yet.
A constructor that binds a port before its fallible preparation leaks the
listener when a later step returns an error, and no one can close it because
the returned service is nil.
pkg/util/http: load the TLS keypair and build the auth middleware before
net.Listen, so binding is the last step that can run and a failure leaves no
bound port behind.
server: create the web server after auth, the tcpmux, main, kcp, quic, ssh
gateway and vhost listeners, and the nat hole controller, following what frpc
already does. If the web server itself fails, close the resources created so
far before returning. The port managers start their cleanup workers on
creation, so they are created after every fallible step too, and the metrics
collectors keep being enabled only when the web server is configured.
Add regression tests for all three failure paths.
Use net/http Server.Protocols and the merged golib listener path instead of the deprecated h2c handler. Keep HTTP/1.1 and cleartext HTTP/2 prior-knowledge support, while intentionally dropping HTTP/1.1 Upgrade: h2c.
SUDP payload codec follows transport wireProtocol; same-protocol v1/v1 and v2/v2 keep raw join; only mixed proxy/visitor protocols use message-aware bridge; no new capability/selection field.
Replace 10 positional parameters in NewControl() with a single
SessionContext struct, matching the client-side pattern. This also
eliminates the post-construction mutation of clientRegistry and
removes two TODO comments.
- Fix SSH tunnel gateway incorrectly binding to proxyBindAddr instead of bindAddr
- This caused external connections to fail when proxyBindAddr was set to 127.0.0.1
- SSH tunnel gateway now correctly binds to bindAddr for external accessibility
- Update Release.md with bug fix description
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Update go.mod to use github.com/quic-go/quic-go v0.53.0
- Replace quic.Connection interface with *quic.Conn struct
- Replace quic.Stream interface with *quic.Stream struct
- Update all affected files to use new API:
- pkg/util/net/conn.go: Update QuicStreamToNetConn function and wrapQuicStream struct
- server/service.go: Update HandleQUICListener function parameter
- client/visitor/xtcp.go: Update QUICTunnelSession struct field
- client/connector.go: Update defaultConnectorImpl struct field
Fixes#4852
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>