
Transcripció en directe quan el tallafocs bloqueja els WebSockets
A les xarxes corporatives els encanta permetre HTTPS i matar en silenci les actualitzacions de WebSocket. Això trenca silenciosament la transcripció en temps real. GeekBye v2.0.8 recorre automàticament a un transport purament HTTPS — i publicar-ho va destapar un bug que hauria fet inútil tota la funció.
Hi ha una manera concreta i exasperant que la transcripció en temps real té de fallar en una xarxa corporativa. El teu Wi-Fi va bé. HTTPS funciona — pots carregar qualsevol web. Però la transcripció en directe simplement... no arrenca, i res no et diu per què.
El culpable és una mena de proxy corporatiu que permet el trànsit HTTPS ordinari però bloqueja l'actualització de WebSocket — el handshake que converteix una connexió HTTPS en el canal persistent i bidireccional que necessita la transcripció en temps real. Per al proxy, un WebSocket sembla un túnel no monitoritzat cap a fora de la xarxa, així que el mata. Per a tu, la transcripció està trencada en silenci.
GeekBye v2.0.8 va afegir una reserva automàtica per a exactament això — i construir-la va destapar un bug que hauria fet que tota la funció no fes res.
Per què una reserva, no simplement un reintent
Ja gestionem les xarxes inestables. Si la teva connexió cau a mitja sessió, GeekBye es reconnecta amb backoff i emmagatzema el teu àudio en un buffer perquè no es perdi res — això és una funció a part, coberta a per què el teu assistent de notes amb IA s'atura amb mal Wi-Fi.
Però un WebSocket bloquejat no és una connexió inestable. Reintentar el mateix WebSocket contra un proxy que rebutja els WebSockets falla igual cada vegada, per sempre. L'única solució és un transport diferent — un que sembli el HTTPS pla que el proxy ja permet.
Així que v2.0.8 recorre a un transport purament HTTPS pel mateix endpoint autenticat:
- Cap avall (les transcripcions que et tornen): server-sent events — una resposta HTTPS de llarga durada que el proxy veu com una descàrrega en streaming ordinària.
- Cap amunt (el teu àudio que surt): peticions POST per lots, cadascuna portant un tros d'àudio amb un número de seqüència perquè el servidor les pugui reassemblar en ordre encara que la xarxa les reordeni.
Cap socket persistent, res que sembli un túnel — només peticions i respostes HTTPS. Si un proxy et permet fer servir una web, permet això.
El bug que hauria publicat una funció morta
Aquí ve la part que val la pena llegir. La reserva se suposa que s'ha d'activar quan la connexió WebSocket esgota els seus intents amb una signatura de transport bloquejat — cada intent fallant en l'actualització, cap problema d'autenticació ni de quota, com a mínim un rebuig amb forma de proxy. Un proxy que bloqueja un WebSocket normalment respon l'actualització amb un HTTP 403 Forbidden o 407.
El problema: el nostre codi de connexió ja tenia una regla que un 403 significa error d'autenticació fatal — atura't, mostra'l a l'usuari, no reintentis. Cosa que és correcta gairebé pertot. Però volia dir que el 403 d'un proxy que bloqueja — exactament el senyal que hauria d'haver activat la reserva — en canvi es llançava com un error fatal abans que la lògica de reserva pogués arribar a executar-se. Només un tall de connexió cru (un tancament 1006) queia fins a la reserva. Així que la funció hauria funcionat per al cas rar i hauria fallat en silenci per al seu objectiu principal real: el proxy corporatiu.
Ho vam caçar mentre enduríem el llançament, no en producció. La solució: un 403/407 a la fase d'actualització del WebSocket ara es tracta com a recuperable perquè el bucle de connexió pugui esgotar-se fins a la reserva — mentre que una fallada d'autenticació genuïna (que arriba de manera diferent, després que l'actualització tingui èxit) encara falla ràpid, exactament com abans. Un test de regressió ara fixa la distinció: un 403 de proxy bloquejat ha de recórrer a la reserva; un 403 d'autenticació real no.
La resta de l'enduriment va seguir la mateixa línia paranoica: un timeout a cada POST cap amunt perquè un proxy que accepta una petició però no respon mai no pugui encallar el flux d'àudio, i una garantia que un problema genuí d'inici de sessió no pugui quedar mai emmascarat en silenci per la maquinària de reserva.
Ho vam provar contra un proxy hostil de veritat
Una funció el propòsit sencer de la qual és sobreviure a xarxes hostils no es pot validar només amb tests unitaris — els tests unitaris no tenen proxies. Abans d'activar-la, vam fer passar l'app real per un reverse proxy local configurat per fer exactament el que fan els proxies corporatius: reenviar HTTPS, rebutjar les actualitzacions de WebSocket amb un 403.
El rastre als logs és el rebut: quatre intents de WebSocket bloquejats, la signatura d'esgotament reconeguda, el canvi automàtic al transport HTTPS, i després una sessió de transcripció sana de 96 segons per HTTPS pur — 66 segments de transcripció, zero pèrdues. El failover funciona perquè el vam veure fer el relleu.
Tres lliçons transferibles
- "Funciona amb Wi-Fi inestable" i "funciona darrere d'un proxy hostil" són garanties diferents. Una necessita reconnexió; l'altra necessita un transport diferent. Confondre-les deixa tota una població d'usuaris corporatius trencats en silenci.
- La teva classificació d'errors pot amagar la teva pròpia funció d'ella mateixa. Una regla que és correcta el 99% del temps (403 = auth fatal) era exactament errònia per a l'1% per servir el qual existia aquesta funció. Quan afegeixes una reserva, audita si la condició d'activació pot arribar tan sols a la reserva.
- Prova l'adversari, no només el camí feliç. L'única prova honesta de "sobreviu a un proxy que bloqueja WebSockets" és un proxy que bloqueja WebSockets. En vam construir un.
GeekBye v2.0.8 va publicar la reserva HTTPS protegida per flag i validada. Per a la feina de fiabilitat que l'acompanya, mira per què el teu assistent de notes amb IA s'atura amb mal Wi-Fi, i per als llançaments veïns d'aquesta sèrie, per què el teu assistent de notes amb IA deixa de gravar a mitja reunió (v2.0.9) i per què la transcripció amb IA sent malament els termes tècnics (v2.0.11).