구현 | 요청 읽기
제어구조
파싱 및 유효성 검사 과정에 대해서는 따로 설명하지 않습니다. 한 가지만 꼽아 말씀을 드린다면, 요청 메시지의 각 줄 끝을 "\r\n"이 아니라 "\n"으로 다룬다면 파싱할 때 문제가 생길 수 있습니다. 그 외에는 리퀘스트를 읽는 과정에서 발생할 수 있는 에러의 수가 굉장히 많기 때문에, 다양한 상태 코드에 대해 학습하면서 어떤 에러가 발생할 수 있는지 고민해보시기를 바랍니다.
리퀘스트를 읽는 과정에서 문제가 있다면 에러 상태 코드(int)를 throw하는데, 이 경우 해당 상태 코드로 바로 응답을 생성합니다. 문제가 없는 경우에만 URL과 method를 참고하여 요청을 처리하고 거기에 맞는 응답을 생성하는 것이죠.
리퀘스트를 읽는 과정에서 recv든 read든 read operation을 하는 함수가 0 또는 -1을 리턴한다면 에러로 간주하고 Client를 제거해야 합니다. 에러 응답도 보내지 않습니다.
Custom 상태 코드
에러 발생시 상태코드를 throw할 때, 아래와 같이 상태코드 뒤에 2자리 수를 더 붙여 해당 에러가 발생한 원인이나 위치를 로그로 남기면 유용합니다. 실제로 응답을 보낼 때는 앞의 3자리만 잘라서 보내면 되고, status_map에 3자리 상태코드에 대한 사유구절도 모두 저장되어 있기 때문에 문제가 없습니다.
Enum
Enum을 활용하면 효율적인 프로그래밍이 가능합니다.
Chunked Transfer-Encoding
바디가 있는 리퀘스트의 경우 클라이언트가 바디를 전송해오는 방식에는 2가지가 있습니다. 하나는 Content-Length가 설정된 경우로, 그 값만큼만 body를 읽으면 됩니다. 하지만 대용량 데이터의 경우 한 번에 그렇게 많은 양을 읽을 수 없으므로 나누어서 보내게 되는데, 이 때에는 Transfer-Encdoing 헤더의 value가 chuked로 오게 됩니다.
위와 같이 얼마만큼의 길이를 보낼 것인지 토큰을 한 번 보내고, 그 숫자만큼의 메시지를 보내고, 또 토큰을 보내고, 토큰의 숫자만큼 메시지를 보내는 과정을 반복합니다. 0 토큰이 종료 신호로 읽히며, raw data로 보면 "0\r\n\r\n"으로 종료됩니다.
토큰은 16진수로 옵니다. 변환을 위한 stoi 함수는 c++11 문법인데, 98을 지키라는 내용이 서브젝트에 없긴 하지만 저희는 98로 작성하기 위해 stoi, to_string과 같은 함수들도 모두 만들어서 사용했습니다.
"0\r\n\r\n"이 body에 존재한다고 무조건 끝인 것은 아닙니다. 앞 토큰의 숫자 범위에 이 데이터가 포함되는 경우, 이 데이터는 종료 토큰이 아니라 바디의 일부로 읽히기 때문입니다. 이미지 데이터의 경우 이런 패턴이 많이 나오기 때문에 조심해서 읽어야 합니다.
Last updated