From bafdcdd027c29ee52f605af2d6d83acf9827855d Mon Sep 17 00:00:00 2001 From: samosvalishe Date: Fri, 19 Jun 2026 15:09:32 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20=D1=83=D1=81=D1=82=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=B1=D0=BB=D0=BE=D0=BA=D0=B5=D1=80=D1=8B?= =?UTF-8?q?=20=D0=BB=D0=B8=D0=BD=D1=82=D0=B5=D1=80=D0=B0=20=D0=B2=20obf/mu?= =?UTF-8?q?lti?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit errcheck nolint на bufPool.Get в rtpopus3/listen.go; multi.MultiProvider переименован в multi.Provider (revive stutter), дедуп vk.New в buildProvider. --- cmd/client/main.go | 42 +++++---------- internal/provider/multi/multi.go | 88 ++++++++------------------------ internal/wire/rtpopus3/listen.go | 4 +- 3 files changed, 38 insertions(+), 96 deletions(-) diff --git a/cmd/client/main.go b/cmd/client/main.go index 1ddc34e..86a20b5 100644 --- a/cmd/client/main.go +++ b/cmd/client/main.go @@ -130,14 +130,9 @@ func main() { return c.User, c.Pass, c.ServerAddrs, nil } - // При нескольких звонках (links) расширяем пул: каждый звонок даёт - // cfg.TURN.N стримов, все объединяются в общий SessionPool. - // Это даёт больше TURN-соединений → выше aggregate пропускная способность. - numLinks := len(cfg.VK.Links) - if numLinks < 1 { - numLinks = 1 - } - totalStreams := cfg.TURN.N * numLinks + // Несколько -links расширяют пул: каждый звонок даёт cfg.TURN.N стримов, + // все объединяются в общий пул (больше параллельных TURN-аллокаций). + totalStreams := cfg.TURN.N * max(len(cfg.VK.Links), 1) if cfg.Proxy.Mode != config.ProxyModeUDP { tcpDtlsDialer := &dtlsdial.Dialer{ @@ -192,32 +187,16 @@ func main() { } } -// buildProvider выбирает реализацию provider.Provider по cfg.Provider.Name. -// При нескольких -links создаёт MultiProvider с одним vk.Provider на каждый link. +// buildProvider строит provider.Provider по cfg.Provider.Name. Одна -links даёт +// обычный vk.Provider; несколько - multi.Provider (один vk.Provider на ссылку). func buildProvider(cfg *config.Client, dialer net.Dialer, connected *atomic.Int32, logger logx.Logger) (provider.Provider, error) { switch cfg.Provider.Name { case config.ProviderVK: if len(cfg.VK.Links) == 0 { return nil, fmt.Errorf("vk: no links configured") } - // Одна ссылка — обычный vk.Provider (backward compat). - if len(cfg.VK.Links) == 1 { + newVK := func(link string) (provider.Provider, error) { return vk.New(vk.Config{ - Link: cfg.VK.Links[0], - Dialer: dialer, - ManualOnly: cfg.VK.ManualCaptcha, - Browser: string(cfg.VK.Browser), - StreamsPerCache: cfg.VK.StreamsPerCred, - StreamsAlive: connected.Load, - Log: logger, - Debug: cfg.Log.Debug, - }, vk.DefaultManualSolver) - } - // Несколько ссылок — MultiProvider. Каждый звонок даёт свой пул - // TURN-реквизитов → больше параллельных стримов. - providers := make([]provider.Provider, 0, len(cfg.VK.Links)) - for i, link := range cfg.VK.Links { - p, err := vk.New(vk.Config{ Link: link, Dialer: dialer, ManualOnly: cfg.VK.ManualCaptcha, @@ -227,12 +206,19 @@ func buildProvider(cfg *config.Client, dialer net.Dialer, connected *atomic.Int3 Log: logger, Debug: cfg.Log.Debug, }, vk.DefaultManualSolver) + } + if len(cfg.VK.Links) == 1 { + return newVK(cfg.VK.Links[0]) + } + providers := make([]provider.Provider, 0, len(cfg.VK.Links)) + for i, link := range cfg.VK.Links { + p, err := newVK(link) if err != nil { return nil, fmt.Errorf("vk provider [%d]: %w", i, err) } providers = append(providers, p) } - logger.Infof("created MultiProvider with %d VK links (%d total streams)", len(providers), cfg.TURN.N*len(providers)) + logger.Infof("multi-provider: %d VK links, %d total streams", len(providers), cfg.TURN.N*len(providers)) return multi.New(providers), nil default: return nil, fmt.Errorf("unknown provider %q", cfg.Provider.Name) diff --git a/internal/provider/multi/multi.go b/internal/provider/multi/multi.go index 3939ca2..105efac 100644 --- a/internal/provider/multi/multi.go +++ b/internal/provider/multi/multi.go @@ -1,6 +1,6 @@ -// Package multi - MultiProvider: объединяет несколько provider.Provider -// в один, распределяя streamID по всем провайдерам round-robin. -// Позволяет использовать несколько VK-звонков одновременно для пула TURN-стримов. +// Package multi объединяет несколько provider.Provider в один, распределяя +// streamID по вложенным провайдерам round-robin. Применение: несколько +// VK-звонков как единый пул TURN-стримов (больше параллельных аллокаций). package multi import ( @@ -10,102 +10,58 @@ import ( "github.com/samosvalishe/free-turn-proxy/internal/provider" ) -// TCP mode (-mode tcp): -// Локальный TCP-поток (Xray) -// -// │ -// ├─ без bond: pool.Pick() → round-robin по всем 30 smux-сессиям -// └─ с bond: bondclient.Handle → страйпинг по всем 30 сессиям -// │ -// │ Каждая smux-сессия: -// │ DTLS → KCP → TURN allocate → Server DTLS -// │ -// ▼ -// Backend (Xray TCP) -// -// UDP mode (-mode udp): -// Локальный UDP (WireGuard) -// -// │ -// ▼ -// inboundChan → broadcast во все 30 DTLS-стримов -// │ -// │ Каждый DTLS-стрим: -// │ (DTLS) → OBF → TURN allocate → Server DTLS -// │ -// ▼ -// Backend (WireGuard UDP) -// -// MultiProvider распределяет streamID по вложенным провайдерам. -// StreamID 1-based: (streamID-1) % n → выбор провайдера. -// Внутренний streamID: ((streamID-1) / n) + 1 → передаётся выбранному провайдеру. -// -// Например, 3 провайдера × 10 стримов = 30 стримов: -// -// streamID 1,4,7,10,13,16,19,22,25,28 → provider[0] (internal 1..10) -// streamID 2,5,8,11,14,17,20,23,26,29 → provider[1] (internal 1..10) -// streamID 3,6,9,12,15,18,21,24,27,30 → provider[2] (internal 1..10) -type MultiProvider struct { +// Provider распределяет 1-based streamID round-robin: провайдер = (streamID-1) % n, +// внутренний streamID = ((streamID-1) / n) + 1 (биекция, без коллизий). +// Без изменяемого состояния - thread-safe. +type Provider struct { providers []provider.Provider n int } -// New создаёт MultiProvider. Паникует если providers пуст. -func New(providers []provider.Provider) *MultiProvider { +func New(providers []provider.Provider) *Provider { if len(providers) == 0 { panic("multi: empty providers") } - return &MultiProvider{ - providers: providers, - n: len(providers), - } + return &Provider{providers: providers, n: len(providers)} } -// providerFor возвращает провайдера и внутренний streamID для глобального streamID. -func (m *MultiProvider) providerFor(streamID int) (provider.Provider, int) { +func (m *Provider) providerFor(streamID int) (provider.Provider, int) { idx := (streamID - 1) % m.n innerID := ((streamID - 1) / m.n) + 1 return m.providers[idx], innerID } -// GetCredentials делегирует выбранному провайдеру. -func (m *MultiProvider) GetCredentials(ctx context.Context, streamID int) (provider.Credentials, error) { +func (m *Provider) GetCredentials(ctx context.Context, streamID int) (provider.Credentials, error) { p, innerID := m.providerFor(streamID) return p.GetCredentials(ctx, innerID) } -// IsAuthError делегирует первому провайдеру (все VK провайдеры используют -// одинаковую эвристику auth-ошибки — проверку строки ошибки). -func (m *MultiProvider) IsAuthError(err error) bool { +// IsAuthError делегирует первому провайдеру: все вложенные однотипны (VK). +func (m *Provider) IsAuthError(err error) bool { return m.providers[0].IsAuthError(err) } -// HandleAuthError делегирует провайдеру, соответствующему streamID. -func (m *MultiProvider) HandleAuthError(streamID int) bool { +func (m *Provider) HandleAuthError(streamID int) bool { p, innerID := m.providerFor(streamID) return p.HandleAuthError(innerID) } -// ResetErrors делегирует провайдеру, соответствующему streamID. -func (m *MultiProvider) ResetErrors(streamID int) { +func (m *Provider) ResetErrors(streamID int) { p, innerID := m.providerFor(streamID) p.ResetErrors(innerID) } -// BackoffUntilUnix возвращает максимальный backoff среди всех провайдеров. -// Консервативно: если хотя бы один провайдер в блокировке, все стримы -// делают короткую паузу вместо tight-spin. -func (m *MultiProvider) BackoffUntilUnix() int64 { - var max int64 +// BackoffUntilUnix - максимум среди провайдеров (один в блокировке -> пауза для всех). +func (m *Provider) BackoffUntilUnix() int64 { + var until int64 for _, p := range m.providers { - if v := p.BackoffUntilUnix(); v > max { - max = v + if v := p.BackoffUntilUnix(); v > until { + until = v } } - return max + return until } -// Name возвращает "multi(N)". -func (m *MultiProvider) Name() string { +func (m *Provider) Name() string { return fmt.Sprintf("multi(%d)", m.n) } diff --git a/internal/wire/rtpopus3/listen.go b/internal/wire/rtpopus3/listen.go index 0be01b5..9d1e3ee 100644 --- a/internal/wire/rtpopus3/listen.go +++ b/internal/wire/rtpopus3/listen.go @@ -59,7 +59,7 @@ type packetConn struct { } func (c *packetConn) ReadFrom(p []byte) (int, net.Addr, error) { - bp := bufPool.Get().(*[]byte) + bp := bufPool.Get().(*[]byte) //nolint:errcheck // pool New always returns *[]byte buf := *bp need := len(p) + overhead if cap(buf) < need { @@ -86,7 +86,7 @@ func (c *packetConn) ReadFrom(p []byte) (int, net.Addr, error) { func (c *packetConn) WriteTo(p []byte, addr net.Addr) (int, error) { wireLen := overhead + len(p) - bp := bufPool.Get().(*[]byte) + bp := bufPool.Get().(*[]byte) //nolint:errcheck // pool New always returns *[]byte out := *bp if cap(out) < wireLen { out = make([]byte, wireLen)