
Live μεταγραφή όταν το firewall μπλοκάρει τα WebSockets
Τα εταιρικά δίκτυα λατρεύουν να επιτρέπουν το HTTPS και να σκοτώνουν σιωπηλά τα WebSocket upgrades. Αυτό σπάει αθόρυβα τη μεταγραφή σε πραγματικό χρόνο. Το GeekBye v2.0.8 πέφτει αυτόματα σε ένα καθαρό HTTPS transport — και η κυκλοφορία του ξέθαψε ένα bug που θα έκανε ολόκληρο το feature άχρηστο.
Υπάρχει ένας συγκεκριμένος, εξοργιστικός τρόπος με τον οποίο η μεταγραφή σε πραγματικό χρόνο αποτυγχάνει σε ένα εταιρικό δίκτυο. Το Wi-Fi σου είναι μια χαρά. Το HTTPS δουλεύει — μπορείς να φορτώσεις οποιοδήποτε site. Αλλά η live μεταγραφή απλώς... δεν ξεκινά, και τίποτα δεν σου λέει γιατί.
Ο ένοχος είναι μια κατηγορία εταιρικών proxy που επιτρέπουν την κανονική HTTPS κίνηση αλλά μπλοκάρουν το WebSocket upgrade — το handshake που μετατρέπει μια HTTPS σύνδεση στο μόνιμο, αμφίδρομο κανάλι που χρειάζεται η μεταγραφή σε πραγματικό χρόνο. Για το proxy, ένα WebSocket μοιάζει με ένα αμονιτόριστο tunnel προς τα έξω από το δίκτυο, οπότε το σκοτώνει. Για σένα, η μεταγραφή είναι σιωπηλά σπασμένη.
Το GeekBye v2.0.8 πρόσθεσε ένα αυτόματο fallback ακριβώς για αυτό — και η κατασκευή του ανέδειξε ένα bug που θα έκανε ολόκληρο το feature να μην κάνει τίποτα.
Γιατί fallback, όχι απλώς retry
Ήδη χειριζόμαστε τα ασταθή δίκτυα. Αν η σύνδεσή σου πέσει στη μέση μιας session, το GeekBye ξανασυνδέεται με backoff και κάνει buffer το audio σου ώστε να μη χαθεί τίποτα — αυτό είναι ένα ξεχωριστό feature που καλύπτεται στο γιατί ο AI notetaker σου σταματά σε κακό Wi-Fi.
Αλλά ένα μπλοκαρισμένο WebSocket δεν είναι μια ασταθής σύνδεση. Το να ξαναδοκιμάζεις το ίδιο WebSocket απέναντι σε ένα proxy που αρνείται τα WebSockets αποτυγχάνει με τον ίδιο τρόπο κάθε φορά, για πάντα. Η μόνη λύση είναι ένα διαφορετικό transport — ένα που μοιάζει με το απλό HTTPS που το proxy ήδη επιτρέπει.
Έτσι το v2.0.8 πέφτει σε ένα καθαρό HTTPS transport πάνω από το ίδιο authenticated endpoint:
- Προς τα κάτω (οι μεταγραφές που επιστρέφουν σε σένα): server-sent events — μια μακρόβια HTTPS απόκριση που το proxy βλέπει ως ένα συνηθισμένο streamed download.
- Προς τα πάνω (το audio σου που βγαίνει): ομαδοποιημένα POST requests, το καθένα μεταφέροντας ένα κομμάτι audio με έναν αριθμό ακολουθίας ώστε ο server να μπορεί να τα ξανασυναρμολογήσει με τη σειρά ακόμη κι αν το δίκτυο τα αναδιατάξει.
Κανένα μόνιμο socket, τίποτα που να μοιάζει με tunnel — μόνο HTTPS requests και responses. Αν ένα proxy σου επιτρέπει να χρησιμοποιήσεις ένα website, επιτρέπει και αυτό.
Το bug που θα κυκλοφορούσε ένα νεκρό feature
Να το κομμάτι που αξίζει το διάβασμα. Το fallback υποτίθεται ότι ενεργοποιείται όταν η WebSocket σύνδεση εξαντλεί τις προσπάθειές της με μια υπογραφή μπλοκαρισμένου transport — κάθε προσπάθεια αποτυγχάνει στο upgrade, κανένα πρόβλημα auth ή quota, τουλάχιστον μία απόρριψη με μορφή proxy. Ένα proxy που μπλοκάρει ένα WebSocket συνήθως απαντά στο upgrade με ένα HTTP 403 Forbidden ή 407.
Το πρόβλημα: ο κώδικας σύνδεσής μας είχε ήδη έναν κανόνα ότι ένα 403 σημαίνει fatal authentication error — σταμάτα, δείξ' το στον χρήστη, μην ξαναδοκιμάσεις. Πράγμα σωστό σχεδόν παντού. Αλλά σήμαινε ότι το 403 από ένα μπλοκάρον proxy — το ακριβές σήμα που θα έπρεπε να είχε ενεργοποιήσει το fallback — αντ' αυτού πεταγόταν ως fatal error πριν καν μπορέσει ποτέ να τρέξει η λογική του fallback. Μόνο ένα ωμό connection-drop (ένα 1006 close) περνούσε μέχρι το fallback. Οπότε το feature θα δούλευε για τη σπάνια περίπτωση και θα αποτύγχανε σιωπηλά για τον πραγματικό βασικό του στόχο: το εταιρικό proxy.
Το πιάσαμε ενώ σκληραγωγούσαμε την έκδοση, όχι στο production. Η διόρθωση: ένα 403/407 στο σκέλος του WebSocket upgrade αντιμετωπίζεται τώρα ως recoverable ώστε το connection loop να μπορεί να εξαντληθεί προς το fallback — ενώ μια γνήσια αποτυχία authentication (που φτάνει διαφορετικά, αφού το upgrade πετύχει) εξακολουθεί να αποτυγχάνει γρήγορα, ακριβώς όπως πριν. Ένα regression test τώρα καρφώνει τη διάκριση: ένα 403 μπλοκάροντος proxy πρέπει να πέσει σε fallback· ένα πραγματικό auth 403 δεν πρέπει.
Η υπόλοιπη σκληραγώγηση ακολούθησε την ίδια παρανοϊκή γραμμή: ένα timeout σε κάθε upstream POST ώστε ένα proxy που δέχεται ένα request αλλά δεν απαντά ποτέ να μην μπορεί να μπλοκάρει το audio stream, και μια εγγύηση ότι ένα γνήσιο πρόβλημα sign-in δεν μπορεί ποτέ να καλυφθεί σιωπηλά από τον μηχανισμό του fallback.
Το δοκιμάσαμε απέναντι σε ένα πραγματικό εχθρικό proxy
Ένα feature του οποίου ολόκληρος ο σκοπός είναι η επιβίωση σε εχθρικά δίκτυα δεν μπορεί να επικυρωθεί μόνο με unit tests — τα unit tests δεν έχουν proxies. Πριν το ενεργοποιήσουμε, τρέξαμε την πραγματική εφαρμογή μέσα από ένα τοπικό reverse proxy ρυθμισμένο να κάνει ακριβώς αυτό που κάνουν τα εταιρικά proxies: προωθεί HTTPS, απορρίπτει τα WebSocket upgrades με ένα 403.
Το ίχνος στα logs είναι η απόδειξη: τέσσερις μπλοκαρισμένες WebSocket προσπάθειες, η υπογραφή εξάντλησης αναγνωρισμένη, η αυτόματη μετάβαση στο HTTPS transport, και μετά μια υγιής 96-δευτερόλεπτη session μεταγραφής πάνω από καθαρό HTTPS — 66 transcript segments, μηδέν απώλειες. Το failover δουλεύει επειδή το είδαμε να κάνει failover.
Τρία μεταβιβάσιμα μαθήματα
- Το «δουλεύει σε ασταθές Wi-Fi» και το «δουλεύει πίσω από εχθρικό proxy» είναι διαφορετικές εγγυήσεις. Το ένα χρειάζεται reconnection· το άλλο χρειάζεται διαφορετικό transport. Το να τα μπερδεύεις αφήνει έναν ολόκληρο πληθυσμό εταιρικών χρηστών σιωπηλά σπασμένο.
- Η ταξινόμηση των errors σου μπορεί να κρύψει το ίδιο σου το feature από τον εαυτό του. Ένας κανόνας που είναι σωστός στο 99% των περιπτώσεων (403 = fatal auth) ήταν ακριβώς λάθος για το 1% που αυτό το feature υπήρχε για να εξυπηρετήσει. Όταν προσθέτεις ένα fallback, έλεγξε αν η συνθήκη ενεργοποίησης μπορεί καν να φτάσει στο fallback.
- Δοκίμασε τον αντίπαλο, όχι μόνο το happy path. Το μόνο έντιμο test για το «επιβιώνει σε ένα proxy που μπλοκάρει WebSockets» είναι ένα proxy που μπλοκάρει WebSockets. Φτιάξαμε ένα.
Το GeekBye v2.0.8 κυκλοφόρησε το HTTPS fallback flag-gated και επικυρωμένο. Για τη δουλειά αξιοπιστίας δίπλα στην οποία βρίσκεται, δες γιατί ο AI notetaker σου σταματά σε κακό Wi-Fi, και για τις γειτονικές εκδόσεις αυτής της σειράς, γιατί ο AI notetaker σου σταματά την ηχογράφηση στη μέση της σύσκεψης (v2.0.9) και γιατί η μεταγραφή με AI παρακούει τους τεχνικούς όρους (v2.0.11).