37bc81576c49551d974cb5e877e8b9a7a84ef9f1
transportClient and transportServer each launch two goroutines for bidirectional copy between the TUN device and the remote connection. Previously, when one goroutine errored, the function returned immediately while the sibling goroutine continued running — reading from and writing to connections that would soon be closed by the caller's deferred cleanup. In the retry loop of handleClient and handleServer, stale goroutines accumulated across iterations, racing with new goroutines for the TUN device and writing to closed pipes. This caused the "io: read/write on closed pipe" error reported in go-gost/gost#345, and more importantly, prevented recovery because subsequent retry iterations operated on a corrupted state. Fix: - Derive a per-transport context in transportClient/transportServer and check it at the top of each goroutine's loop to allow the first-failing goroutine to signal its sibling to exit - Replace the single-error select with a collectFirstError helper that drains both goroutines and includes a timeout guard (5s) to prevent deadlock when a goroutine is stuck in a non-cancelable read - Use a per-iteration context in handleClient for the keepalive goroutine so it is properly cancelled when an iteration ends - Fix handleServer to use errors.Is(err, ErrTun) for consistency - Fix transportServer's errc buffer from 1 to 2 to prevent a goroutine leak when both goroutines error simultaneously
x
The implementation layer for the GOST proxy framework. Every interface defined in core/ is implemented here — handlers, listeners, dialers, connectors, plus config parsing, registries, and all cross-cutting concerns.
Package layout
| Directory | Purpose |
|---|---|
handler/ |
Protocol handlers (http, socks4/5, ss, ssh, tunnel, tun, dns, redirect, etc.) |
listener/ |
Protocol listeners (tcp, tls, ws, http2/3, quic, kcp, icmp, tun, udp, etc.) |
dialer/ |
Outbound dialers (tcp, tls, ws, http2/3, quic, grpc, ssh, wg, kcp, icmp, etc.) |
connector/ |
Destination connectors (http, socks4/5, ss, ssh, relay, tunnel, direct, etc.) |
config/ |
Config struct, YAML/JSON parsing, and service construction |
registry/ |
Typed global registries for all component types |
service/ |
Accept loop wiring listener + handler |
chain/ |
Multi-hop forwarding chain and route implementation |
router/ |
Route table router (destination-based routing) |
hop/ |
Node group with load-balanced selection |
selector/ |
Load-balancing strategies (round-robin, random, weighted, hash) |
auth/, bypass/, admission/ |
Authentication, bypass rules, admission control |
resolver/, hosts/ |
DNS resolution and host mapping |
limiter/ |
Traffic, connection, and rate limiters |
recorder/, observer/ |
Traffic recording and observability |
ingress/, sd/, routing/ |
Ingress control, service discovery, routing rules |
logger/, metrics/, api/ |
Logging, Prometheus metrics, Web API |
metadata/, ctx/ |
Metadata key-value system and context propagation |
internal/ |
Shared internals — not importable externally |
Component pattern
Every handler, listener, dialer, and connector follows the same pattern: init() registers a constructor into a global registry, the constructor takes functional options, and Init(metadata.Metadata) extracts typed configuration from the metadata key-value map.
See CLAUDE.md for detailed architecture and conventions.
Description
Languages
Go
100%