teklog

HTTP 완벽 가이드 - 5 웹 서버

2025/03/02

n°65

category : HTTP

img

MIME 타입, 서버사이드의 동적 컨텐츠 생성이 도움이 된다. 4장의 내용이 약간 어려워, 우선 주요 내용들을 간략히 추리고 인상 깊은 내용에 하이라이트 하였다.

다채로운 웹 서버

  • 웹서버 : 웹 서버 소프트웨어와 웹 페이지 제공에 특화된 장비 양쪽 모두

  • HTTP 및 그와 관련된 TCP 처리를 구현한 것. TCP 커넥션 관리에 대한 책임을 운영체제와 나눠 갖는다.

  • HTTP 프로토콜을 구현, 웹 리소스 관리, 웹 서버 관리 기능 제공

  • 다목적 소프트웨어 웹 서버, 임베디드 웹 서버 등. 다양한 형태로 있을 수 있다.

웹서버가 하는 일

  1. 커넥션을 맺는다

  2. 요청을 받는다

  3. 요청을 처리한다

  4. 리소스에 접근한다

  5. 응답을 만든다

  6. 응답을 보낸다

  7. 트랜잭션을 로그로 남긴다

1 클라이언트 커넥션 수락

  • 클라이언트가 서버에 이미 열려있는 지속적 커넥션을 갖고 있다면 요청을 보내기 위해 그 커넥션을 사용할 수 있음

  • 아닐 시 클라이언트는 서버에 대한 새 커넥션을 열어야함

새 커넥션 다루기

  • 클라이언트에서 서버로 TCP 커넥션을 요청하면, 서버는 그 커넥션을 맺고 TCP 커넥션에서 IP 주소를 추출하여 클라이언트 확인

  • 받아들여 진다면, 커넥션 목록에 추가함

  • 웹 서버는 어떤 커넥션이든 거절하거나 즉시 닫을 수 있음

  • 클라이언트의 주소, 호스트명이 인가되지 않거나 악의적이라고 판단하는 경우. 다른 신원 파악 방법 사용 가능

클라이언트 호스트 명 식별

  • 역방향 DNS를 사용해 사용자 IP를 클라이언트 호스트명으로 변환할 수 있음.

  • 구체적인 접근 제어, 로깅을 위해 사용할 수 있으나, 시간이 걸려 트랜잭션을 느리게 할 수 있음. 따라서 대용량 서버는 호스트명 분석을 꺼두거나 특정 콘텐츠에만 켜놓음.

ident를 이용해 사용자 알아내기

  • IETF idemt 프로토콜을 사용하여 서버에게 어떤 사용자 이름이 HTTP 커넥션을 초기화했는지 알 수 있게함.

  • 웹 서버 로깅에 유용. 일반적인 로그 포맷에서 ident 필드에 HTTP 요청 사용자 이름을 담음

  • 성능, 보안, 대부분의 클라이언트 pc에서 identd를 사용하지 않아 잘 사용되지 않음

2 요청 메시지 수신

HTTP 메시지의 내부 표현

  • 커넥션에 내용 도착 시, 서버는 데이터를 읽고 파싱하여 요청메시지를 구성함

  • 요청 메시지 파싱할 때 하는 일들

    • 요청 메서드, 리소스 URL, 프로토콜 버전을 찾음. 요청줄은 CRLF 문자열로 줄바꿈 구분

    • 메시지 헤더를 읽음. 마찬가지로 줄바꿈 CRLF로 끝남.

    • 헤더의 끝을 CRLF로 찾음.

    • 본문이 있다면 읽음

  • 요청 메시지 파싱 중 서버는 입력 데이터를 네트워크로부터 불규칙적으로 받음.

  • 이 때 커넥션은 언제든 무효화될 수 있음.

  • 파싱하여 이해가 가능할 정도의 분량이 될 때만큼 임시로 저장해둘 필요가 있음

  • 몇몇 서버는 요청 메시지를 쉽게 다루도록 서버 내부의 자료 구조에 저장함.

  • 요청 메시지의 각 조각에 대한 포인터, 길이를 담을 수 있음.

  • 헤더는 룩업 테이블에 저장되어 각 필드에 빠르게 접근 가능.

커넥션 입/출력 처리 아키텍쳐

  • 고성능 서버는 수 천개의 커넥션을 동시에 열 수 있도록 지원함.

  • 서버가 전 세계의 클라이언트와 각각 한개 이상의 커넥션을 통해 통신할 수 있게 해줌.

  • 서버는 항상 요청에 대기함. 서버 아키텍처에 따라 요청을 처리하는 방식이 다름

단일 스레드 웹 서버

  • 한번에 하나씩 요청을 처리함. 트랜잭션 완료 시 다음 커넥션이 처리됨. 심각한 성능 저하 가능.

  • 간단하지만 처리 도중 모든 다른 커넥션이 무시되어 진단 도구에서만 사용.

멀티 프로세스와 멀티 스레드 웹 서버

  • 여러 요청을 동시에 처리하기 위해 여러개의 프로세스 혹은 고효율 스레드를 할당

  • 스레드/프로세스는 필요할 때마다 혹은 미리 만들어질 수 있음.

  • 몇몇 서버는 매 커넥션마다 스레드/프로세스 하나를 할당하지만, 수백~수만 개의 동시 커넥션을 처리할 때 너무 많은 메모리와 리소스를 소비

  • 대다수 멀티스레드 웹 서비스는 스레드/프로세스의 최대 개수를 제한

다중 I/O 서버

  • 대량의 커넥션을 지원하기 위해 다수의 웹서버가 채택한 다중 아키텍쳐

  • 모든 커넥션은 동시에 활동이 감시됨

  • 커넥션 상태가 바뀌면, 그 커넥션에 대해 작은 양의 처리가 수행됨

  • 처리 완료 시 다음번 상태 변경을 위해 열린 커넥션 목록으로 돌아감

  • 실제로 처리해야할 요청이 있을 때 작업이 수행됨

  • 유휴 상태의 커넥션에 매여 기다리느라 리소스를 낭비하지 않음

다중 멀티스레드 웹서버

  • CPU 여러 개의 이점을 위해 멀티스레딩과 다중화를 결합

  • 여러 개의 스레드는 각각 열려있는 커넥션을 감시하고 각 커넥션에 대해 조금씩 작업을 수행

3 요청 처리

  • 서버는 요청으로부터 메서드, 리소스, 헤더, 본문body(없는 경우도 있음)을 얻어 처리

  • POST를 비롯한 몇몇 메서드는 요청 메시지에 본문이 있을 것을 요구

  • OPTIONS를 비롯한 다수의 메서드는 요청에 본문이 있는 것을 허용하되 요구하지 않음

  • GET과 같이 요청 메시지에 본문을 금지하는 메서드도 있다

  • HTTP 명세에 따르면 GET 요청은 리소스의 상태 변화를 변경하지 않아야 한다. get 요청의 본문은 의미가 없으며 서버에 의해 무시될 수 있다. URL에서 리소스의 일부나 필터링한 일정 결과를 얻으려면 본문 대신 쿼리(질의)를 사용

4 리소스의 매핑과 접근

  • 웹 서버는 리소스 서버다. HTML, JPEG 이미지 같은 미리 만들어진 콘텐츠를 제공할 수 있다.

  • 서버 위에 동작하는 리소스 생성 애플리케이션을 통해 만들어진 동적 컨텐츠도 제공한다.

  • 요청 메시지 URI에 대응하는 알맞은 컨텐츠나 컨텐츠 생성기를 웹 서버에 찾아서 그 컨텐츠의 원천을 식별해야한다.

docroot

  • 가장 단순한 방법은 요청 URI를 웹 서버의 파일 시스템 안에 있는 파일 이름으로 사용하는 것

  • 일반적으로 서버 파일 시스템의 docroot 폴더를 웹 컨텐츠를 위해 예약해둠.

가상 호스팅된 docroot:

  • 가상 호스팅 웹 서버는 각 사이트에 분리된 문서 루트를 주는 방법으로 한 웹서버에 여러 웹 사이트를 호스팅한다.

  • 가상 호스팅 웹 서버는 URI나 Host 헤더에서 얻은 IP 주소나 호스트 명을 이용해 올바른 문서 루트를 식별

사용자 홈 디렉터리 docroots:

  • 한 대의 웹 서버에서 여러 개인 사용자가 사이트를 사용할 때

  • 빗금('/'), 물결('~') 다음에 사용자 이름이 오는 것으로 시작하는 URI는 그 사용자 개인 문서 루트를 가리킴.

디렉터리 목록

  • 경로가 파일이 아닌 디렉터리를 가리키는 디렉터리 URL에 대한 요청을 받을 수 있음

  • 디렉터리 URL 요청을 받았을 때 다른 행동을 취하도록 설정

    • 에러를 반환

    • 디렉터리 대신 특별한 '색인 파일'을 반환

    • 디렉터리를 탐색하여 그 내용을 담은 HTML 페이지를 반환

  • 대부분의 웹서버는 요청한 URL에 대응하는 디렉터리 안에서 index.html로 이름 붙은 파일을 찾는다.

  • 디렉터리에 index.html이란 이름의 파일이 있다면 이를 반환할 것이다.

  • Directoryindex 설정 지시자를 사용해 기본 디렉터리 파일로 사용될 파일 이름의 집합을 설정할 수 있다.

동적 컨텐츠 리소스 매핑

  • 요청에 맞게 컨텐츠를 생성하는 프로그램에 URL을 매핑하는 것

  • 어떤 리소스가 동적이라면, 그에 대한 동적 컨첸츠 생성 프로그램이 어디있고, 어떻게 실행하는지 알려줄 수 있어야한다.

  • 대부분의 웹서버는 동적 리소스를 식별하고 매핑할 수 있는 기본적인 메커니즘을 갖고 있다.

서버사이드 인클루드

  • 만약 어떤 리소스가 서버사이드 인클루드를 포함하고 있는 것으로 설정된다면, 서버는 그 리소스의 콘텐츠를 클라이언트에게 보내기 전에 처리한다.

  • 쉬운 동적 컨텐츠 생성: 서버는 컨텐츠에 변수 이름이나 내장된 스크립트가 될 수 있는 어떤 특별한 패턴이 있는지 검사를 받는다. 특별한 패턴은 변수 값이나 실행 가능한 스크립트의 출력 값으로 치환된다.

접근 제어

  • 각 리소스에 접근 제어를 할당할 수 있다.

  • 접근 제어되는 리소스에 대한 요청이 도착하면, 서버는 클라이언트 IP 주소에 근거하여 접근을 제어하거나 비밀번호를 요구할 수 있다.

5 응답 만들기

응답 엔터티

  • 트랜잭션이 응답 본문을 생성한다면, 그 내용을 응답 메시지와 함께 돌려보냄

  • 본문이 있다면 응답 메시지는 주로 MIME 타입을 서술하는 Content-Type 헤더, 응답 본문의 길이를 서술하는 Content-Length 헤더, 실제 응답 본문 내용 을 포함한다.

MIME 타입 결정

  • 웹 서버는 응답 본문의 MIME 타입을 결정해야 하는 책임이 있다.

  • 서버는 특정 파일이 특정 MIME 타입을 갖게끔 설정할 수 있다.

  • MIME 타입과 리소스를 연결하는 여러가지 방법들이 있다.

  • mime:types: MIME 타입을 나타내기 윟 파일 이름 확장자를 사용. 각 리소스의 MIME 타입을 계산하기 위한 확장자별 MIME 타입이 담겨 있는 파일을 탐색. 확장자 기반 타입 연계가 가장 흔한 방법이다.

  • 매직 타이핑: 아파티 웹 서버는 각 파일의 MIME 타입을 알아내기 위해 파일의 내용을 검사하여 알려지 패턴에 대한 테이블에 해당하는 패턴이 있는지 찾아볼 수 있다. 느리지만, 표준 확장자 없이 이름 지어진 경우에 편리.

  • 유형 명시: 특정 파일이나 디렉터리 안의 파일들이 확장자 내용에 상관없이 어떤 MIME 타입을 갖도록 서버를 설정할 수 있다.

  • 유형 협상: 한 리소스가 여러 종류의 문서 형식에 속하도록 설정할 수 있다. 서버가 사용자와 협상 과정을 통해 사용하기 가장 좋은 형식(과 대응하는 MIME 타입)을 판별할 것인지 여부를 설정할 수 있다.

리다이렉션

요청에 따라 3XX 상태 코드의 리다이렉션 응답을 내려줄 때가 있다. Location 응답 헤더는 컨텐츠의 새로운 / 선호하는 위치에 대한 URI를 포함한다. 다음의 경우에 유용하다.

  • 영구히 리소스가 옮겨진 경우: 리소스가 새로운 URL이 부여되어 새 위치로 옮겨지거나 이름이 바뀌었을 때. 서버는 리다이렉트를 위해 클라이언트에 301 Moved Permanently 상태 코드를 보낸다.

  • 임시로 리소스가 옮겨진 경우: 임시로 리소스가 옮겨지거나 이름이 변경된 경우 리다이렉트 한다. 303 see other, 307 Temporary Redirect 상태 코드 사용.

  • URL 증강: 서버가 문맥 정보를 포함시키기 위해 재 작성된 URL로 리다이렉트한다. 요청이 도착했을 때, 서버는 상태 정보를 내포한 새 URL을 생성하고 사용자를 이 URL로 리다이렉트 한다. 클라이언트는 리다이렉트 이후 상태정보가 추가된 완전한 URL을 포함한 요청을 다시 보낸다. 트랜잭션 간 상태를 유지하는 유용한 방법이다. 303, 307 상태 코드를 사용한다.

  • 부하 균형: 과부화된 서버가 요청을 받으면, 서버는 부하가 덜한 서버로 클라이언트를 리다이렉트할 수 있다. 303, 307 상태 코드를 사용한다.

  • 익숙한 다른 서버가 있을 때: 서버는 사용자에 대한 정보를 갖을 수 있다. 해당 클라이언트를 그 클라이언트에 대한 정보를 갖고 있는 다른 서버로 리다이렉트 할 수 있다. 303, 307 상태코드를 사용.

  • 디렉터리 이름 정규화: 클라이언트가 디렉터리 이름에 대한 요청을 하는데 끝에 빗금(/)을 빠뜨렸다면, 대부분의 웹 서버는 상대경로가 정상적으로 작동하게끔 추가한 URL로 리다이렉트 한다.

6 응답 보내기

  • 커넥션을 받을 때와 마찬가지로 데이터를 보낼 때도 비슷한 이슈가 있을 수 있다.

  • 서버는 여러 클라이언트에 대한 많은 커넥션을 가질 수 있다.

  • 그들 중 일부는 아무것도 안하는 상태고, 일부는 서버로 데이터를 보내며, 일부는 클라이언트로 돌려줄 응답 데이터를 보내고 있다.

  • 서버는 커넥션 상태를 추적해야 하며, 지속적인 커넥션은 특별히 주의해서 다뤄야한다.

  • 지속적인 커넥션이라면, 서버가 Content-Length 헤더를 바르게 계산하기 위해 주의를 필요로 하는 경우, 클라이언트 응답이 언제 끝나는지 알 수 없는 경우에 커넥션을 열어둘 것이다.

  • 비지속적인 커넥션이라면, 서버는 모든 메시지를 전송하고 자신 쪽의 커넥션을 닫는다.

7 로깅

  • 트랜잭션이 완료되었을 때 어떻게 수행됐는 지에 대한 로그를 로그 파일에 기록한다.

  • 서버는 로깅에 대한 여러 설정 양식을 제공한다.