e791ba47e2
Breaking Change: moves Connector, ConnectorPool, parseTunnelID from tunnel.go into dedicated connector.go and id.go. Bug Fixes: - dialer.go: clear stale retry err so SD fallback is not masked (nil err from pool.Get() left previous iteration's error live) - connect.go: check WriteTo errors in both mux and non-mux paths - eprelay.go: use fresh relay.Response for features sent over mux (previously reused same resp struct after resp.WriteTo(muxConn)) Refactor: - entrypoint.go: split ~790 lines into 6 files (ephttp, eptls, eprelay, epwebsocket, eplistener, entrypointsvc), moved to dedicated package - handler.go: moved initEntrypoints/createEntrypointService to entrypointsvc.go - tunnel.go: moved Connector/ConnectorPool/parseTunnelID to connector.go and id.go - connect.go: moved entrypoint-originated handleConnect to eprelay.go (handler-side handleConnect kept in connect.go) Tests (48 new, 88 total): - id_test.go: parseTunnelID variants (empty, UUID, private '$', invalid) - tunnel_test.go: Tunnel lifecycle (AddConnector, GetConnector weighted/closed filtering, CloseOnIdle, clean), Connector (NewConnector nil-opts, GetConn nil/closed session, Close, IsClosed), ConnectorPool (Add/Get/Close, idle cleanup, concurrency) - dialer_test.go: SD error paths (sd.Get error, empty address) - handler_test.go: observeStats (nil observer, cancelled context loop), initEntrypoints (no entrypoints configured) - helpers_test.go: testLogger, fakeConn for handler tests - eprelay_test.go: handleConnect ingress/dial/SD round-trip
115 lines
2.6 KiB
Go
115 lines
2.6 KiB
Go
package tunnel
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/go-gost/core/ingress"
|
|
"github.com/go-gost/core/listener"
|
|
"github.com/go-gost/core/service"
|
|
xnet "github.com/go-gost/x/internal/net"
|
|
xrecorder "github.com/go-gost/x/recorder"
|
|
xservice "github.com/go-gost/x/service"
|
|
)
|
|
|
|
func (h *tunnelHandler) initEntrypoints() (err error) {
|
|
if h.md.entryPoint != "" {
|
|
svc, err := h.createEntrypointService(h.md.entryPoint, h.md.ingress)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
go svc.Serve()
|
|
|
|
h.entrypoints = append(h.entrypoints, svc)
|
|
h.log.Infof("entrypoint: %s", svc.Addr())
|
|
}
|
|
|
|
for _, ep := range h.md.entrypoints {
|
|
if ep.Addr == "" {
|
|
continue
|
|
}
|
|
svc, err := h.createEntrypointService(ep.Addr, ep.Ingress)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
go svc.Serve()
|
|
|
|
h.entrypoints = append(h.entrypoints, svc)
|
|
h.log.Infof("entrypoint: %s %s", ep.Name, svc.Addr())
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (h *tunnelHandler) createEntrypointService(addr string, ingress ingress.Ingress) (service.Service, error) {
|
|
ep := &entrypoint{
|
|
node: h.id,
|
|
service: h.options.Service,
|
|
pool: h.pool,
|
|
ingress: ingress,
|
|
sd: h.md.sd,
|
|
log: h.log.WithFields(map[string]any{
|
|
"kind": "entrypoint",
|
|
}),
|
|
sniffingWebsocket: h.md.sniffingWebsocket,
|
|
websocketSampleRate: h.md.sniffingWebsocketSampleRate,
|
|
readTimeout: h.md.entryPointReadTimeout,
|
|
}
|
|
ep.transport = &http.Transport{
|
|
DialContext: ep.dial,
|
|
IdleConnTimeout: 30 * time.Second,
|
|
ResponseHeaderTimeout: h.md.entryPointReadTimeout,
|
|
DisableKeepAlives: !h.md.entryPointKeepalive,
|
|
DisableCompression: !h.md.entryPointCompression,
|
|
}
|
|
|
|
for _, ro := range h.options.Recorders {
|
|
if ro.Record == xrecorder.RecorderServiceHandler {
|
|
ep.recorder = ro
|
|
break
|
|
}
|
|
}
|
|
|
|
network := "tcp"
|
|
if xnet.IsIPv4(addr) {
|
|
network = "tcp4"
|
|
}
|
|
|
|
ln, err := net.Listen(network, addr)
|
|
if err != nil {
|
|
h.log.Error(err)
|
|
return nil, err
|
|
}
|
|
|
|
serviceName := fmt.Sprintf("%s-ep-%s", h.options.Service, ln.Addr())
|
|
log := h.log.WithFields(map[string]any{
|
|
"service": serviceName,
|
|
"listener": "tcp",
|
|
"handler": "tunnel-ep",
|
|
"kind": "service",
|
|
})
|
|
epListener := newTCPListener(ln,
|
|
listener.AddrOption(addr),
|
|
listener.ServiceOption(serviceName),
|
|
listener.ProxyProtocolOption(h.md.entryPointProxyProtocol),
|
|
listener.LoggerOption(log.WithFields(map[string]any{
|
|
"kind": "listener",
|
|
})),
|
|
)
|
|
if err = epListener.Init(nil); err != nil {
|
|
return nil, err
|
|
}
|
|
epHandler := &entrypointHandler{
|
|
ep: ep,
|
|
}
|
|
if err = epHandler.Init(nil); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return xservice.NewService(
|
|
serviceName, epListener, epHandler,
|
|
xservice.LoggerOption(log),
|
|
), nil
|
|
} |