writeToNextLane теперь зовёт c.cancel() сразу, когда removeLane возвращает
left==0, вместо ожидания таймаута через 10ms sleep-loop. Убирает spin, когда
все lane пропали, а readLane уже отменил контекст.
Все N стримов теперь стартуют конкурентно. okchan оставлен только как лог-сигнал
первого успешного handshake; стримы 2..N больше не ждут завершения
DTLS-handshake стрима 1.
Открыть все кандидатные стримы сначала, затем писать Hello с len(opened), чтобы
bondserver.waitForInitialLanes не зависал на lane, которые никогда не
открывались (закрыты/ошибка). Также документирует, почему клиентский write-retry
не зацикливается как серверный: набор lane клиента фиксирован на время жизни
Handle.
Конкурентные вызыватели HandleAuthError могли видеть устаревшие счётчики ошибок,
очищенные до того, как стрим стал виден в connected-счётчике. Переупорядочено
так, что снимок stream-alive идёт первым.
- logx: добавить префиксы [DEBUG]/[INFO]/[WARN]/[ERROR]; уровни были
неразличимы в выводе (всё шло через голый log.Printf).
- logx: добавить хелпер OrNop для nil-проверок на стороне конструктора.
- vkauth/{client,token}.go: заменить прямой log.Printf на c.log.Infof
(97 мест обходили унифицированный логгер после прошлого рефакторинга).
- udprelay.oneTURN: буферизовать error chan (cap 1) + select на ctx.Done в
TURNLoop — небуферизованный send блокировался навсегда, когда TURNLoop
выходил по ctx до чтения, утекали горутина + TURN-стрим.
- udprelay: перенести write-цикл conn2->relay в wg.Go (был голый go func, мог
пережить wg.Wait и читать из закрытого conn).
- udprelay.TURNLoop: заменить time.Sleep(2s) на select+ctx, чтобы shutdown не
блокировался per-stream.
- udprelay.Run / tcpfwd.Run: возвращать error вместо log.Panicf при сбое listen;
cmd/* теперь log.Fatalf на всплывшей ошибке.
- cmd/*: log.Panicf → log.Fatalf для CLI-ошибок (без stack trace на плохой флаг).
- kcptun.NewKCPOverDTLS: закрывать серверный kcp.Listener на error-путях после
SetDeadline/AcceptKCP (утекал при любом сбое).
- doc пакета udprelay: переписать устаревшее утверждение "avoid importing
vkauth" (файл импортирует vkauth ради sentinel-ошибок).
- udprelay: lifecycle-сообщения ("Established/Closed DTLS connection")
Errorf → Infof; строка "relayed-address" под DebugEnabled использует Debugf.
- tcpfwd: lifecycle-сообщения ("connected/disconnected/waiting/listening")
Errorf → Infof, "failed to wrap listener" → Warnf.
- tcpfwd.maintainSession doc: "fixed 2-3s backoff" было неверно (3s при сбое
setup, 2s после disconnect).
- pool.go doc: "bond client (in main)" → internal/proxy/bondclient.
- srtpmimicry.Unwrap: doc предупреждает, что AEAD открывает in-place внутри
wire, поэтому вызыватели должны считать wire потреблённым.
- комментарии-алиасы GetCredsFunc udprelay/tcpfwd: убрать фикцию "cmd/main
wiring stability" (всё в internal/).
Build чисто, go vet чисто, все тесты проходят.
Заменяет прямой log.Printf на logger.Errorf/Debugf в каждом proxy-пакете
(udprelay, tcpfwd, bondclient, bondserver, udpserver, tcpfwdserver). Вывод
остаётся совместимым (под капотом stdlib log), но теперь может быть глобально
перенаправлен сменой реализации Logger.
Тесты + lint проходят.
Исходный план просил proxy.Engine + интерфейс Handler, объединяющий udprelay и
tcpfwd. После разбора кода заявленные ~70% пересечения оказались ближе к ~30
строкам: udprelay (WG → DTLS-terminate → wrap → TURN) и tcpfwd (TCP → smux →
KCP → DTLS → wrap → TURN) наслаивают wrap/DTLS по-разному и имеют разные
жизненные циклы. Engine с двумя if-ветками просто загнал бы дублирование внутрь,
а не убрал.
Объём минимальный: новый пакет internal/proxy/common с тем, что реально
идентично:
- тип GetCredsFunc (дублировался в обоих пакетах).
- DialTURN(ctx, host, port, udp, peer, link, streamID, getCreds) — берёт creds
и открывает turndial-стрим. Политика retry на auth-ошибке остаётся в udprelay.
- NewClientWrap(key) — возвращает (nil, nil) при пустом/коротком ключе, иначе
srtpmimicry.NewConn(key, false).
udprelay и tcpfwd теперь импортируют common.DialTURN / common.NewClientWrap.
Их локальные объявления GetCredsFunc становятся type alias на
common.GetCredsFunc, чтобы связка cmd/vk-turn-client компилировалась без churn.
bond-as-tcpfwd-option отменён вместе с интерфейсом Handler; bondclient остаётся
отдельным пакетом, подключённым через Deps.BondHandler.
Без изменений wire-формата, флагов CLI и поведения. go build / go vet /
go test / gofmt чисто.
internal/wrap → internal/wire/srtpmimicry; правки doc ("обфускация, не security").
internal/bond/{frame,client,server} → internal/wire/bondframe,
internal/proxy/bondclient, internal/proxy/bondserver.
internal/netadapt → internal/netconn.
internal/turnpipe → internal/transport/turndial; internal/dtlsdial →
internal/transport/dtlsdial.
tcputil → internal/transport/kcptun; selectedKCPProfile и selectedFEC теперь
кешируются через sync.Once (env читается один раз вместо per-connection).
internal/proxy/udp → internal/proxy/udprelay; internal/proxy/vless →
internal/proxy/tcpfwd. Флаг CLI -vless сохранён.
internal/ish → internal/client/ish; doc пакета расширен (iSH = Linux user-mode
эмулятор на iOS, только linux/386).
Чисто механическое переименование + обновление import-путей/идентификаторов.
Wire-формат, флаги CLI и поведение не изменены. go build + go test зелёные.
Добавляет internal/logx с Debugf/Infof/Warnf/Errorf + DebugEnabled().
Протягивает logx.Logger через bondclient, bondserver, proxy/udp, proxy/vless,
vkauth. Убирает глобалы isDebug/debugf из обоих бинарей. Переносит
globalBondRegistry из package init в main (инициализация после
config.ParseServer), закрывая давний footgun "Debug=false на init".
- server/main.go → cmd/vk-turn-server/main.go. Несёт остаточные правки сервера
(drop параметра useBond, убрано поле startup-лога).
- client/internal/{browserprofile,captcha,dnsdial,namegen,vkauth} →
internal/client/* (единая иерархия internal/; параллельное дерево
client/internal/ убрано). Все Go import-пути обновлены.
- .github/workflows/{ci,release}.yml, Dockerfile, README.md — пути сборки
переключены на ./cmd/vk-turn-client и ./cmd/vk-turn-server.
- README: убран устаревший пример "server -vless-bond" (флаг удалён ранее);
добавлены рецепты сборки .so для Android arm64 + Linux arm64/amd64 под
ручную упаковку для мобилок.
- internal/proxy/udp/udp.go, internal/config/config.go: обновлены устаревшие
ссылки client/internal/* в doc-комментариях.
- notes/GLOSSARY.md (новый, ~20 терминов): фиксирует DTLS-как-обфускацию,
wrap=AEAD-не-XOR, четыре значения "client", три значения "stream", пять
ортогональных переключателей "mode", заметки по сборке libvkturn.so, iSH,
хрупкость init globalBondRegistry и удаления флагов.
cmd/vk-turn-client/ намеренно оставлен вне этого коммита; сворачивается вместе
с извлечением ручной captcha в следующем коммите, т.к. переименование и правки
импортов manualcaptcha.* попадают в один файл.
Wire-форматы не тронуты. Поведение не изменено кроме уже закоммиченных
удалений CLI.
Переносит runVLESSMode/maintainVLESSSession/createSmuxSession/pipe и sessionPool
в internal/proxy/vless. Клиентская половина bond (handleBondedTCP, copyTCPToBond,
copyBondToTCP, bondClientLane) остаётся в client/main.go до этапа 5.1 —
инжектится в vless через Deps.BondHandler.
sessionPool/pooledSession экспортированы как SessionPool/PooledSession, чтобы
половина bond работала со счётчиками lane (ID/Sess/Opened/Active/Closed/
ToSession/FromSession).
Также дедуплицирует iSH (linux/386) listener-shim в новый пакет internal/ish;
client/manual_captcha.go и internal/proxy/vless импортируют его через
ish.WrapListener.
client/main.go: 895 → 552 (-343). Wire-формат и CLI не изменены.
Переносит oneDtlsConnection/oneTurnConnection и их retry-циклы из client/main.go
в internal/proxy/udp, за интерфейс AuthHandler, чтобы пакет оставался вне
client/internal/. UDPPacket/packetPool становятся udp.Packet/udp.Pool. Глобал
globalAppCancel убран в пользу Deps.AppCancel. turnParams пока остаётся в
client/main.go, т.к. vless ещё его использует; переедет с этапом 4.2.
client/main.go: 1195 → 895. Wire-формат и CLI не изменены.