mirror of
https://github.com/go-gost/x.git
synced 2026-10-19 16:16:32 +00:00
e95b830670afffa31a44ab6efcea3709420c0a24
The route table answers a destination with a name, and the socket server turns that name into a UDP address. A p2p hub needs the other answer: the name is a peer key, and the thing it resolves to is a stream, not an address. That is the whole of the second delivery — resolveUDPAddr is unreachable here by construction, and putting it back on this path would be a design error rather than a bug. The shape is forced by the device, not chosen. tunDevice shares one read buffer and one write buffer across every call and takes no lock: two readers would have the second overwrite the first's target mid-call, and two writers would copy through the same write buffer at once. It is safe today only because the socket server happens to run one goroutine per side. A p2p hub runs one per peer, so it has to reach the same guarantee a different way: run() is the only goroutine that reads, and wmu is the only way to write. This is the socket server's shape arrived at from the other side — not a bridge per stream. Which goroutine read a packet carries no meaning, because the route is what says where it goes. Two locks, for two different hazards. The device's wmu guards the shared device buffer. peerStream's own mu guards one peer's stream, whose conn frames every write onto a byte stream and serializes only its read buffer: two writers would put one frame's length prefix against the other's payload, and every frame length would then be read from the wrong offset. The -race run is what proves both are doing their job. withdraw removes a stream only while it still holds its key. A peer that reconnected already installed a replacement under that name, so reclaiming routes for it on the old stream's teardown would cut off the live connection — which is what makes a flapping peer unreachable rather than merely briefly absent. The keepalive is answered on the stream it arrived on, because the table registers but never writes. Drop the echo and every keepalive:true spoke misses the reply its read deadline is refreshed from and redials on its own timeout, with nothing in any log: client.go's registration gate tests for "udp" and a p2p link is "ip", so a spoke that never registers is already indistinguishable from silence. Hence the warning naming the destination. The concurrency tests are mutation-checked: with the device lock removed the first fails on overlapping writes in round 0, and with the stream lock removed the second fails on a spliced frame. Both were caught by the assertions, not only by the race detector — the first harness was a datagram pipe, which is atomic per message and so passed with the lock gone until it was replaced by a byte stream, which is what the p2p conn really is.
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.
Languages
Go
100%