Steven
Steven4 분 소요

방화벽이 WebSocket을 막을 때, 실시간 전사는 어떻게 되는가

기업 네트워크는 HTTPS는 통과시키면서 WebSocket 업그레이드는 조용히 죽여버리길 좋아합니다. 그게 실시간 전사를 소리 없이 망가뜨립니다. GeekBye v2.0.8은 순수 HTTPS 트랜스포트로 자동 폴백합니다 — 그리고 그걸 출시하는 과정에서, 기능 전체를 무용지물로 만들 뻔한 버그가 드러났습니다.

전사
네트워킹
엔지니어링
GeekBye 릴리스
방화벽이 WebSocket을 막을 때, 실시간 전사는 어떻게 되는가

기업 네트워크에서 실시간 전사가 실패하는, 특정한, 미치게 만드는 방식이 있습니다. Wi-Fi는 멀쩡합니다. HTTPS는 됩니다 — 어떤 웹사이트든 열 수 있습니다. 그런데 라이브 전사는 그냥… 시작이 안 되고, 왜인지 아무것도 알려주지 않습니다.

범인은, 평범한 HTTPS 트래픽은 허용하면서 WebSocket 업그레이드 — HTTPS 연결을, 실시간 전사가 필요로 하는 지속적인 양방향 채널로 바꾸는 핸드셰이크 — 를 막는 부류의 기업 프록시입니다. 프록시가 보기에 WebSocket은 네트워크 밖으로 빠져나가는 감시 안 되는 터널처럼 보이므로, 죽여버립니다. 당신이 보기엔, 전사가 소리 없이 망가진 것일 뿐입니다.

GeekBye v2.0.8은 바로 이것을 위한 자동 폴백을 추가했습니다 — 그리고 그걸 만드는 과정에서, 기능 전체를 아무것도 안 하게 만들 뻔한 버그가 드러났습니다.

왜 재시도가 아니라 폴백인가

불안정한 네트워크는 이미 처리합니다. 세션 도중 연결이 끊기면 GeekBye는 백오프하며 재접속하고 오디오를 버퍼링해 아무것도 잃지 않습니다 — 그건 별개의 기능으로, AI 노트테이커는 왜 나쁜 Wi-Fi에서 멈추는가에서 다룹니다.

하지만 차단된 WebSocket은 불안정한 연결이 아닙니다. WebSocket을 거부하는 프록시에 같은 WebSocket을 재시도하면, 매번, 영원히, 똑같이 실패합니다. 유일한 해결책은 다른 트랜스포트 — 프록시가 이미 허용하는 순수 HTTPS처럼 보이는 것입니다.

그래서 v2.0.8은 같은 인증된 엔드포인트 위에서 순수 HTTPS 트랜스포트로 폴백합니다:

  • 하향(당신에게 돌아오는 전사): server-sent events — 프록시가 평범한 스트리밍 다운로드로 보는, 오래 유지되는 HTTPS 응답.
  • 상향(나가는 당신의 오디오): 배치 POST 요청. 각각이 시퀀스 번호가 붙은 오디오 청크를 실어 날라, 네트워크가 순서를 뒤섞어도 서버가 순서대로 재조립할 수 있습니다.

지속적인 소켓도 없고, 터널처럼 보이는 것도 없습니다 — 그냥 HTTPS 요청과 응답입니다. 프록시가 당신에게 웹사이트 사용을 허용한다면, 이것도 허용합니다.

죽은 기능을 출시할 뻔한 버그

여기가 읽을 값어치가 있는 부분입니다. 폴백은 WebSocket 연결이 차단 트랜스포트 시그니처와 함께 시도를 소진했을 때 발동해야 했습니다 — 모든 시도가 업그레이드에서 실패하고, 인증이나 쿼터 문제는 없으며, 적어도 하나는 프록시 형태의 거부. WebSocket을 막는 프록시는 보통 업그레이드에 HTTP 403 Forbidden이나 407로 답합니다.

문제는 이렇습니다: 우리 연결 코드에는 이미 403치명적 인증 오류 — 멈추고, 사용자에게 드러내고, 재시도하지 마라를 뜻한다는 규칙이 있었습니다. 거의 모든 곳에서 그건 옳습니다. 하지만 그건, 차단 프록시에서 온 403 — 폴백을 발동시켜야 할 바로 그 신호 — 이 대신, 폴백 로직이 돌아볼 새도 없이 치명적 오류로 던져지고 있었다는 뜻이었습니다. 오직 생짜 연결 끊김(1006 종료)만이 폴백까지 떨어졌습니다. 그러니 기능은 드문 경우엔 동작하고, 실제 주 대상인 기업 프록시에는 소리 없이 실패할 뻔했습니다.

이건 프로덕션이 아니라 릴리스를 단단히 다지는 중에 잡았습니다. 수정: WebSocket 업그레이드 구간의 403/407은 이제 복구 가능으로 취급돼 연결 루프가 소진되어 폴백으로 떨어질 수 있고 — 반면 진짜 인증 실패(이건 다른 형태로, 업그레이드가 성공한 뒤에 도착합니다)는 예전과 똑같이 빠르게 실패합니다. 회귀 테스트가 이제 그 구분을 못 박습니다: 차단 프록시의 403은 폴백해야 하고, 진짜 인증 403은 하면 안 된다.

나머지 강화도 같은 의심 많은 노선을 따랐습니다: 모든 상향 POST에 타임아웃을 걸어, 요청은 받으면서 결코 답하지 않는 프록시가 오디오 스트림을 멎게 하지 못하도록 했고, 진짜 로그인 문제가 폴백 장치에 의해 소리 없이 가려지는 일이 결코 없도록 보장했습니다.

우리는 진짜 적대적 프록시에 대해 테스트했다

존재 이유 전체가 적대적 네트워크를 견디는 데 있는 기능은 유닛 테스트만으로 검증할 수 없습니다 — 유닛 테스트에는 프록시가 없습니다. 활성화하기 전에, 기업 프록시가 하는 짓을 그대로 하도록 설정한 로컬 리버스 프록시를 통해 실제 앱을 돌렸습니다: HTTPS는 전달하고, WebSocket 업그레이드는 403으로 거부하도록.

로그에 남은 흔적이 영수증입니다: 차단된 WebSocket 시도 4회, 인식된 소진 시그니처, HTTPS 트랜스포트로의 자동 전환, 그리고 순수 HTTPS 위에서 건강한 96초 전사 세션 — 전사 세그먼트 66개, 드롭 0. 페일오버가 동작하는 건, 우리가 그것이 페일오버하는 모습을 지켜봤기 때문입니다.

옮겨 쓸 수 있는 세 가지 교훈

  1. "불안정한 Wi-Fi에서 된다"와 "적대적 프록시 뒤에서 된다"는 서로 다른 보장이다. 하나는 재접속이 필요하고, 다른 하나는 다른 트랜스포트가 필요하다. 둘을 뭉뚱그리면 기업 사용자 한 무리 전체를 소리 없이 망가진 채로 방치하게 된다.
  2. 당신의 오류 분류가, 당신 자신의 기능을 그 기능 자신으로부터 숨길 수 있다. 99%의 경우에 옳은 규칙(403 = 치명적 인증)이, 이 기능이 존재하는 이유인 그 1%에는 정확히 틀렸다. 폴백을 추가할 때는, 그 트리거 조건이 애초에 폴백에 도달할 수 있는지 감사하라.
  3. 해피 패스만이 아니라 적을 테스트하라. "WebSocket 차단 프록시를 견딘다"의 유일하게 정직한 테스트는 WebSocket 차단 프록시다. 우리는 그걸 만들었다.

GeekBye v2.0.8은 HTTPS 폴백을, 플래그로 게이팅하고 검증한 상태로 출시했습니다. 그것과 나란히 놓인 신뢰성 작업은 AI 노트테이커는 왜 나쁜 Wi-Fi에서 멈추는가를, 이 시리즈의 이웃 릴리스는 AI 노트테이커는 왜 회의 중간에 녹음을 멈추는가(v2.0.9)와 AI 전사가 왜 기술 용어를 잘못 알아듣는가(v2.0.11)를 보세요.