Скрипт воркера писал в сокет Telegram так:
server.addEventListener("message", (event) => {
writer.write(chunk).catch(shutdown);
});
`write()` вызывался поверх незавершённого, `writer.ready` не спрашивался вовсе.
Пока в клиенте голодала отправка, настоящего потока вверх через воркер не
возникало, и код держался. В beta.12 голодание починили — поток появился, и
репортёр #42 сразу получил переподключения на обоих клиентах, которых на
beta.11 с тем же воркером не было.
Запись сериализована цепочкой промисов: следующий чанк уходит после того, как
записан предыдущий, и только когда писатель готов. Кто разворачивал воркер
раньше — нужен передеплой, о чём сказано в docs/CLOUDFLARE_WORKER.md.
Причина у репортёра не подтверждена: рантайма Workers у меня нет, проверить
можно только у него.
Заодно счётчик «промолчали». Соединение, которое открылось и ничего не
прислало за `IO_TIMEOUT`, закрывалось и не попадало ни в один счётчик:
`unknown_clients` растёт, только когда запрос пришёл и не разобрался, а не
когда его не дождались. Тот же репортёр сообщил, что его телефон
переустанавливает соединение примерно раз в десять секунд — ровно период
`IO_TIMEOUT`. Проверить это по диагностике было нечем, теперь есть чем.
Тесты: Ping через туннель (путь не был покрыт вовсе, а Ping бывает только на
маршруте воркера) и молчащий клиент. Второй гоняет виртуальное время, чтобы не
ждать десять секунд по-настоящему, — отсюда dev-зависимость на tokio/test-util.
Co-authored-by: by-sonic <171230345+by-sonic@users.noreply.github.com>
7.5 KiB
Резервный маршрут через свой Cloudflare Worker
Когда это нужно
Только в одном случае: провайдер заблокировал саму веб-инфраструктуру Telegram, и все обычные маршруты TGLock перестали отвечать.
Как это выглядит в приложении:
| Что видно | Что это значит | Нужен ли Worker |
|---|---|---|
| «Telegram на связи» | туннель работает | нет |
| «Ищем новый маршрут» и не проходит | маршруты перебираются и все падают | да |
| «Защита включена», DC не определяется | Telegram ещё не подключался | нет, открой Telegram |
| В диагностике счётчик сбоев растёт, туннелей 0 | ни один маршрут не отвечает | да |
В CLI то же самое видно в строке статуса: туннелей 0 · DC не определён · сбоев 14.
Если Telegram работает — ничего настраивать не надо. Поле Worker в настройках существует для случая, когда обычные маршруты умерли.
Обрати внимание: Worker не спасает, если Telegram недоступен с самого воркера. Он помогает, когда домены Telegram заблокированы у тебя, а датацентры Cloudflare до них дотягиваются.
Что понадобится
- аккаунт Cloudflare (бесплатного тарифа достаточно);
- 10 минут.
Ни своего сервера, ни домена, ни карты не нужно — воркер получит адрес вида имя.твой-логин.workers.dev.
Установка через веб-интерфейс
- Зайди на dash.cloudflare.com → Workers & Pages → Create application → Create Worker.
- Дай имя, например
tglock. Нажми Deploy — сначала задеплоится заготовка, это нормально. - Нажми Edit code.
- Удали всё содержимое редактора и вставь файл
worker/tglock-worker.jsиз этого репозитория целиком. - Deploy.
- Скопируй адрес воркера. Он показан сверху и выглядит как
tglock.имя.workers.dev— безhttps://и без пути.
Проверка, что воркер жив
Открой в браузере https://tglock.имя.workers.dev/apiws. Должно вернуться expected a websocket upgrade — это правильный ответ: значит код развёрнут и работает, просто браузер пришёл обычным запросом.
Если вернулось not found — проверь, что путь именно /apiws. Если ошибка про cloudflare:sockets — у воркера слишком старая дата совместимости, поставь в Settings → Compatibility date сегодняшнюю.
Разворачивал воркер до 2.0.0-beta.14 — обнови скрипт. В прежней версии запись в сокет Telegram шла без ожидания предыдущей и без backpressure. Пока в клиенте голодала отправка, через воркер не проходило настоящего потока вверх и это не проявлялось; после того как голодание починили в beta.12, поток появился.
Подключение в TGLock
В приложении: Настройки → поле Cloudflare Worker → вставь tglock.имя.workers.dev → Сохранить. Настройки меняются только при выключенной защите.
В CLI: флаг --worker, можно повторять:
tglock-cli --worker tglock.имя.workers.dev
tglock-cli --worker первый.workers.dev --worker второй.workers.dev
Worker всегда пробуется последним, после всех маршрутов Telegram. Пока обычные маршруты живы, трафик через него не пойдёт, и это осознанно: чужая инфраструктура в цепочке — это лишнее звено, а не улучшение.
Ограничение доступа
Адрес воркера сам по себе секрет, но лучше поставить токен: Settings → Variables → Add variable, имя TGLOCK_TOKEN, значение — любая длинная строка.
Пока переменная не задана, проверка токена выключена. Когда задана — воркер начнёт отвечать 403 без параметра ?token=. Клиент TGLock этот параметр пока не отправляет, так что включать токен есть смысл, если ты правишь и сам скрипт, и адрес.
Независимо от токена воркер соединяется только с семью адресами Telegram, которые запрашивает TGLock. Любой другой dst получает 403, так что открытым TCP-прокси он не станет.
Контракт
Если захочешь написать свою реализацию — вот что именно делает клиент (src/transport.rs):
wss://<домен>/apiws?dst=<telegram-ip>&dc=<номер-dc>
Sec-WebSocket-Protocol: binary
dst— адрес Telegram, к которому нужно подключиться по TCP на порт 443;dc— номер датацентра, для логов;- подпротокол
binaryобязательно нужно подтвердить в ответе — без этого клиент разорвёт рукопожатие; - дальше бинарные frames пересылаются в обе стороны без изменений;
- TLS до самого воркера обеспечивает Cloudflare.
Список допустимых dst совпадает с transport::worker_allowed_destinations().
Честно про проверку
Скрипт написан по контракту, вычитанному из исходников клиента, и путь с параметрами закреплён тестом connects_through_the_documented_worker_contract — он поднимает локальный сервер, который ведёт себя ровно так, как описано выше, и проверяет, что туннель через него поднимается и данные доходят в обе стороны.
Чего этот тест не проверяет: развёрнутый воркер в самом Cloudflare. Если что-то не сойдётся с их API — открой issue, поправлю.