크래프톤 정글 8주 차 CSAPP 11장의 웹서버 구현을 진행했다. 그리고 소형 웹서버를 기반으로 프록시 서버를 구현해야 한다. CSAPP 11 장의 내용은 이전 포스트 들을 확인할 수 있다.
컴퓨터 시스템 : CSAPP 11장 정리 - 11.6 종합설계 :소형 웹 서버 Part.1
11.6절 Putting It Together: The Tiny Web Server이 절에서는 지금까지 배운 내용을 종합하여 작동 가능한 소형 웹 서버 Tiny를 구현한다. 이 서버는 다음을 처리할 수 있다:정적 콘텐츠 (HTML, 이미지 등)동적
www.gowoong.com
컴퓨터 시스템 : CSAPP 11장 정리 - 11.6 종합설계 :소형 웹 서버 Part.2
지난 포스팅에서 11.6장 Tiny Web Server의 main()과 doit() 함수에 대해 알아봤다.2025.05.03 - [크래프톤 정글 (컴퓨터 시스템: CSAPP)/11장 네트워크 프로그래밍] - 컴퓨터 시스템 : CSAPP 11장 정리 - 11.6 종합설
www.gowoong.com
우선 proxylab.pdf 문서를 확인해 필수적으로 구현해야 하는 내용과 놓쳐서는 안 되는 지시사항을 확인하겠다.
반드시 구현해야 할 주요 기능
1. 기본 동작: Sequential Proxy (Part I)
- HTTP/1.0 GET 요청만 지원 (POST 등은 선택 사항)
- 브라우저의 요청을 받아서 서버에 전송하고, 응답을 브라우저로 전달하는 프록시 구현
- 요청 파싱 시:
- Host, path 등을 추출하여
- 요청을 GET /path HTTP/1.0 형식으로 변환 후 서버에 전달
- 반드시 \r\n 줄 바꿈, 마지막 빈 줄 등 HTTP 형식을 준수해야 함
2. 헤더 처리 (4.2절)
- 다음 헤더는 무조건 포함해서 서버로 전달해야 함:
- Host: ...
- User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:10.0.3)...
- Connection: close
- Proxy-Connection: close
- 브라우저가 보낸 기타 헤더는 그대로 전달
3. 포트 처리
- proxy 실행 시 포트 번호를 명령줄 인자로 받음
예: ./proxy 15213 - URL에 포트가 있을 경우(http://host:8080/...) 해당 포트로 접속해야 함
4. 동시성 처리 (Part II)
- 여러 클라이언트 요청을 동시에 처리하도록 구현해야 함
- pthread_create로 각 요청마다 스레드 생성, detached 모드로 실행
- 스레드 안전한 함수(open_clientfd, open_listenfd 등) 사용
5. 캐시 기능 (Part III)
- 프록시에서 자주 요청되는 객체를 메모리에 캐싱
- 다음 제한 조건을 반드시 만족해야 함:
- 총 캐시 크기 ≤ 1MB (MAX_CACHE_SIZE)
- 개별 객체 크기 ≤ 100KB (MAX_OBJECT_SIZE)
- 객체가 최대 크기를 넘으면 캐시 하지 않음
- 캐시 교체 정책은 LRU(최근 사용 안 한 객체 제거) 유사 정책 사용
- 캐시 접근은 스레드 안전해야 함:
- 여러 읽기 스레드는 동시에 접근 가능
- 쓰기 중에는 읽기/쓰기 모두 차단
- 단순한 mutex 하나로 전부 막는 것은 허용되지 않음
절대 놓치지 말아야 할 주의사항
- HTTP/1.1 요청 → HTTP/1.0 요청으로 변환해서 서버로 보낼 것
- 멀티라인 헤더는 처리하지 않아도 되나, 잘못된 요청에도 crash 없이 동작해야 함
- SIGPIPE 무시: write 중 연결 종료 시 오류처리 (EPIPE, ECONNRESET) 필요
- 바이너리 데이터 처리 가능하도록 구현 (텍스트 외 이미지 등 포함)
- driver.sh 실행해서 autograder 점수 확인:
- Correctness: 40점
- Concurrency: 15점
- Caching: 15점
프록시 서버 설계
구현을 진행하기 앞서 먼저 가장 큰 틀에서 프록시 서버를 어떻게 구현할지에 대한 정리를 하려고 한다.
1. 포워드 프록시(Forward Proxy)
우리가 구현해야 할 프록시 서버는 포워드 프록시이다. 평소 리버스 프록시를 주로 사용하던 나로서는 생소한 개념이었다.
구분 | 포워드 프록시 | 리버스 프록시 |
위치 | 클라이언트 앞 | 서버 앞 |
목적 | 클라이언트 보호, 접근제어, 익명성 | 서버 보호, 로드밸런싱, 캐싱 |
예시 | 회사 내부에서 외부 웹 접근 통제 | Nginx, Apache HTTP Server 앞단 |
예시:
GET http://www.google.com HTTP/1.0
Host: www.google.com
프록시 동작:
- 이 요청을 수신한 프록시가 www.google.com에 연결하여 요청을 대신 보내고,
- 응답을 받은 후 클라이언트에게 전달
프록시 서버에 대한 기본 개념을 가진 상태로 CSAPP 11장에서 배운 내용을 이용해 생각해 보겠다. 11장에서는 소켓 연결을 통한 웹 서버를 구현하는데 클라이언트와 서버가 소켓 연결의 단계를 거쳐서 연결을 수행한다. 하지만 프록시 서버를 구현하는 지금에는 어떻게 클라리언트와 최종 서버와의 연결이 진행될 수 있을까?
그래서 내가 생각한 것은 프록시 서버가 클라이언트 입장에서는 서버가 되고 최종 서버 입장에서는 프록시 서버가 클라이언트와 같이 연결이 된다면 프록시를 구현할 수 있겠다고 생각했다.
2. 필요 함수 설계
큰 틀에서 프록시 서버의 구현 아이디어는 구성했다. 그럼 어떤 함수를 구현해야 할지 생각해 보겠다.
2.1 main 함수
기존 tiny 서버를 기반으로 하면 될 것 같다. main에서 클라이언트와의 연결을 진행하기 위해 프록시 서버를 일종의 서버 호스트로 설정하는 과정을 기존 tiny 서버와 같이 진행하는 것이 좋을 것 같다.
2.2 doit 함수
기존의 tiny 서버에 있던 doit을 이용하되 많은 부분이 변경될 함수 중 하나이다. 먼저 기존 tiny 서버에서 진행하던 정적, 동적 요청에 대한 처리와 관련된 로직을 전부 제거를 해야 할 것 같다. 프록시 서버는 최종 서버가 응답으로 보내는 데이터를 클라리언트에 보내주기만 하면 되니 굳이 파일들을 처리하는 로직이 필요가 없을 것 같다.
그렇다면 어떤 기능들이 이 함수에 포함되어야 할까? 그건 처음 확인한 구현해야 하는 주요 기능에 있다. 아래 내용이 구현해야 하는 기능이었다:
- 요청 파싱
- Host, path 등을 추출하여
- 요청을 GET /path HTTP/1.0 형식으로 변환 후 서버에 전달
요청 헤더에 있는 데이터를 추출하여 그 요청을 목적지 서버에 보내면 되는 것이다. 그러면 기존에 있던 함수를 변경하여 사용하면 될 것 같다.
이 과정에서 목적지 서버와 소켓 연결을 하기 위한 정보를 추출하고 목적지 서버와 open_clientfd() 함수를 사용해 연결을 시도할 예정이다.
이후 목적지 서버로부터 받은 데이터를 클라이언트에 반환하는 작업을 수행할 예정이다.
2.3 요청 분리
사용자 요청을 그대로 서버로 보내면 안 될 수 있다고 생각했다. 그래서 요청 헤더에서 필수적으로 필요한 헤더와 무시해도 되는 헤더 그 외의 나머지 헤더를 분리하여 저장할 수 있는 함수가 될 것이다.
2.4 parser 함수
이 함수에서 수행하게 될 것으로 기대하는 작업은 호스트, 포트, 경로에 대한 것을 추출하는 것이다. 이렇게 분리해서 추출하고 이 정보를 이용해 목적지 서버와 소켓 연결을 시도할 것이다.
2.5 반환 함수
목적지 서버로 부터 받은 응답을 클라이언트에 전달하는 역할을 할 것이다.
2.6 그 외의 함수
요청 에러를 처리하는 clienterror 함수는 기존에 tiny 웹 서버의 그것을 사용할 것이다.
다음 포스팅에서 실제 구현을 진행하겠다.
'크래프톤 정글' 카테고리의 다른 글
[WebProxy-Lab] proxy 서버 구현하기 Part.3 - 동시성 처리 (0) | 2025.05.05 |
---|---|
[WebProxy-Lab] proxy 서버 구현하기 Part.2 - 프록시 서버 구현 (1) | 2025.05.05 |
Malloc Lab 회고 (0) | 2025.04.30 |
[CS] 이더넷(Ethernet) (2) | 2025.04.28 |
[CS] DMA(Direct Memory Access) (0) | 2025.04.28 |