회고

오늘은 HTTP 프로토콜의 Request, Response 규격에 맞춰 응용 계층을 직접 구현해보는 미션이었다. 요구사항은 다음과 같았다.

URI/URL 규격 처리 모듈

  • URL 구성 규칙은 RFC 1738 따른다.

HTTP Request

  • Node API 중 DNS 모듈 이용하여 도메인 주소를 IP Address 로 변경한다.
  • Node API 중 Net 모듈 이용하여 TCP 소켓을 연결한다.
  • Request 객체를 생성하고 필요한 Header를 구성한다.
  • Request Message를 TCP 세션에 전송(Send)한다.

HTTP Response

  • Response 크기가 커서 분리되는 경우 Payload가 모두 받은 다음에 화면에 노출한다.
  • Response Content-Length 헤더와 Body 크기가 동일해야 한다.
  • Response 객체는 Header, Body, Status Code로 구성한다.

URI/URL 규격 처리 모듈은 정규 표현식을 이용해서 URL 적합성과 Scheme, User, Password, Host, Port, Path, QueryString을 구분하려고 했다. 입력된 URL이 해당 컴포넌트를 모두 가지고 있다면 쉬울 수 있었지만 User, Password, Port, Path, QueryString은 존재하지 않을 수도 있어서 정규 표현식으로 처리하는데 어려움을 느꼈다. 결과적으로는 RFC 1738 정의된 예외 처리는 하지 못하였지만, 조건문으로 각 컴포넌트를 추출하였다.

HTTP Request 객체에서 HTTP Message를 구성하는 과정에서 CRLF을 제대로 알지 못하여 고생을 하였다. HTTP Header는 모두 space로 구분되고, 각 라인은 CRLF로 구분된다. 그리고 Header와 Body 부분은 두 개의 CRLF로 구분한다. Header와 Body를 한 개의 CRLF로 적용하였더니 응답이 오지 않는 문제가 발생하였다.

HTTP Response 객체는 데이터 전달 크기인 MTU 크기로 인하여 Transfer-Chunk 헤더가 적용되어 각 Payload가 일정한 크기로 따로 전달되었다. Response 객체에서는 송신측에서 보낸 Content-Length와 수신측에서 받은 Message 크기가 달랐다. Message는 CRLF 형식 때문에 문자열이 아닌 Buffer 타입의 Stream으로 전달되고 있었다. 전달된 Message Buffer를 Array에 저장하고 전송이 완료되면, Octects 기준으로 해당 Buffer 길이를 모두 더하여 Content-Length 헤더와 비교하였는데 길이가 서로 다른 문제가 있었다. 결과적으로는 송신측과 수신측이 Encoding/Decoding 방식을 맞추지 않아 생기는 문제였다. 즉, HTTP Request 헤더에 수신측이 해석할 수 있는 Decoding 방식을 명시하는 Accept-Charset을 명시하지 않아 서로 다르게 Encoding/Decoding 하고 있었던 것이다. 결과적으로 HTTP Request 헤데에 "Accept-Charset: utf-8" 헤더를 추가하여 송신측에서 UTF-8로 Encoding 하도록 하여 문제를 해결할 수 있었다.