HTTP CONNECT 응답 후 버퍼에 남은 데이터 보존하기
응답 헤더 경계를 찾고 그 뒤에 도착한 바이트를 보존하여 멈춰 버리는 HTTP CONNECT 핸드셰이크를 피하세요.
작은 프록시 래퍼가 HTTPS에서는 동작하다가, 서버가 먼저 말하는 프로토콜에 재사용될 때 멈출 수 있습니다. 의심스러운 줄은 대개 수신 버퍼가 \r\n\r\n로 끝나기를 기다리는 줄입니다.
TCP는 클라이언트에 스트림을 제공합니다. 한 번의 수신 호출이 HTTP 상태 줄의 절반, 완전한 CONNECT 응답, 또는 그 응답에 곧바로 이어지는 터널 데이터를 반환할 수 있습니다. 읽기가 헤더 끝에서 멈춘다는 보장은 없습니다.
버퍼 안에서 경계 찾기
HTTP/1.1 CONNECT 교환에서는 헤더 종결자가 버퍼 어디에서든 나타날 때까지 바이트를 누적합니다. 처음 나타난 지점에서 나눕니다. 상태 줄과 헤더는 첫 부분에서 파싱하고, 경계 이후의 모든 것은 따로 보관합니다. 기다리는 동안 헤더 크기 제한과 타임아웃을 적용하고, 빈 소켓 읽기는 루프를 계속하라는 초대가 아니라 닫힌 연결로 취급합니다.
최종적인 성공 2xx 응답은 그 헤더 직후에 연결을 터널 모드로 전환합니다. 그 뒤따르는 바이트는 이미 터널 바이트입니다. 이를 버리지 말고, CONNECT 응답 본문을 기다리지 말고, Content-Length 필드를 새 프로토콜의 일부를 소비해도 된다는 허가로 해석하지 마세요. 정보성 응답이 있다면 그 최종 응답 전에 처리해야 합니다.
래퍼는 남은 데이터를 넘겨야 한다
기반 소켓만 반환하면 핸드셰이크 파서가 이미 끌어낸 바이트를 잃게 됩니다. 다음 읽기 측에는 저장된 꼬리를 먼저 내보내고 그다음 소켓 읽기를 재개하는 버퍼링 스트림이 필요합니다. 서버가 먼저 말하는 프로토콜에서 그 첫 인사를 잃으면 양쪽이 무한정 기다릴 수 있습니다.
반대로 실패한 CONNECT 응답의 바이트는 터널 인사가 아닙니다. 407은 여전히 프록시 교환에 속하며 인증 처리가 필요하고, 다른 비성공 상태에는 적절한 오류 경로가 필요합니다.
이는 HTTP 프록시를 통해 FTP나 SSH 클라이언트를 적용할 때 특히 중요합니다. 프록시는 요청된 대상 포트도 허용해야 하며, FTP는 추가 데이터 연결이 필요할 수 있습니다. 올바른 헤더 파싱은 스트림 인계를 해결하지만, CONNECT 권한을 부여하거나 FTP의 두 번째 채널을 제공할 수는 없습니다.