저희는 Memory Leak에 대해 큰 걱정이 없었습니다. malloc을 안 쓰면 되니까요. 컨테이너를 최대한 활용하면, 인스턴스의 소멸에 대해서는 컨테이너가 책임지기 때문에 이슈가 없습니다. 저희는 malloc을 프로그램 전체에 걸쳐 딱 한 번 사용하는데, CGI 실행 후 환경변수(char **)를 해제하는 것입니다. minishell을 진행하면서 사용했다 메모리 해제 함수를 끌어다 그대로 썼습니다.
2. Response Connection Type
enumConnectionType { CLOSE, KEEP_ALIVE, };
응답에서 커넥션 타입의 경우 Close와 Keep-Alive를 enum으로 사용하면 편리합니다. 이런 작업들을 문자열로 관리하게 되면 오타 때문에 에러가 나기 쉽고, 개발을 하다가 후보 값들을 확인하려면 enum 의 정의를 보는 것보다 접근성 및 가독성이 무척 떨어집니다.
3. Transfer Type
enumTransferType { GENERAL, CHUNKED };
전송 타입의 경우 Request와 Response 객체 모두 GENERAL(Content-Length)과 CHUNKED로 구분하여 관리하면 편리합니다.
Request, Response, Connection 모두 한 번에 처리할 수 없는 작업이 많습니다. 특히 저희는 아래와 같이 data가 들어오는 대로 처리를 진행했기 때문에 요청과 응답 모두 phase가 필요했습니다. 만약 요청 메시지를 다 읽은 후에 리퀘스트를 생성하는 방식으로 구현하면 요청이나 응답의 처리 페이즈를 enum으로 구현하지 않아도 괜찮을 거에요. 하지만 그 경우 처리 시간이 길어진다는 이슈가 있습니다.
커넥션은 리퀘스트 대기, 리퀘스트 읽기, CGI 프로그램과 입출력 시작 전, 입출력 시작 중, 응답 전송 전, 응답 전송 중이라는 선형적인 작업 흐름을 가집니다. 현재 커넥션이 어떤 상태에 있는가를 명확하게 status로 관리하면, 커넥션을 처리하는 흐름을 제어하기가 매우 편해집니다.
Autoindex를 처리하기 위해서는 HTML 페이지를 만들 필요가 있습니다. 저희는 HTML Writer 클래스를 생성하여 필요한 html 페이지를 만들었습니다. 이외에도 이런 클래스를 만들어서 처리하면 에러 페이지 생성 등 필요한 HTML 페이지를 편리하게 만들 수 있습니다.
select 함수의 인자로 감시할 fd의 범위를 넘겨주려면 max fd 값이 필요한데, fd를 닫거나 새로운 fd를 만들 때마다 재설정하면 코드는 빠르겠지만 관리해야 할 포인트들이 많아서 유지보수가 어려워집니다. 저는 read_set이나 write_set 중 둘 중 하나에는 client_fd와 server_fd가 늘 들어있기 때문에 isset 함수를 활용하여 설정된 최대 fd를 확인하는 방식으로 select 함수 실행 전 max_fd를 재설정했습니다.
read_set과 write_set 이외에도 connect_set이나 used_set을 만들어서 현재 사용중인 fd를 따로 관리하는 방식이 더 깔끔할 수 있습니다.
여러 번 강조하지만, read/write operation이 0이나 1을 리턴할 경우 connection(client)은 제거되어야 합니다. 그러나 이것을 변수나 리턴 값으로 조회하게 되면 로직이 매우 복잡해집니다. 입출력 에러가 발생했을 때에는 IOError를 정의하여 throw하고, 밖에서 catch로 받아서 클라이언트를 제거하는 것이 심플하고 효율적입니다.
특정 fdSet에 설정되어 있는 fd들을 한 줄의 문자열로 확인하는 함수입니다. 현재 read_set이나 write_set의 상태가 어떤지를 디버깅할 때 매우 편리합니다.
std::stringgetSetFdString(int max_fd, fd_set* fset) { std::string ret;bool first =true;for (int i =0; i <= max_fd; ++i) {if (ft::fdIsset(i, fset)) {if (!first) {ret.append(","); } first =false;ret.append(ft::to_string(i)); } }return (ret); }
16. overtime 체크
hanging connection이 없어야 합니다. 일반적으로 클라이언트 측에서 커넥션을 닫으면 서버의 select에서 감지가 되지만, 계속해서 열어놓는 경우에는 문제가 될 수 있습니다. 오래된 커넥션을 주기적으로 닫아주는 로직을 만들어놓으면 커넥션들을 보다 깔끔하게 관리할 수 있습니다.
if (connection.isOverTime())closeClientConnection(fd);