
เมื่อไฟร์วอลล์บล็อก WebSocket แล้วการถอดเสียงเรียลไทม์จะเป็นยังไง
เครือข่ายองค์กรชอบปล่อย HTTPS ให้ผ่าน แต่แอบฆ่า WebSocket upgrade ทิ้งเงียบๆ ซึ่งทำให้การถอดเสียงเรียลไทม์พังแบบไร้เสียง GeekBye v2.0.8 จะ fall back ไปยัง transport แบบ HTTPS ล้วนโดยอัตโนมัติ — และระหว่างที่ทำมันออกมา เราก็เจอบั๊กที่จะทำให้ทั้งฟีเจอร์นี้ไร้ประโยชน์ไปเลย
มีวิธีล้มเหลวแบบเจาะจงและน่าหัวเสียอยู่แบบหนึ่ง ที่การถอดเสียงเรียลไทม์จะเจอบนเครือข่ายองค์กร Wi-Fi ของคุณก็ปกติดี HTTPS ก็ใช้ได้ — เปิดเว็บไหนก็ได้ แต่การถอดเสียงสดกลับ…ไม่ยอมเริ่ม และไม่มีอะไรบอกคุณเลยว่าทำไม
ตัวการคือ proxy องค์กรประเภทหนึ่งที่ปล่อยทราฟฟิก HTTPS ธรรมดาให้ผ่าน แต่บล็อก WebSocket upgrade — คือ handshake ที่เปลี่ยนการเชื่อมต่อ HTTPS ให้กลายเป็นช่องทางสองทางแบบถาวรที่การถอดเสียงเรียลไทม์ต้องใช้ ในสายตา proxy WebSocket ดูเหมือนอุโมงค์ที่ไม่ถูกตรวจสอบซึ่งพุ่งออกนอกเครือข่าย มันเลยฆ่าทิ้ง ส่วนในสายตาคุณ การถอดเสียงก็แค่พังไปเงียบๆ
GeekBye v2.0.8 เพิ่ม fallback อัตโนมัติสำหรับกรณีนี้โดยเฉพาะ — และการสร้างมันขึ้นมาก็ทำให้เจอบั๊กที่จะทำให้ทั้งฟีเจอร์ไม่ทำอะไรเลย
ทำไมต้อง fallback ไม่ใช่แค่ retry
เน็ตกระตุกๆ เราจัดการอยู่แล้ว ถ้าการเชื่อมต่อหลุดกลางเซสชัน GeekBye จะ reconnect แบบมี backoff และ buffer เสียงของคุณไว้ให้ไม่มีอะไรหาย — นั่นเป็นอีกฟีเจอร์หนึ่ง อ่านได้ที่ทำไม AI notetaker ของคุณถึงหยุดเมื่อ Wi-Fi ห่วย
แต่ WebSocket ที่ถูกบล็อกไม่ใช่การเชื่อมต่อที่กระตุก การ retry WebSocket ตัวเดิมกับ proxy ที่ปฏิเสธ WebSocket ก็ล้มเหลวแบบเดิมทุกครั้ง ตลอดไป ทางแก้เดียวคือ transport คนละแบบ — แบบที่ดูเหมือน HTTPS ธรรมดาที่ proxy อนุญาตอยู่แล้ว
ดังนั้น v2.0.8 จึง fall back ไปยัง transport แบบ HTTPS ล้วน บน endpoint เดิมที่ยืนยันตัวตนแล้ว:
- ขาลง (ผลถอดเสียงที่ส่งกลับมาหาคุณ): server-sent events — HTTPS response อายุยาวที่ proxy มองว่าเป็นการดาวน์โหลดแบบ stream ธรรมดา
- ขาขึ้น (เสียงของคุณที่ส่งออกไป): POST request แบบ batch แต่ละอันแบกเสียงชิ้นหนึ่งพร้อมหมายเลขลำดับ เพื่อให้เซิร์ฟเวอร์ประกอบกลับเรียงตามลำดับได้ แม้เครือข่ายจะสลับลำดับก็ตาม
ไม่มี socket ถาวร ไม่มีอะไรที่ดูเหมือนอุโมงค์ — มีแค่ request และ response แบบ HTTPS ถ้า proxy ยอมให้คุณใช้เว็บไซต์ได้ มันก็ยอมให้อันนี้ได้
บั๊กที่เกือบปล่อยฟีเจอร์ที่ตายแล้วออกไป
นี่คือส่วนที่คุ้มค่าจะอ่าน fallback ควรจะทำงานเมื่อการเชื่อมต่อ WebSocket ใช้ความพยายามจนหมดพร้อม signature ของการถูกบล็อก transport — คือทุกครั้งล้มเหลวที่ขั้น upgrade ไม่มีปัญหาเรื่อง auth หรือ quota และมีการปฏิเสธในรูปแบบของ proxy อย่างน้อยหนึ่งครั้ง proxy ที่บล็อก WebSocket มักตอบ upgrade กลับมาด้วย HTTP 403 Forbidden หรือ 407
ปัญหาคือ: โค้ดการเชื่อมต่อของเรามีกฎอยู่แล้วว่า 403 หมายถึง auth error ร้ายแรง — หยุด แล้วโยนให้ผู้ใช้เห็น อย่า retry ซึ่งถูกต้องแทบทุกที่ แต่มันแปลว่า 403 จาก proxy ตัวบล็อก — สัญญาณตัวเป๊ะๆ ที่ควรจะจุดชนวน fallback — กลับถูกโยนเป็น error ร้ายแรง ก่อนที่ logic ของ fallback จะได้มีโอกาสทำงานเสียอีก มีแต่การหลุดการเชื่อมต่อแบบดิบๆ (การปิดด้วย 1006) เท่านั้นที่รอดลงไปถึง fallback ฟีเจอร์นี้เลยจะทำงานได้กับกรณีที่เกิดยาก แต่ล้มเหลวเงียบๆ กับเป้าหมายหลักตัวจริงของมัน นั่นคือ proxy องค์กร
เราดักเจอตอนกำลังทำ release ให้แข็งแรง ไม่ใช่ตอนขึ้น production แล้ว วิธีแก้: 403/407 บนขา upgrade ของ WebSocket ตอนนี้ถูกถือว่ากู้คืนได้ เพื่อให้ loop การเชื่อมต่อใช้จนหมดแล้วตกลง fallback ได้ — ในขณะที่ความล้มเหลวของ auth ตัวจริง (ซึ่งมาถึงคนละแบบ หลังจาก upgrade สำเร็จแล้ว) ก็ยังล้มเหลวเร็วเหมือนเดิมเป๊ะ ตอนนี้มี regression test ตรึงความต่างนี้ไว้: 403 ของ proxy ตัวบล็อกต้อง fall back; 403 ของ auth ตัวจริงต้องไม่ fall back
การทำให้แข็งแรงส่วนที่เหลือก็เดินตามแนวขี้ระแวงเดียวกัน: ใส่ timeout ให้ทุก POST ขาขึ้น เพื่อไม่ให้ proxy ที่รับ request แต่ไม่ตอบกลับมาเลยมาทำให้ audio stream ค้าง และรับประกันว่าปัญหา sign-in ตัวจริงจะไม่มีวันถูกกลไก fallback บดบังไปแบบเงียบๆ
เราทดสอบกับ proxy ตัวร้ายของจริง
ฟีเจอร์ที่มีเหตุผลในการมีอยู่ทั้งหมดคือการรอดจากเครือข่ายที่มีเจตนาร้าย จะตรวจสอบด้วย unit test อย่างเดียวไม่ได้ — unit test ไม่มี proxy ก่อนเปิดใช้ เรารันแอปตัวจริงผ่าน reverse proxy ในเครื่องที่ตั้งค่าให้ทำสิ่งที่ proxy องค์กรทำเป๊ะๆ: ส่งต่อ HTTPS ปฏิเสธ WebSocket upgrade ด้วย 403
ร่องรอยใน log คือใบเสร็จ: ความพยายามต่อ WebSocket ที่ถูกบล็อกสี่ครั้ง, signature ของการใช้จนหมดที่ถูกจดจำได้, การสลับไป transport แบบ HTTPS โดยอัตโนมัติ แล้วก็เซสชันถอดเสียง 96 วินาทีที่แข็งแรงบน HTTPS ล้วน — 66 segment ถอดเสียง ไม่มีหลุดเลย การ failover ทำงานได้เพราะเราเฝ้าดูมัน fail over
บทเรียนที่เอาไปใช้ต่อได้สามข้อ
- "มันใช้ได้บน Wi-Fi ที่กระตุก" กับ "มันใช้ได้หลัง proxy ที่มีเจตนาร้าย" คือคำรับประกันคนละอย่าง อันหนึ่งต้องการการ reconnect อีกอันต้องการ transport คนละแบบ เอามารวมกันเมื่อไหร่ ก็ทิ้งผู้ใช้องค์กรทั้งกลุ่มไว้ให้พังแบบเงียบๆ
- การจัด error ของคุณ อาจซ่อนฟีเจอร์ของคุณเองไม่ให้มันเห็นตัวเอง กฎที่ถูก 99% ของเวลา (403 = auth ร้ายแรง) กลับผิดพอดีสำหรับ 1% ที่ฟีเจอร์นี้มีอยู่เพื่อรับใช้ เวลาคุณเพิ่ม fallback ให้ตรวจดูว่าเงื่อนไข trigger มันไปถึง fallback ได้จริงไหม
- ทดสอบตัวศัตรู ไม่ใช่แค่เส้นทางที่ราบรื่น การทดสอบ "รอดจาก proxy ที่บล็อก WebSocket" อย่างซื่อสัตย์เพียงอย่างเดียวคือ proxy ที่บล็อก WebSocket เราสร้างมันขึ้นมาเอง
GeekBye v2.0.8 ปล่อย HTTPS fallback แบบมี flag กำกับและผ่านการตรวจสอบแล้ว สำหรับงานด้านความเสถียรที่อยู่ข้างกัน ดูทำไม AI notetaker ของคุณถึงหยุดเมื่อ Wi-Fi ห่วย และสำหรับรีลีสข้างเคียงในซีรีส์นี้ ดูทำไม AI notetaker ของคุณถึงหยุดบันทึกเสียงกลางประชุม (v2.0.9) และทำไม AI ถอดเสียงถึงฟังศัพท์เทคนิคผิด (v2.0.11)