e45328d1bb
Bug fixes: - packetConn.Read: fix slice bounds panic when dlen > len(b); n was set to dlen (the wire size) but only len(b) bytes were copied to b; callers doing b[:n] would panic. Clamp n via copy(). - lockWriter.Close: add mutex lock to prevent data race with Write; Router.Close calls connector.Close (lockWriter.Close) while handlePacket concurrently calls Writer.Write — both touch w.w. - Router.DelConnector: delete empty host slices from the map after removing the last connector, preventing unbounded map growth. Documentation: - Package-level doc with architecture diagram, data flow, component hierarchy, thread-safety invariants, and connector weighting rules. - Documented every exported type (Connector, Router, ConnectorPool, ConnectorOptions, routerHandler, metadata, lockWriter, packetConn) and all non-trivial methods with purpose, parameters, lifecycle, and algorithmic details (e.g., GetConnector weighted selection). - Annotated critical code paths with step-by-step comments (handleAssociate stages, handlePacket routing algorithm, handleEntrypoint packet format and forwarding logic). - Explained observeStats retry pattern and metadata key conventions. Verification: build, vet, 98 tests race-clean.
80 lines
1.9 KiB
Go
80 lines
1.9 KiB
Go
package router
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"time"
|
|
|
|
"github.com/go-gost/core/observer"
|
|
)
|
|
|
|
// checkRateLimit applies connection-level rate limiting based on the
|
|
// client's remote address.
|
|
//
|
|
// The rate limiter key is the host portion of the remote address.
|
|
// If no rate limiter is configured, all connections are allowed.
|
|
func (h *routerHandler) checkRateLimit(addr net.Addr) bool {
|
|
if h.options.RateLimiter == nil {
|
|
return true
|
|
}
|
|
host, _, _ := net.SplitHostPort(addr.String())
|
|
if limiter := h.options.RateLimiter.Limiter(host); limiter != nil {
|
|
return limiter.Allow(1)
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// observeStats periodically collects traffic statistics and reports them
|
|
// to the configured observer.
|
|
//
|
|
// The collection period is controlled by metadata.observerPeriod
|
|
// (default: 5s, minimum: 1s).
|
|
//
|
|
// # Retry behavior
|
|
//
|
|
// If observation fails (e.g., observer backend is temporarily
|
|
// unavailable), the events are buffered and retried on the next tick.
|
|
// During retry, new events are NOT collected — this prevents
|
|
// unbounded accumulation while the backend is unhealthy. Once the
|
|
// buffered events are successfully sent, the next tick resumes
|
|
// normal collection.
|
|
//
|
|
// This pattern is used consistently across all handler packages
|
|
// (http, http2, socks4/5, relay, tunnel, router, etc.).
|
|
func (h *routerHandler) observeStats(ctx context.Context) {
|
|
if h.options.Observer == nil {
|
|
return
|
|
}
|
|
|
|
var events []observer.Event
|
|
|
|
ticker := time.NewTicker(h.md.observerPeriod)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
// Retry buffered events from a previous failed attempt first.
|
|
if len(events) > 0 {
|
|
if err := h.options.Observer.Observe(ctx, events); err != nil {
|
|
continue
|
|
}
|
|
}
|
|
|
|
// Collect and send fresh events.
|
|
evs := h.stats.Events()
|
|
if len(evs) > 0 {
|
|
if err := h.options.Observer.Observe(ctx, evs); err != nil {
|
|
events = evs
|
|
continue
|
|
}
|
|
}
|
|
events = nil
|
|
|
|
case <-ctx.Done():
|
|
return
|
|
}
|
|
}
|
|
}
|