CodeVetter · Repo Unpack · Data flow
fatedier/frp
Config enters as TOML/YAML/JSON (or deprecated INI), is template-rendered with environment variables, decoded into pkg/config/v1 types, completed, and validated before use. Control-plane data flows as typed msg structs (Login, NewProxy, NewWorkConn, Ping, NatHole*) over a single muxed connection, versioned by the v1/v2 wire protocol. User traffic flows: public listener on frps -> proxy -> pooled work connection -> frpc -> optional plugin -> local service, joined byte streams via libio.Join. Admin data flows through gorilla/mux routers into adminapi controllers, and reloads re-merge config via the source.Aggregator.
- Config loading: LoadFileContentWithTemplate renders Go templates exposing .Envs plus parseNumberRange helpers, then LoadConfigure tries TOML->JSON marshal, JSON detection via yaml.IsJSONBuffer, or YAML (with dot-field stripping), honoring strict mode.
- Legacy INI configs are detected by a 'common' section and converted through pkg/config/legacy converters (Convert_ServerCommonConf_To_v1, Convert_ClientCommonConf_To_v1) before joining the v1 path.
- Client proxy/visitor configs come from inline [[proxies]]/[[visitors]] plus includeConfigFiles globs (LoadAdditionalClientConfigs), dedup-checked by validateNoDuplicateNames, filtered by common.Start and enabled flags in FilterClientConfigurers.
- Login handshake: client sends msg.Login over the control conn; server runs pluginManager.Login hooks, wraps the conn in an encrypted ReadWriter (AEAD for v2, crypto RW for v1), then RegisterControl admits and activates the Control, replying LoginResp{RunID}.
- Proxy registration: client pm.UpdateAll sends msg.NewProxy; server handleNewProxy runs NewProxy plugin hooks, builds a configurer via config.NewProxyConfigurerFromMsg, creates a Proxy through proxyFactoryRegistry, runs it (acquiring ports/vhost routes) and answers NewProxyResp.
- Work connections are pooled: server keeps workConnCh buffered at poolCount+10, sends msg.ReqWorkConn on demand; client handleReqWorkConn dials a fresh conn, writes authenticated NewWorkConn, waits for StartWorkConn, then hands the conn to the owning proxy.
- TCP user traffic path on frpc: BaseProxy.InWorkConn -> HandleTCPWorkConnection -> wrapWorkConn (limiter -> encryption -> compression) -> optional proxyPlugin.Handle or dial localIP:localPort -> libio.Join(localConn, remote).
- HTTP(S) traffic is routed by hostname through vhost.Routers into the matching proxy; frps can share the bind port with vhost ports by muxing HTTP/HTTPS listeners off mux.Mux when BindPort == VhostHTTP(S)Port.
- UDP traffic uses msg.UDPPacket frames; the negotiated codec (udpPacketCodec) is fixed per session via the v2 ServerHello and passed into proxy/visitor managers.
- Hot reload path: /api/reload -> apiController.Reload -> Service.UpdateConfigSource -> configSource.ReplaceAll -> reloadConfigFromSourcesLocked -> aggregator.Load -> Filter/Complete -> UpdateAllConfigurer -> ctl.pm/vm.UpdateAll.
- Metrics flow: server/metrics.Server records NewClient/NewProxy/CloseProxy events during Control lifecycle; pkg/metrics EnableMem/EnablePrometheus are activated when webServer.Port > 0 and exposed at /metrics.
- NatHole orchestration: NatHoleVisitor/NatHoleClient/NatHoleReport messages are dispatched as async handlers on the server Control and routed to nathole.Controller, which analyzes NAT behavior and replies NatHoleResp to visitors.