42서울/webserv

Webserv 과제 분석 및 개요

뜨거운 개발자 2024. 4. 25. 10:15

인사말

안녕하세요. 너무 늦게 찾아왔네요. 가이드를 만들고자고 다짐한 지 한참인데, 다른 할 일들을 하다 보니 너무 늦어졌네요. 꽤 긴 시리즈가 될 것 같은데요, 이제 시작해보고자 합니다.
이번 게시물은 Webserv 과제를 한글로 분석해보고, 과제에서 공부해야 하는 개념들을 목록으로 나누겠습니다.

과제 요약

이 과제를 한 문장으로 요약하면 아래 문장으로 나타낼 수 있을 것 같습니다.

HTTP 1.1 기준을 따르는 Nginx와 유사하게 동작하는 웹서버를 만들어라.

오 그러면 nginx는 깃허브에 소스코드를 공개하니까 그걸 따라서 만들면 되겠다..! 라고 생각하고 처음에는 nginx 깃허브를 봤습니다.
하지만 저희는 nginx의 동작을 참고하는 것이지, nginx를 똑같이 만들려먼 3명의 팀원으로는 1년이 걸려도 모자라겠죠. 
자, 그러면 어떤 걸 만들어야 하는지 주어진 과제를 분석해보도록 하겠습니다.

nginx 공식 깃허브 (파싱 파일만 어마무시합니다.)

과제 명세서

1. 프로그램 제한사항

  • 프로그램은 절대로 충돌하거나 예기치 못하게 종료 되어서는 안 된다.
  • cpp98만 사용해야 한다.

2. 사용가능 함수

  • cpp98 모든 함수와 아래 C함수를 사용할 수 있습니다.
  • 프로세스 관련 함수 : execve, dup, dup2, pipe, errno, fork, strerror, gai_strerror
  • 소켓 연결 관련 함수 : htons, htonl, ntohs, ntohl, select, poll, epoll (epoll_create, epoll_ctl, epoll_wait), kqueue (kqueue, kevent), socket, accept, listen, send, recv, bind, connect, getaddrinfo, freeaddrinfo, setsockopt, getsockname, getprotobyname,
  • 시스템 콜: fcntl, close, read, write, waitpid, kill, signal, access, stat, opendir, readdir , closedir
  • 모든 매크로를 사용할 수 있으며 FD_SET, FD_CLR, FD_ISSET, FD_ZERO를 한번 공부해 보세요.

3. 프로그램 실행 및 제출 파일 형식

  • 프로그램 이름 : webserv
  • 제출파일 : config file, Makefile,. hpp,. cpp,. tpp,. ipp 등
  • 설명 : C++ 98의 HTTP 서버
  • 실행형식 :./webserv [configuration file]
  • 인자 : config 파일

과제 상세 요구사항

과제 TIP

  • 이 프로젝트를 시작하기 전에 RFC를 읽고, telnet과 NGINX로 몇 가지 테스트를 해보면서 어떻게 동작하는지 테스트해볼 수 있습니다.
  • 모든 RFC를 구현할 필요는 없지만 읽으면 도움이 됩니다.
  • 과제에서 말하는 poll 함수는 select, kqueue, epoll 함수와 같습니다.

과제 요구사항

  1. 프로그램은 config 파일을 인자로 받거나 기본경로를 사용해야 합니다.
    • 인자를 넣으면 그 구성파일을 사용하고 인자를 넣지 않고 실행하면, default로 가져오는 경로를 지정해야 합니다.
  2. 서버는 절대로 client를 block 해서는 안되고, 필요한 경우 클라이언트를 바운스 할 수 있어야 합니다.
    • 바운스 : 서버가 클라이언트 요청에 알맞은 핸들링을 의미합니다.
  3. non-blocking을 사용해야 하고 poll 한 개 만을 사용해야만 합니다.
    • poll은 읽기와 쓰기를 동시에 확인해야만 합니다.
  4. HTTP response status codes는 반드시 정확해야 합니다.
  5. 리소스가 없는 경우 default error pages를 제공해야 합니다.
  6. Fork 함수는 CGI에만 사용해야 합니다.(like PHP, or Python, and so forth)
  7. 완전 정적 웹사이트를 serve 할 수 있어야 합니다.
  8. 클라이언트는 파일을 upload 할 수 있어야 한다.
  9. 최소 GET, POST, DELETE 3개의 메서드는 구현해야 합니다.

구성 파일 요구사항

  • config 파일을 읽을 때는 굳이 poll로 read 할 필요는 없습니다.
NGINX 구성파일의 server부분에서 영감을 얻을 수 있습니다.
  1. 서버가 여러 포트를 수신할 수 있어야 합니다.
  2. server_name을 설정하거나 설정하지 않습니다.(nginx config 참조)
  3. default error pages를 설정합니다.
  4. 클라이언트 body size를 제한합니다.
  5. 라우팅을 세팅합니다. 정규식을 사용하지는 않고, 설정파일의 하나에 여러 개의 라우팅 설정이 있을 수 있습니다.
  6. HTTP 메서드에 따른 라우팅을 어떻게 할지 정의해야 합니다.
  7. HTTP redirection을 정의해야 합니다.
  8. 파일을 검색할 디렉터리 또는 파일을 정의합니다.
  9. 예를 들어 url /kapouet 이 /tmp/www를 루트로 하는 경우, url /kapouet/pouic/toto/pouet 은 /tmp/www/pouic/toto/pouet로 됩니다.
  10. 요청이 디렉터리인 경우에는 응답할 파일을 설정합니다.
  11. 특정 파일 확장자(예시. php)를 기반으로 CGI를 실행합니다.
  12. 경로가 업로드된 파일을 수락하고 저장할 위치를 구성할 수 있도록 설정해야 합니다.

Config에서 CGI 규칙

  • CGI를 직접 호출하지 않고, 전체 경로를 PATH_INFO로 사용합니다
  • 청크 된 요청의 경우 서버가 청크를 해제해야 하며, CGI는 본문의 끝을 EOF로 예상한다는 점만 기억하세요
  • CGI의 출력도 마찬가지입니다. CGI에서 content_length가 반환되지 않으면 EOF는 반환된 데이터의 끝을 표시합니다.
  • 프로그램은 요청된 파일을 첫 번째 인수로 사용하여 CGI를 호출해야 합니다.
  • CGI는 상대 경로 파일 액세스를 위해 올바른 디렉터리에서 실행해야 합니다
  • 서버는 하나의 CGI(php-CGI, Python 등)로 작동해야 합니다.

보너스

  • 추가할 수 있는 추가 기능은 다음과 같습니다:
  • 쿠키 및 세션 관리 지원
  • 여러 CGI를 처리합니다

주의사항

  1. execve로 다른 웹서버 실행하는 건 금지됩니다.
  2. A request to your server should never hang forever server에 대한 요청이 영원히 지속되어서는 안 됩니다.
  3. -> timeout을 걸라는 말로 해석했습니다.
  4. 서버는 선택한 웹 브라우저와 호환되어야 합니다.
  5. fnctl (fd, F_SETFL, O_NONBLOCK)만 사용가능하고 다른 flag는 금지됩니다.
  6. 중요한 것은 복원력입니다. 서버는 절대로 죽어서는 안 됩니다.
  7. 한 가지 프로그램으로만 테스트하지 마세요. Python이나 Golang 등 더 편리한 언어로 테스트를 작성하세요. 원한다면 C 또는 C++로도 가능합니다.  → 이 부분은 CGI 프로그램을 여러 개의 언어로 작성해 보라는 것으로 이해했습니다.
  8. read/wirte가 poll을 거치지 않는 것은 없어야 합니다.
  9. read/write 이후 errno를 확인하는 것은 금지됩니다.
  10. non-blocking file descriptors를 사용해야 하므로, poll과 같은 함수가 없어도 non-blocking I/O 를 수행할 수 있습니다. 다만, 시스템 리소스를 많이 소비하기 때문에 poll과 같은 함수를 사용하지 않으면 0점입니다.
  11. 서버 스트레스를 테스트하십시오. 어떤 대가를 치르더라도, 서버를 계속 사용할 수 있어야 합니다.
간단한 테스터를 공유해 드렸습니다. 브라우저와 테스트에서 모든 것이 정상적으로 작동한다면 반드시 통과해야 하는 것은 아니지만, 버그를 찾는 데 도움이 될 수 있습니다.

 

테스터

테스트 요구사항 중 일부입니다. 1억바이트씩 20명이 5번 반복해서 보내는 테스트는 상당히 오래 걸렸습니다.

과제에서 기본적으로 테스터를 제공합니다. 실제 과제 요구사항보다 많은 메서드를 구현해야 하고, 설정이 까다로워서 많은 사람들이 테스터를 안 하고 넘어가기도 합니다.
과제에서 적혀있듯 테스터가 완벽하게 돌아가는 게 필수는 아닙니다. 테스터가 서버에 부하가 높은 요청을 많이 보내서 하다 보면 서버가 터지는 경우가 많습니다.
테스트 과정에서 broken pipe 이슈, backlog이슈 등 많은 이슈가 있었고, 그 덕에 더 견고한 서버를 만들 수 있었습니다.
잘 만들어진 테스트는 훨씬 견고하게 프로그래밍할 수 있도록 도와준다는 것을 알았습니다. 테스터를 돌리면서 짰던 코드가 오히려 더 빠른 개발을 할 수 있게 도와줘서, 다음에는 TDD를 한번 해보고 싶다는 생각을 하였습니다.
 

테스터 통과하면 나오는 앵무새 그림

이 과제를 한다면 꼭 이 앵무새를 보기를 추천합니다. 어차피 하는 공부 기왕이면 제대로 만들면 훨씬 더 얻어갈게 많다고 생각합니다.
그 외에 siege 명령어를 사용해서 서버에 부하를 거는 테스트도 해야 하는데, 그건 테스터를 통과했다면 전혀 문제가 안 될 것입니다.
여러 개의 테스터를 동시에 실행할 때 나왔던 leaks를 잡는 등 확실히 테스터가 있으니 더 견고한 서버를 만들 수 있었습니다.

과제 개념

Webserv 과제는 시작하기 전에 모르는 키워드가 많아서 막막한데요, 서브젝트를 보면 공부해야 하는 개념들을 얼추 정리할 수 있습니다.
공부한 내용을 크게 분류하면 5가지 개념으로 나눠볼 수 있습니다.

  1. HTTP
  2. CGI란?
  3. TCP/IP소켓 프로그래밍( 윤성우 님 책 참고 및 Kqeue tutorial)
  4. 웹서버란? (feat. nginx란?)
  5. Nginx config파일

과제를 시작하기 전에 이 다섯 개의 개념을 먼저 학습하고 나서야 드디어 개발에 들어갈 수 있었습니다.
이 중 가장 중요한 것은 소켓 프로그래밍의 개념입니다. 하지만 다른 개념도 알고 가야 하기 때문에, 5개의 주제에 대해서 먼저 게시물도 다루겠습니다.
이후 개념학습이 끝나면, 어떻게 서버를 설계했는지, 어떤 식으로 코드를 구현했는지, 코드를 짜면서 해결했던 문제들을 다루도록 하겠습니다.
자, 그러면 웹서버의 세계로 떠나보겠습니다.
다음 게시물은 HTTP 시리즈로 돌아오겠습니다.

728x90