
Транскрипція в реальному часі, коли файрвол блокує WebSocket
Корпоративні мережі обожнюють пропускати HTTPS і тихцем убивати upgrade до WebSocket. Це непомітно ламає транскрипцію в реальному часі. GeekBye v2.0.8 автоматично переходить на чисто-HTTPS транспорт — а його випуск викрив баг, який зробив би всю функцію непотрібною.
Є один конкретний, до сказу дратівливий спосіб, яким транскрипція в реальному часі відмовляє в корпоративній мережі. Ваш Wi-Fi у порядку. HTTPS працює — ви можете відкрити будь-який сайт. Але транскрипція наживо просто... не запускається, і ніщо не каже вам чому.
Винуватець — клас корпоративного proxy, яке пропускає звичайний HTTPS-трафік, але блокує upgrade до WebSocket — рукостискання, що перетворює HTTPS-з’єднання на постійний двосторонній канал, який потрібен транскрипції в реальному часі. Для proxy WebSocket виглядає як немоніторований тунель назовні з мережі, тож воно його вбиває. Для вас транскрипція непомітно зламана.
GeekBye v2.0.8 додав автоматичний fallback саме на цей випадок — і його створення викрило баг, через який уся функція не робила б нічого.
Чому fallback, а не просто повтор
Ми вже даємо раду нестабільним мережам. Якщо ваше з’єднання обривається посеред сесії, GeekBye перепідключається з backoff і буферизує ваше аудіо, щоб нічого не пропало, — це окрема функція, описана в чому ваш ШІ-нотатник зупиняється на поганому Wi-Fi.
Але заблокований WebSocket — це не нестабільне з’єднання. Повтор того самого WebSocket проти proxy, яке відмовляє WebSocket’ам, відмовляє однаково щоразу, вічно. Єдине виправлення — інший транспорт, який виглядає як простий HTTPS, що proxy вже пропускає.
Тож v2.0.8 переходить на чисто-HTTPS транспорт через той самий автентифікований endpoint:
- Вниз (транскрипти, що повертаються до вас): server-sent events — довгоживуча HTTPS-відповідь, яку proxy бачить як звичайне потокове завантаження.
- Вгору (ваше аудіо, що йде назовні): батчеві POST-запити, кожен несе фрагмент аудіо з порядковим номером, щоб сервер міг зібрати їх по порядку, навіть якщо мережа їх переставить.
Жодного постійного сокета, нічого, що виглядає як тунель, — лише HTTPS-запити й відповіді. Якщо proxy дозволяє вам користуватися сайтом, воно дозволяє й це.
Баг, який випустив би мертву функцію
Ось частина, заради якої варто читати. Fallback має спрацьовувати, коли WebSocket-з’єднання вичерпує свої спроби із сигнатурою заблокованого транспорту — кожна спроба відмовляє на upgrade, жодних проблем з auth чи лімітом, щонайменше одне відхилення у формі proxy. Proxy, що блокує WebSocket, зазвичай відповідає на upgrade HTTP 403 Forbidden або 407.
Проблема: у нашому коді з’єднання вже було правило, що 403 означає фатальну помилку автентифікації — зупинись, покажи її користувачеві, не повторюй. Що правильно майже всюди. Але це означало, що 403 від блокуючого proxy — рівно той сигнал, який мав запустити fallback — натомість викидалося як фатальна помилка перш ніж логіка fallback узагалі могла запуститися. Лише сирий обрив з’єднання (закриття 1006) провалювався далі до fallback. Тож функція працювала б для рідкісного випадку і тихцем відмовляла б для своєї справжньої головної цілі: корпоративного proxy.
Ми спіймали це під час загартування релізу, а не в продакшені. Виправлення: 403/407 на upgrade-етапі WebSocket тепер трактується як відновлюване, щоб цикл з’єднання міг вичерпатися у fallback, — тоді як справжня відмова автентифікації (яка приходить інакше, після успішного upgrade) досі відмовляє швидко, рівно як раніше. Регресійний тест тепер закріплює цю різницю: 403 від заблокованого proxy має піти у fallback; справжнє 403 auth — ні.
Решта загартування пішла тією самою параноїдальною лінією: тайм-аут на кожному вихідному POST, щоб proxy, яке приймає запит, але ніколи не відповідає, не могло застопорити аудіопотік, і гарантія, що справжня проблема входу ніколи не може бути тихцем замаскована машинерією fallback.
Ми протестували це проти справжнього ворожого proxy
Функцію, увесь сенс якої — виживати у ворожих мережах, не можна перевірити самими юніт-тестами — у юніт-тестів немає proxy. Перш ніж увімкнути її, ми прогнали реальний застосунок через локальне reverse proxy, налаштоване робити рівно те, що роблять корпоративні proxy: пропускати HTTPS, відхиляти upgrade’и WebSocket із 403.
Слід у логах — це чек: чотири заблоковані спроби WebSocket, розпізнана сигнатура вичерпання, автоматичне перемикання на HTTPS-транспорт, а потім здорова 96-секундна сесія транскрипції по чистому HTTPS — 66 сегментів транскрипту, нуль втрат. Failover працює, бо ми бачили, як він перемикається.
Три переносні уроки
- «Працює на нестабільному Wi-Fi» і «працює за ворожим proxy» — це різні гарантії. Одній потрібне перепідключення; іншій потрібен інший транспорт. Змішувати їх — значить лишити цілу популяцію корпоративних користувачів тихцем зламаною.
- Ваша класифікація помилок може сховати вашу ж функцію від неї самої. Правило, вірне у 99% випадків (403 = фатальний auth), було рівно хибним для того 1%, заради якого ця функція й існувала. Коли ви додаєте fallback, перевірте, чи умова спрацювання взагалі може дійти до fallback.
- Тестуйте супротивника, а не лише щасливий шлях. Єдиний чесний тест «виживає proxy, що блокує WebSocket» — це proxy, що блокує WebSocket. Ми його побудували.
GeekBye v2.0.8 випустив HTTPS-fallback за прапорцем і перевіреним. Про сусідню з ним роботу над надійністю дивіться чому ваш ШІ-нотатник зупиняється на поганому Wi-Fi, а про сусідні релізи цієї серії — чому ваш ШІ-нотатник зупиняє запис посеред зустрічі (v2.0.9) і чому ШІ-транскрипція перекручує технічні терміни (v2.0.11).