다채로운 웹서버
웹 서버는 HTTP 요청을 처리하고 응답을 제공한다.
1) 웹 서버 구현
웹 서버는 HTTP 처리를 구현한다. 웹 서버는 제공하는 리소스를 관리하고, 웹 서버를 설정, 통제, 확장하기 위한 관리 기능을 제공한다.
웹 서버는 여러 형태로 존재한다.
- 다목적 소프트웨어 웹 서버를 표준 컴퓨터에 설치하고 실행한 것
- 몇 개의 컴퓨터 칩만으로 구현된 웹 서버를 내장시켜서 완전한 관리 콘솔로 제공한 것
2) 다목적 소프트웨어 웹 서버
다목적 소프트웨어 웹 서버는 네트워크에 연결된 표준 컴퓨터 시스템에서 동작한다.
아파치, Nginx, W3C의 직소 같은 오픈소스 소프트웨어를 사용하거나 마이크로소프트나 아이플래닛의 웹 서버 같은 상용 소프트웨어를 사용할 수 있다. ➡️ 웹 서버 소프트웨어는 거의 모든 컴퓨터와 운영체제에서 동작한다.
3) 임베디드 웹 서버
임베디드 웹 서버는 소비자용 제품에 내장될 목적으로 만들어진 작은 웹 서비스다.
임베디드 웹 서버는 소비자용 기기를 간단한 웹 브라우저 인터페이스로 관리할 수 있게 해준다.
진짜 웹 서버가 하는 일
웹 서버는 공통적으로 몇 가지 일들을 수행한다.
- 커넥션을 맺는다.
클라이언트의 접속을 받아들이거나 원치 않는 클라이언트라면 닫는다.
- 요청을 받는다.
HTTP 요청 메시지를 네트워크로부터 읽어 들인다.
- 요청을 처리한다.
요청 메시지를 해석하고 행동을 취한다.
- 리소스에 접근한다.
메시지에서 지정한 리소스에 접근한다.
- 응답을 만든다.
올바른 헤더를 포함한 HTTP 응답 메시지를 생성한다.
- 응답을 보낸다.
응답을 클라이언트에게 돌려준다.
- 트랜잭션을 로그로 남긴다.
로그파일에 트랜잭션 완료에 대한 기록을 남긴다.
클라이언트 커넥션 수락
클라이언트가 이미 서버에 대해 열려있는 지속적 커넥션을 갖고 있다면 클라이언트는 요청을 보내기 위해 해당 커넥션을 사용할 수 있다.
그렇지 않다면 클라이언트는 서버에 대한 새 커넥션을 열어야 한다.
1) 새 커넥션 다루기
클라이언트가 웹 서버에 TCP 커넥션을 요청하면, 웹 서버는 그 커넥션을 맺고 TCP 커넥션에서 IP 주소를 추출하여 커넥션 맞은편에 어떤 클라이언트가 있는지 확인한다. 서버는 새 커넥션을 커넥션 목록에 추가한다.
2) 클라이언트 호스트명 식별
웹 서버는 역방향 DNS를 사용해서 클라이언트의 IP 주소를 클라이언트의 호스트명으로 변환한다.
웹 서버는 클라이언트 호스트명을 구체적인 접근 제어와 로깅을 위해 사용할 수 있다.
3) ident를 통해 클라이언트 사용자 알아내기
웹 서버는 IETF ident 프로토콜을 지원한다.
ident 프로토콜은 서버에게 어떤 사용자 이름이 HTTP 커넥션을 초기화했는지 찾아낼 수 있게 해준다.
이 정보는 웹 서버 로깅에서 유용하지만, 공공 인터넷에서는 잘 동작되지 않는다.
요청 메시지 수신
커넥션에 데이터가 도착하면 웹 서버는 네트워크 커넥션에서 그 데이터를 읽어 들이고 파싱하여 요청 메시지를 구성한다.
요청 메시지 파싱할 때, 웹 서버가 하는 일
- 요청 줄을 파싱 ➡️ 요청 메서드, 지정된 리소스의 식별자 (URI), 버전 번호를 찾는다. (요청줄은 CRLF 문자열로 끝난다)
- 메시지 헤더들을 읽는다. (각 메시지 헤더는 CRLF 문자열로 끝난다)
- 헤더의 끝을 의미하는 CRLF로 끝나는 빈 줄을 찾아낸다(존재한다면)
- 요청 본문이 있다면, 읽는다 (길이는 Content-Length 헤더로 정의된다)
1) 메시지의 내부 표현
몇몇 웹 서버는 요청 메시지를 쉽게 다룰 수 있도록 내부의 자료 구조에 저장한다.
예시
요청 메시지의 각 조각에 대한 포인터와 길이를 저장하는 자료구조.
헤더는 속도가 빠른 룩업 테이블에 저장되어 각 필드에 신속하게 접근할 수 있게 된다.
2) 커넥션 입력/출력 처리 아키텍처
웹 서버 아키텍처의 차이에 따라 요청을 처리하는 방식도 달라진다.
단일 스레드 웹 서버
단일 스레드 웹 서버는 한 번에 하나씩 요청을 처리한다.
트랜잭션이 완료된 경우, 다음 커넥션이 처리한다. 이는 성능 문제를 야기할 수 있다.
멀티프로세스와 멀티스레드 웹 서버
멀티프로세스와 멀티스레드 웹 서버는 여러 요청을 동시에 처리하기 위해 여러 개의 프로세스 또는 고효율 스레드를 할당한다.
스레드/프로세스는 필요할 때마다 만들어질 수도 있고, 미리 만들어질 수도 있다.
생성된 스레드/프로세스로 인한 메모리 문제를 방지하기 위해 생성 최대 개수에 제한을 건다.
다중 I/O 서버
대량의 커넥션을 지원하기 위해 많은 웹 서버는 다중 아키텍처를 채택했다.
다중 아키텍처 내의 모든 커넥션은 활동을 감시당한다.
커넥션의 상태가 변경되면, 그 커넥션에 대해 작은 양의 처리가 수행된다. 그 처리가 완료되면 커넥션은 다음번 상태 변경을 위해 열린 커넥션 목록으로 돌아간다. 어떤 커넥션에 대해 작업을 수행하는 것은 그 커넥션에 실제 할 일이 있을 때만이다.
이로써 스레드와 프로세스는 유휴 상태의 커넥션에 매여 기다리느라 리소스를 낭비하지 않을 수 있다.
다중 멀티스레드 웹 서버
여러 개의 CPU 이점을 살리기 위해 멀티스레딩과 다중화(multiplexing)를 결합한다.
여러 개의 스레드는 각각 열려있는 커넥션을 감시하고, 각 커넥션에 대해 조금씩 작업을 수행한다.
요청 처리
웹 서버가 요청을 받으면, 서버는 요청으로부터 메서드, 리소스, 헤더, 본문을 얻어내어 처리한다.
리소스의 매핑과 접근
웹 서버는 리소스 서버다. 정적인 컨텐츠를 제공하며, 서버 위에서 동작하는 리소스 생성 애플리케이션(WAS)을 통해 만들어진 동적 컨텐츠도 제공한다.
웹 서버가 클라이언트에 콘텐츠를 전달하려면 그전에 요청 메시지의 URI에 대응하는 알맞은 콘텐츠나 콘텐츠 생성기를 웹 서버에서 찾아서 그 콘텐츠의 원천을 식별해야 한다.
1) Docroot
웹 서버는 다양한 리소스 매핑을 지원한다.
리소스 매핑의 가장 단순한 형태는 요청 URI를 웹 서버의 파일 시스템 안에 있는 파일 이름으로 사용하는 것이다.
일반적으로 웹 서버 파일 시스템의 특별한 폴더를 웹 콘텐츠를 위해 예약해두며, 이 폴더는 docroot라고 불린다.
웹 서버는 요청 메시지의 URI을 가져와서 문서 루트 뒤에 붙인다.
문서 루트는 httpd.conf 설정 파일에 DocumentRoot 줄을 추가하여 아파치 웹 서버의 문서 루트를 설정할 수 있다.
예시) /specials/saw-blade.gif에 대한 요청
웹 서버는 문서 루트 /usr/local/httpd/files를 갖고 있다고 가정하면, /specials/saw-blade.gif에 대한 요청은 /usr/local/httpd/files/specials/saw-blade.gif 에 접근한다.
⚠️ 서버는 상대적인 url이 docroot를 벗어나서 파일 시스템의 다른 부분이 노출되지 않도록 주의해야 한다.
가상 호스팅된 docroot
가상 호스팅 웹 서버는 각 사이트에 그들만의 분리된 문서 루트를 주는 방법으로 한 웹 서버에서 여러 개의 웹 사이트를 호스팅한다.
가상 호스팅 웹서는 URI 또는 Host 헤더의 IP 주소/호스트 명을 이용해 올바른 문서 루트를 식별한다.
이 방법으로 하나의 웹 서버 위에서 2개의 사이트가 완전히 분리된 컨텐츠를 갖고 호스팅 될 수 있도록 한다.
사용자 홈 디렉터리 docroots
docroot의 다른 활용 방법은 사용자들이 한 대의 웹 서버에서 각자의 웹 사이트를 만들 수 있도록 해주는 것이다.
`/~` 다음에 사용자 이름이 오는 것으로 시작하는 URI는 그 사용자의 개인 문서 루트를 가리킨다.
개인 docroot는 주로 사용자 홈 디렉터리 안에 있는 public_html로 불리는 디렉터리이다. (설정으로 변경 가능하다)
2) 디렉터리 목록
웹 서버는 경로가 파일이 아닌 디렉터리를 가리키는 디렉터리 URL에 대한 요청을 받았을 때, 다음과 같은 행동을 취하도록 설정할 수 있다.
- 에러 반환
- 특별한 색인 파일을 반환
- 디렉터리를 탐색하여 HTML 페이지를 반환
대부분의 경우, 요청한 URL에 대응되는 디렉터리 안에서 index.html으로 이름붙은 파일을 찾아서 반환한다.
3) 동적 콘텐츠 리소스 매핑
웹 서버는 URI를 동적 리소스에 매핑할 수 있다. 즉, 요청에 맞게 콘텐츠를 생성하는 프로그램에 URI를 매핑하는 것이다.
4) 서버사이드 인클루드(SSI)
많은 웹서버가 서버사이드 인클루드도 지원한다.
만약 어떤 리소스가 서버사이드 인클루드를 포함하고 있는 것으로 설정되어 있다면, 서버는 그 리소스의 콘텐츠를 클라이언트에게 보내기 전에 처리한다.
5) 접근 제어
웹 서버는 각각의 리소스에 접근 제어를 할당할 수 있다.
접근 제어되는 리소스에 대한 요청이 도착했을 때, 웹 서버는 클라이언트의 IP 주소에 근거하여 접근을 제어할 수 있고, 리소스에 접근하기 위한 비밀번호를 물어볼 수도 있다.
응답 만들기
서버가 리소스를 식별하면, 서버는 요청 메서드로 서술되는 동작을 수행한 뒤 응답 메시지를 반환한다.
응답 메시지에 포함되어야 할 요소
- 응답 상태 코드
- 응답 헤더
- 응답 본문 (필요한 경우)
1) 응답 엔티티
트랜잭션이 응답 본문을 생성한다면, 그 내용을 응답 메시지와 함께 돌려보낸다.
본문이 있다면 응답 메시지는 다음을 포함해야 한다.
- 응답 본문의 MIME 타입을 서술하는 Content-Type 헤더
- 응답 본문의 길이를 서술하는 Content-Length 헤더
- 실제 응답 본문의 내용
2) MIME 타입 결정하기
웹 서버는 응답 본문의 MIME 타입을 결정해야 한다.
MIME 타입과 리소스를 연결하는 여러가지 방법
- mime.types
MIME 타입을 나타내기 위해 파일 이름의 확장자를 사용한다. (확장자 기반 타입 연계)
- 매직 타이핑(Magic Typing)
각 파일의 MIME 타입을 알아내기 위해 파일의 내용을 검사해서 알려진 패턴에 대한 테이블에 해당하는 패턴이 있는지 탐색한다.
파일이 표준 확장자 없이 명시된 경우에 편리하다.
- 유형 명시(Magic Typing)
특정 파일 등의 내용에 상관 없이 어떤 MIME 타입을 갖도록 웹 서버를 설정할 수 있다.
- 유형 협상(Type negotiation)
어떤 웹 서버는 한 리소스가 여러 종류의 문서 형식에 속하도록 설정할 수 있다.
이 경우, 웹 서버는 사용자와의 협상 과정을 통해 사용하기 가장 좋은 형식을 판별할 것인지 여부를 설정할 수 있다.
3) 리다이렉션
웹 서버는 종종 성공 메시지 대신 리다이렉션 응답을 반환한다. 웹 서버는 요청을 수행하기 위해 브라우저가 다른 곳으로 가도록 리다이렉트할 수 있다. Location 응답 헤더는 콘텐츠의 새로운 혹은 선호하는 위치에 대한 URI를 포함한다.
리다이렉트가 유용한 경우
- 영구히 리소스가 옮겨진 경우
리소스는 새 URI이 부여되어 새로운 위치로 옮겨졌거나 이름이 변경되었을 수 있다.
301 Moved Permanently 상태 코드는 이런 경우에 사용된다.
- 임시로 리소스가 옮겨진 경우
임시로 옮겨지거나 이름이 변경된 경우, 서버는 클라이언트를 새 위치로 리다이렉트하기를 원한다.
이름 변경은 임시적이므로, 서버는 클라이언트가 나중에는 원래 URI로 찾아오고 북마크 갱신도 원하지 않는다.
303 See Other / 307 Temporary Redirect 상태 코드는 이런 종류의 리다이렉트를 위해 사용된다.
- 부하 균형
과부하된 서버가 요청을 받으면, 부하가 적은 서버로 리다이렉트할 수 있다.
303 See Other / 307 Temporary Redirect 상태 코드를 사용한다,
- 친밀한 다른 서버가 있을 때
웹 서버는 어떤 사용자에 대한 정보를 가질 수 있다. 서버는 클라이언트를 그 클라이언트에 대한 정보를 갖고 있는 다른 서버로 리다이렉트할 수 있다.
303 See Other / 307 Temporary Redirect 상태 코드를 사용한다.
- 디렉터리 이름 정규화
클라이언트가 디렉터리 이름에 대한 URI를 요청하는데 끝에 `/`를 빠뜨린 경우, 웹 서버는 상대 경로가 정상적으로 동작할 수 있도록 클라이언트를 슬래시를 추가한 URI로 리다이렉트한다.
응답 보내기
웹 서버는 여러 클라이언트에 대한 많은 커넥션을 가질 수 있다. 커넥션 중 일부는 유휴 상태이고, 일부는 서버로 데이터를 보내고 있을 것이며, 나머지는 클라이언트로 돌려줄 응답 데이터를 보내고 있다.
서버는 커넥션 상태를 추적해야 하며, 지속적인 커넥션은 특별히 주의해서 다뤄야 한다.
지속적인 커넥션의 경우, 서버가 Content-Length 헤더를 바르게 계산하기 위해 특별한 주의를 필요로 하는 경우나, 클라이언트가 응답이 언제 끝나는지 알 수 없는 경우에 커넥션은 열린 상태를 유지하게 된다.
로깅
트랜잭션이 완료되었을 때, 웹 서버는 트랜잭션이 어떻게 수행되었는지에 대한 로그를 로그파일에 기록한다.