
Khi tường lửa chặn WebSocket, phiên âm thời gian thực sẽ ra sao
Mạng doanh nghiệp thích cho HTTPS đi qua nhưng âm thầm giết chết bước nâng cấp WebSocket. Điều đó lặng lẽ làm hỏng phiên âm thời gian thực. GeekBye v2.0.8 tự động fall back sang một transport thuần HTTPS — và trong lúc làm ra nó, chúng tôi đào được một con bug có thể khiến cả tính năng trở nên vô dụng.
Có một cách thất bại rất riêng và phát điên mà phiên âm thời gian thực gặp phải trên mạng doanh nghiệp. Wi-Fi của bạn vẫn ổn. HTTPS vẫn chạy — bạn mở được bất kỳ website nào. Vậy mà phiên âm trực tiếp thì cứ… không chịu khởi động, và chẳng có gì cho bạn biết vì sao.
Thủ phạm là một loại proxy doanh nghiệp cho lưu lượng HTTPS thông thường đi qua nhưng chặn bước nâng cấp WebSocket — cái bắt tay biến một kết nối HTTPS thành kênh hai chiều, bền vững mà phiên âm thời gian thực cần. Với proxy, một WebSocket trông như một đường hầm không được giám sát chui ra khỏi mạng, nên nó giết đi. Với bạn, phiên âm chỉ lặng lẽ hỏng.
GeekBye v2.0.8 đã thêm một fallback tự động cho đúng tình huống này — và việc xây nó lôi ra một con bug có thể khiến cả tính năng chẳng làm gì cả.
Vì sao là fallback, chứ không chỉ là thử lại
Mạng chập chờn thì chúng tôi đã xử lý. Nếu kết nối của bạn rớt giữa phiên, GeekBye kết nối lại có backoff và đệm âm thanh của bạn để không mất gì — đó là một tính năng riêng, được nói trong vì sao AI notetaker của bạn dừng khi Wi-Fi tệ.
Nhưng một WebSocket bị chặn không phải là một kết nối chập chờn. Thử lại cùng một WebSocket trước một proxy từ chối WebSocket thì lần nào cũng thất bại y như nhau, mãi mãi. Cách sửa duy nhất là một transport khác — một cái trông giống thứ HTTPS trơn mà proxy vốn đã cho phép.
Vậy nên v2.0.8 fall back sang một transport thuần HTTPS trên cùng một endpoint đã xác thực:
- Chiều xuống (các đoạn phiên âm trả về cho bạn): server-sent events — một phản hồi HTTPS sống lâu mà proxy thấy như một lượt tải xuống dạng luồng bình thường.
- Chiều lên (âm thanh của bạn gửi ra): các yêu cầu POST theo lô, mỗi cái mang một khối âm thanh kèm một số thứ tự để máy chủ ráp lại đúng thứ tự ngay cả khi mạng đảo lộn chúng.
Không có socket bền vững, không có gì trông như đường hầm — chỉ là các yêu cầu và phản hồi HTTPS. Nếu một proxy cho phép bạn dùng website, thì nó cho phép cái này.
Con bug suýt phát hành một tính năng đã chết
Đây mới là phần đáng đọc. Fallback lẽ ra phải kích hoạt khi kết nối WebSocket dùng cạn số lần thử với một chữ ký transport-bị-chặn — mọi lần thử đều thất bại ở bước nâng cấp, không có vấn đề xác thực hay hạn ngạch, ít nhất một lần bị từ chối theo kiểu proxy. Một proxy chặn WebSocket thường trả lời bước nâng cấp bằng một HTTP 403 Forbidden hoặc 407.
Vấn đề: mã kết nối của chúng tôi vốn đã có một quy tắc rằng một 403 nghĩa là lỗi xác thực nghiêm trọng — dừng lại, đưa nó ra cho người dùng, đừng thử lại. Điều này đúng ở gần như mọi nơi. Nhưng nó có nghĩa là cái 403 từ một proxy chặn — đúng cái tín hiệu lẽ ra phải kích hoạt fallback — lại bị ném ra như một lỗi nghiêm trọng trước khi logic fallback kịp chạy. Chỉ một cú rớt kết nối trần trụi (một lần đóng 1006) mới lọt xuống tới fallback. Thế nên tính năng lẽ ra sẽ chạy được cho trường hợp hiếm và âm thầm thất bại với mục tiêu chính thật sự của nó: cái proxy doanh nghiệp.
Chúng tôi bắt được nó khi đang gia cố bản phát hành, chứ không phải trong production. Cách sửa: một 403/407 ở chặng nâng cấp WebSocket giờ được coi là có thể phục hồi để vòng lặp kết nối dùng cạn rồi rơi vào fallback — trong khi một thất bại xác thực thật sự (vốn đến theo cách khác, sau khi nâng cấp đã thành công) vẫn thất bại nhanh, y hệt như trước. Một bài test hồi quy giờ ghim chặt sự phân biệt này: một 403 của proxy chặn phải fall back; một 403 xác thực thật thì không được.
Phần gia cố còn lại đi theo cùng một lối đa nghi: một timeout trên mọi POST chiều lên để một proxy nhận yêu cầu nhưng không bao giờ trả lời không thể làm nghẽn luồng âm thanh, và một bảo đảm rằng một vấn đề đăng nhập thật sự không bao giờ bị bộ máy fallback âm thầm che mất.
Chúng tôi đã kiểm thử với một proxy thù địch thật
Một tính năng mà toàn bộ mục đích tồn tại là sống sót qua các mạng thù địch thì không thể chỉ kiểm chứng bằng unit test — unit test không có proxy. Trước khi bật, chúng tôi cho chính ứng dụng thật chạy qua một reverse proxy cục bộ được cấu hình để làm đúng cái các proxy doanh nghiệp làm: chuyển tiếp HTTPS, từ chối nâng cấp WebSocket bằng một 403.
Dấu vết trong log chính là biên nhận: bốn lần thử WebSocket bị chặn, chữ ký dùng-cạn được nhận ra, cú chuyển tự động sang transport HTTPS, rồi một phiên phiên âm 96 giây khỏe mạnh trên thuần HTTPS — 66 đoạn phiên âm, không rớt cái nào. Chuyển đổi dự phòng chạy được là vì chúng tôi đã tận mắt xem nó chuyển đổi.
Ba bài học mang đi được
- "Nó chạy được trên Wi-Fi chập chờn" và "nó chạy được sau một proxy thù địch" là hai đảm bảo khác nhau. Một cái cần kết nối lại; cái kia cần một transport khác. Gộp chúng lại là bỏ mặc cả một nhóm người dùng doanh nghiệp hỏng trong lặng lẽ.
- Cách phân loại lỗi của bạn có thể giấu chính tính năng của bạn khỏi chính nó. Một quy tắc đúng 99% thời gian (403 = xác thực nghiêm trọng) lại sai đúng cái 1% mà tính năng này tồn tại để phục vụ. Khi bạn thêm một fallback, hãy soát xem điều kiện kích hoạt có thể chạm tới được fallback hay không.
- Hãy kiểm thử kẻ thù, không chỉ đường đi suôn sẻ. Bài kiểm thử trung thực duy nhất cho "sống sót qua một proxy chặn WebSocket" là một proxy chặn WebSocket. Chúng tôi đã dựng ra một cái.
GeekBye v2.0.8 phát hành fallback HTTPS này, có cờ kiểm soát và đã được kiểm chứng. Về công việc độ tin cậy nằm cạnh nó, xem vì sao AI notetaker của bạn dừng khi Wi-Fi tệ, và về các bản phát hành lân cận trong series này, xem vì sao AI notetaker của bạn dừng ghi âm giữa cuộc họp (v2.0.9) và vì sao AI phiên âm nghe nhầm thuật ngữ kỹ thuật (v2.0.11).