큐레이션 요약
hyper HTTP 라이브러리에서 버그를 발견한 방법
Cloudflare의 Images binding을 로컬 Unix 소켓 기반 구조로 개편한 뒤, 대용량 이미지 응답이 간헐적으로 잘리는 버그가 발생했다. 응답은 HTTP 200과 정상적인 Content-Length를 반환했지만 실제 본문은 수백 KB만 전달되어 이미지가 부분적으로 렌더링되거나 디코딩에 실패했다. 원인은 특정 조건에서 발생하는 hyper 라이브러리의 레이스 컨디션이었으며, 최종적으로 네 줄의 코드 수정으로 해결됐다.
Images binding과 hyper의 데이터 흐름
- Workers는 바인딩을 통해 Images 서비스에 이미지 데이터를 직접 전달하고, 변환 결과를 스트림으로 받을 수 있다.
- 이미지 변환 과정은 다음과 같이 진행된다.
- Workers 런타임이 소켓을 통해 Images 서비스에 요청을 보낸다.
- Images 서비스가 이미지를 합성·리사이즈·트랜스코딩한다.
- 변환된 전체 이미지를 메모리 블록으로 hyper에 전달한다.
- hyper가 데이터를 내부 버퍼에 저장한 뒤 소켓의 송신 버퍼로 기록한다.
- 소켓의 양 끝에는 커널이 관리하는 버퍼가 있다.
- 수신자가 충분히 빠르면 hyper가 한 번에 모든 데이터를 보내고 소켓을 종료할 수 있다.
- 수신자가 조금이라도 느리면 송신 버퍼가 가득 차고, hyper는 공간이 생길 때까지 추가 쓰기를 기다려야 한다.
네트워크 중계에서 로컬 Unix 소켓으로 전환
- 초기 Images binding은 Workers 런타임과 Images 사이에서 FL이라는 내부 중계 서비스를 거쳤다.
- FL은 DNS 조회와 라우팅 등 전체 네트워크 처리 파이프라인을 수행했기 때문에 오버헤드가 있었다.
- 2025년 12월, Cloudflare는 FL을 같은 머신에서 실행되는 내부 Worker binding으로 교체했다.
- 새 구조는 네트워크 소켓 대신 Unix 소켓으로 서비스를 직접 연결했다.
- 이 변경으로 다음 효과를 기대했다.
- 네트워크 스택과 FL 처리 과정 제거
- Images 요청 경로 단축
- Images 팀이 독립적으로 binding을 배포하고 변경 가능
- 그러나 출시 며칠 뒤부터 대용량 이미지 응답 실패 제보가 접수됐다.
HTTP 200이지만 잘린 응답
- 고객 사례는 두 단계의 이미지 처리 파이프라인을 중첩한 비표준 구성이었다.
- 내부 파이프라인: R2의 JPEG와 PNG를 합성해 JPEG 생성
- 외부 파이프라인: 결과 이미지를 다시 압축·변환·리사이즈
- 실제 문제는 내부 transformation binding의 반환 경로에서 발생했다.
- 외부 파이프라인은 내부 응답에서 다음과 같은 모순을 받았다.
- 상태 코드는
200 OK Content-Length는 수 MB로 설정- 실제 수신 본문은 일부 데이터에 불과함
- 상태 코드는
- 한 사례에서는 예상 크기 3.3MB 중 약 200KB만 전달됐다.
- 상위 계층에서는 다음 오류가 발생했지만, 실제 원인이 어느 서비스에 있는지는 즉시 알기 어려웠다.
error reading a body from connection: end of file before message length reached
- 브라우저에서는 이미지 형식에 따라 일부만 표시되거나, 하단이 회색으로 남거나, 아예 깨진 이미지로 표시됐다.
재현과 원인 범위 좁히기
- 개발팀은 고객의 중첩 파이프라인을 재현하는 Worker를 만들었다.
- 이후 외부 파이프라인과 여러 계층을 하나씩 제거해 binding 단독으로도 문제를 재현할 수 있음을 확인했다.
- 배치 요청을 보내는 간단한 스크립트로 재현을 자동화했다.
- 초기 실행에서는 25건 중 19건이 실패했다.
- 매번 도착한 데이터가 약 200KB였는데, 이는 운영 환경의 소켓 버퍼 크기와 매우 유사했다.
- 이를 통해 문제는 고객 설정이 아니라 소켓 버퍼가 가득 찬 뒤 hyper가 응답 전송과 연결 종료를 처리하는 방식과 관련 있음을 추정할 수 있었다.
- 최종 원인은 특정 타이밍에서 발생하는 hyper 내부의 레이스 컨디션으로 밝혀졌고, 수정에는 네 줄의 코드만 필요했다.
실용적인 결론
소켓 기반 스트리밍에서는 200 OK나 올바른 Content-Length만으로 전송 성공을 판단할 수 없다. 특히 송신 버퍼가 가득 차는 상황, 느린 수신자, 데이터 기록과 연결 종료가 동시에 일어나는 경로를 반드시 테스트해야 하며, 대용량·중첩 파이프라인을 포함한 재현 테스트가 간헐적인 네트워크 버그를 찾는 데 결정적이다.
관련 글
큐레이션 요약을 이어서 읽어보세요.