- HTTP 통신을 진단해주는 간단한 웹 서버를 node.js로 작성해본다.
- 어떻게 웹 서버가 HTTP 트랜젝션을 처리하는지 단계별로 설명해본다.
웹 서버가 하는 일
- 웹 서버는 HTTP 및 그와 관련된 TCP 처리를 구현한 것이다.
- 웹 서버는 자신이 제공하는 리소스를 관리하고, 웹 서버를 설정, 통제, 확장하기 위한 관리 기능을 제공한다.
- 웹 서버는 TCP 커넥션 관리에 대한 책임을 운영체제와 나눠 갖는다.
- 운영체제는
- 컴퓨터 시스템의 하드웨어를 관리하고 TCP/IP 네트워크 지원
- 웹 리소스를 유지하기 위한 파일 시스템
- 현재 연산 활동을 제어하기 위한 프로세스 관리를 제공한다.
- 웹 서버는
- 다목적 소프트웨어 웹 서버를 표준 컴퓨터 시스템에 설치하고 실행할 수 있다.
- 몇몇 공유기들은 웹브라우저로 접근 가능한 관리 기능을 제공한다.
node.js로 간단 웹서버를 구현해 보았다.
공통적으로는 아래 그림에서 하는 일을 한다.
- 커넥션을 맺는다.
- 클라의 접속을 받아들이거나, 원치 않는 클라라면 닫는다.
- 요청을 받는다.
- HTTP 요청 메세지를 네트워크로부터 읽어 들인다.
- 요청을 처리한다.
- 요청 메세지를 해석하고 행동을 취한다.
- 리소스에 접근한다.
- 메세지에서 지정한 리소스에 접근한다.
- 응답을 만든다.
- 올바른 헤더를 포함한 HTTP 응답 메세지를 생성한다.
- 응답을 보낸다.
- 응답을 클라에게 돌려준다.
- 트랜잭션을 로그로 남긴다.
- 로그파일에 트랜잭션 완료에 대한 기록을 남긴다.
1. 클라이언트 커넥션 수락
- 클라가 이미 서버에 대해 열려있는 지속적 커넥션을 갖고 있다면, 해당 커넥션을 사용할 수 있다.
- 그렇지 않다면, 새로운 커넥션을 열 필요가 있다.
1.1 새 커넥션 다루기
- 클라가 웹 서버에 TCP 커넥션을 요청하면, 웹 서버는 그 커넥션을 맺고 TCP 커넥션에서 IP 주소를 추출하여 커넥션 맞은편에 어떤 클라가 있는지 확인한다.
- 새 커넥션이 맺어지고 받아들여지면, 서버는 새 커넥션을 커넥션 목록에 추가하고, 커넥션에서 오가는 데이터를 지켜보기 위한 준비를 한다.
- 웹 서버는 어떤 커넥션이든 마음대로 거절하거나 즉시 닫을 수 있다.
- 어떤 웹서버들은 클라의 IP 주소나 호스트명이 인가되지 않았거나, 악의적이라고 알려진 것인 경우 커넥션을 닫는다.
1.2 클라이언트 호스트명 식별
- **역방향 DNS(reverse DNS)**를 사용해서 클라의 IP 주소를 클라의 호스트 명으로 변환하도록 설정되어 있다.
- 웹서버는 클라이언트 호스트 명을 구체적인 접근 제어와 로깅을 위해 사용할 수 있다.
- 호스트명 룩업(hostname lookup)은 꽤 시간이 많이 걸릴 수 있어, 웹 트랜잭션을 느려지게 할 수 있다.
- 많은 대용량 웹 서버는 호스트명 분석(hostname resolution)을 꺼두거나, 특정 콘텐츠에 대해서만 켜놓는다.
1.3 ident를 통해 클라이언트 사용자 알아내기
- 몇몇 웹서버는 또한 IETF ident 프로토콜을 지원한다.
- IETF: 국제 인터넷 표준화 기구
- Internet Engineering Task Force
- ident 프로토콜은 서버에게 어떤 사용자 이름이 HTTP 커넥션을 초기화했는지 찾아낼 수 있게 해준다.
- 이 정보는 특히 웹서버 로깅에서 유용하기 때문에, 널리 쓰이는 일반 로그포맷(Common Log Format)의 두 번째 필드는 각 HTTP 요청의 ident 사용자 이름을 담고 있다.
아래는 어떻게 ident 프로토콜이 동작하는지 보여준다.
- 만약 클라가 ident 프로토콜을 지원한다면, 클라는 ident 결과를 위해 TCP port 113번을 listen한다.
공공 인터넷에서는 다음과 같은 이유로 잘 동작하지 않는다.
- 많은 클라 PC는 identd 신원확인 프로토콜 데몬 소프트웨어를 실행하지 않는다.
- ident 프로토콜은 HTTP 트랜잭션을 유의미하게 지연시킨다.
- 방화벽이 ident 트래픽이 들어오는 것을 막는 경우가 많다.
- ident 프로토콜은 안전하지 않고 조작하기 쉽다.
- ident 프로토콜은 가상 IP 주소를 잘 지원하지 않는다.
- 클라 사용자 이름의 노출로 인한 프라이버시 침해의 우려가 있다.
2. 요청 메세지 수신
- 커넥션 데이터가 도착하면,
웹서버는 네트워크 커넥션에서 그 데이터를 읽어 들이고,
파싱하여 요청 메세지를 구성한다.
요청 메세지를 파싱할 때, 웹서버는 다음과 같은 일을 한다.
- 요청줄을 파싱하여 요청 메서드, 지정된 리소스의 식별자(URI), 버전 번호를 찾는다.
각 값은 스페이스 한 개로 분리되어 있으며, 요청줄은 캐리지 리턴 줄바꿈(CRLF
) 문자열로 끝난다. (참고) - 메세지 헤더들을 읽는다. 각 메세지 헤더는
CRLF
로 끝난다. - 헤더의 끝을 의미하는
CRLF
로 끝나는 빈 줄을 찾아낸다. - 요청 본문이 있다면, 읽어 들인다. (길이는
Content-Length
헤더로 정의한다.)
2.1 메세지의 내부 표현
- 몇몇 웹서버는 요청 메세지를 쉽게 다룰 수 있도록 내부의 자료구조에 저장한다.
- 자료구조는 요청 메세지의 각 조각에 대한 포인터와 길이를 담을 수 있을 것이고,
헤더는 속도가 빠른 룩업 테이블에 저장되어 각 필드에 신속하게 접근할 수 있을 것이다.
2.2 커넥션 입력/출력 처리 아키텍처
- 고성능 웹서버는 수천 개의 커넥션을 동시에 열 수 있도록 지원한다.
- 어떤 커넥션들로부터는 요청이 느리게, 혹은 드물게 흘러 들어오고,
또 어떤 것들은 나중에 일어날 활동을 위해 대기하고 있는데 비해,
일부 커넥션들은 웹 서버로 급속히 요청을 보내고 있을 것이다. - 웹 서버들은 항상 새 요청을 주시하고 있다. 요청은 언제라도 도착할 수 있기 때문이다.
웹 서버는 아키텍처의 차이에 따라 요청을 처리하는 방식도 달라진다.
2.2.1 단일 스레드 웹서버
- 단일 스레드 웹서버는 한 번에 하나씩 요청을 처리한다.
- 트랜젝션이 완료되면, 다음 커넥션이 처리된다.
- 처리 도중에 모든 다른 커넥션은 무시된다.
- 성능 문제를 만들어내므로, 로드가 적은 서버나 진단도구에서만 적당하다.
2.2.2 멀티스레드와 멀티스레드 웹서버
- 여러 요청을 동시에 처리하기 위해 여러 개의 프로세스 혹은 고효율 스레드를 할당한다.
프로세스란 어떤 프로그램의 자신만의 변수 집합을 갖는 하나의 독립된 제어 흐름이다.
스레드는 프로세스의 더 빠르고 더 효율적인 버전이다.
스레드와 프로세스 모두 하나의 프로그램이 여러 작업을 동시에 할 수 있게 해준다. - 몇몇 서버는 매 커넥션마다 스레드/프로세스 하나를 할당하지만,
서버가 수만 개의 동시 커넥션을 처리할 때 그로 인해 만들어진 수많은 프로세스나 스레드는 너무 많은 메모리나 시스템 리소스를 소비한다. - 많은 멀티스레드 웹 서비스가 스레드/프로세스의 최대 개수를 제한을 건다.
2.2.3 다중 I/O
- 대량의 커넥션을 지원하기 위해, 많은 웹 서버는 다중 아키텍처를 채택했다.
- 다중 아키텍처에서는 모든 커넥션은 동시에 그 활동을 감시당한다.
- 커넥션의 상태가 바뀌면 (예: 데이터를 사용할 수 있게 되거나, 에러가 발생),
그 커넥션에 대해 작은 양의 처리가 수행된다. - 그 처리가 완료되면, 커넥션은 다음번 상태 변경을 위해 열린 커넥션 목록으로 돌아간다.
- 커넥션의 상태가 바뀌면 (예: 데이터를 사용할 수 있게 되거나, 에러가 발생),
- 스레드와 프로세스는 유휴 상태의 커넥션에 매여 기다리느라 리소스를 낭비하지 않는다.
2.2.4 다중 멀티스레드 웹서버
- 몇몇 시스템은 자신의 컴퓨터 플랫폼에 올라와 있는 CPU 여러 개의 이점을 살리기 위해 멀티스레딩과 다중화(multiplexing)를 결합한다.
- 여러 개의 스레드(보통 하나의 물리적 프로세스)는 각각 열려있는 커넥션(혹은 열려있는 커넥션의 부분집합)을 감시하고, 각 커넥션에 대해 조금씩 작업을 수행한다.
3. 요청 처리
4. 리소스의 매핑과 접근
- 웹서버는 리소스 서버다.
- 그들은 HTML 페이지나 JPEG 이미지 같은 미리 만들어진 콘텐츠를 제공하며,
서버 위에서 동작하는 리소스 생성 어플리케이션을 통해, 만들어진 동적 콘텐츠도 제공한다. - 웹서버가 클라에 콘텐츠를 전달하려면, 그 전에 요청 메세지의 URI에 대응하는 알맞은 콘텐츠나 콘텐츠 생성기를 웹서버에서 찾아서 그 콘텐츠의 원천을 식별해야 한다.
4.1 Docroot
- 가장 단순한 형태는 요청 URI를 웹서버의 파일 시스템 안에 있는 파일 이름으로 사용하는 것이다.
- 일반적으로 웹서버 파일시스템의 특별한 폴더를 웹 콘텐츠를 위해 예약해둔다.
- 이 폴더는 문서 루트 혹은
docroot
로 불린다. - 웹서버는 요청 메세지에서 URI를 가져와서 문서 루트 뒤에 붙인다.
httpd.conf 설정 파일에 docroot 경로를 설정해두면 요청 헤더의 URI를 문서 루트 뒤에 붙인다.
1 | GET /specials/joy.gif HTTP/1.0 |
1 | DocumentRoot /usr/local/httpd/files |
/usr/local/httpd/files
+ /specials/joy.gif
- 서버는 docroot 이외의 부분이 노출되는 일이 생기지 않도록 주의해야 한다.
1
https://feel5ny.github.io/.../
4.1.1 가상 호스팅 된 docroot
- 가상 호스팅 웹서버는 각 사이트에 그들만의 분리된 문서 루트를 주는 방법으로, 한 웹서버에서 여러 개의 웹사이트를 호스팅한다.
- 가상 호스팅 웹서버는 URI나 Host 헤더에서 얻은 IP 주소나 호스트 명을 이용해 올바른 문서 루트를 식별한다.
4.1.2 사용자 홈 디렉터리 docroots
- 사용자들이 한대의 웹서버에서 각자의 개인 웹사이트를 만들 수 있도록 해주는 것이다.
- 보통
/
과 물결표~
다음에 사용자 이름이 오는 것으로 시작하는 URI는 그 사용자의 개인 문서 루트를 가리킨다. - 개인
docroot
는 주로 사용자 홈 디렉터리 안에 있는 public_html로 불리는 디렉터리지만, 설정에 따라 다르다.
4.2 디렉터리 목록
- 웹서버는 경로가 파일이 아닌, 디렉터리를 가리키는, 디렉터리 URL에 대한 요청을 받을 수 있다.
- 대부분의 웹서버는 클라가 디렉터리 URL을 요청했을 때,
다음과 같이 몇가지 다른 행동을 취하도록 설정할 수 있다.- 에러를 반환한다.
- 디렉터리 대신 특별한 ‘색인파일’을 반환한다.
- 디렉터리를 탐색해서 그 내용을 담은 HTML 페이지를 반환한다.
- 대부분의 웹서버는 요청한 URL에 대응되는 디렉터리 안에서
index.html
혹은index.htm
으로 이름 붙은 파일을 찾아 반환한다. - 사용자가 디렉터리 URI를 요청했을 때, 기본 색인 파일이 없고 디렉터리 색인 기능이 꺼져있지 않다면, 많은 웹서버는 자동으로 그 디렉터리의 파일들을 크기, 변경일 및 그 파일에 대한 링크와 함께 열거한 HTML 파일을 반환한다.
4.3 동적 콘텐츠 리소스 매핑
- 웹서버는 URI를 동적 리소스에 매핑할 수도 있다.
- 요청에 맞게 콘텐츠를 생성하는 프로그램에 URI를 매핑하는 것이다.
- 웹서버들 중에서 어플리케이션 서버라고 불리는 것들은 웹서버를 복잡한 백엔드 어플리케이션과 연결하는 일을 한다.
- 어플리케이션 서버는 그에 대한 동적 콘텐츠 생성 프로그램이 어디에 있는지, 그리고 어떻게 그 프로그램을 실행하는지 알려줄 수 있어야 한다.
- 대부분의 웹서버는 동적 리소스를 식별하고 매핑할 수 있는 기본적인 매커니즘을 갖고 있다.
- URI의 경로명이 실행 가능한 프로그램이 위치한 디렉터리로 매핑되도록 설정하는 기능을 제공하는 웹서버도 있다.
- 서버가 실행 가능한 경로명을 포함한 URI로 요청을 받으면, 그 경로에 대응하는 디렉터리에서 프로그램을 찾아 실행하려 시도한다.
4.4 서버사이드 인클루드 Server-Side Includes, SSI
- 만약 어떤 리소스가 서버사이드 인클루드를 포함하고 있는 것으로 설정되어 있다면, 서버는 그 리소스의 콘텐츠를 클라에게 보내기 전에 처리한다.
- 서버는 콘텐츠에 변수 이름이나 내장된 스크립트가 될 수 있는 어떤 특별한 패턴이 있는지 검사를 받는다.
- 특별한 패턴은 변수 값이나 실행 가능한 스크립트의 출력 값으로 치환된다. 이것은 동적 콘텐츠를 만드는 쉬운 방법이다.
4.5 접근 제어
- 각각의 리소스에 접근제어를 할당할 수 있다.
- 웹서버는 클라의 IP 주소에 근거하여 접근을 제어할 수 있고, 접근하기 위한 비밀번호를 물어볼 수도 있다.
- HTTP 인증에서 자세히 다룬다.
5. 응답 만들기
- 응답메세지는 응답상태코드, 응답헤더, 응답본문을 포함한다.
5.1 응답 엔터티
본문이 있다면, 응답 메세지는 주로 다음을 포함한다.
- 응답 본문의 MIME 타입을 서술하는 Content-Type 헤더
- 응답 본문의 길이를 서술하는 Content-Length 헤더
- 실제 응답 본문의 내용
5.2 MIME 타입 결정하기
웹 서버에게는 응답 본문의 MIME타입을 결정해야하는 책임이 있다.
MIME타입과 리소스를 연결하는 여러 가지 방법이다.
5.2.1 mime.types
- 파일 이름의 확장자를 사용할 수 있다.
- 웹서버는 각 리소스의 MIME 타입을 계산하기 위해 확장자별 MIME타입이 담겨있는 파일을 탐색한다.
- 확장자에 따라 mime타입을 결정한다.
확장자별 mime타입 - mdn
- 확장자에 따라 mime타입을 결정한다.
5.2.2 매직 타이핑 Magic typing
- 아파치 웹 서버는 각 파일의 MIME타입을 알아내기 위해 파일의 내용을 검사해서 알려진 패턴에 대한 테이블 (매직 파일이라 불린다)에 해당하는 패턴이 있는지 찾아볼 수 있다.
- 느리긴 하지만, 파일이 표준 확장자 없이 이름 지어진 경우에는 편리하다.
매직 넘버. 다른 종류의 파일의 문법은 구조 상 보여지는 타입을 결정하는 데 도움을 줍니다. 예를 들어, 각 GIF 파일들은 47 49 46 38 16진수 값 [GIF89]로 시작되며 PNG 파일의 경우 89 50 4E 47 [.PNG]로 시작됩니다. 파일의 모든 타입들이 이런 매직 넘버를 가지고 있는 것은 아니므로 100% 신뢰할 만한 시스템은 아니기도 합니다.
출처
5.2.3 유형 명시 Explicit typing
- 특정 파일이나 디렉터리 안의 파일들이 파일 확장자나 내용에 상관없이
어떤 MIME 타입을 갖도록 웹 서버를 설정할 수 있다.
5.2.4 유형 협상 Type negotiation
- 어떤 웹 서버는 한 리소스가 여러 종류의 문서 형식에 속하도록 설정할 수 있다.
- 이때 웹 서버가 사용자와의 협상 과정을 통해 사용하기 가장 좋은 형식(그리고 대응하는 MIME타입)을 판별할 것인지의 여부도 설정할 수 있다.
- 웹서버는 특정 파일이 특정 MIME타입을 갖게끔 설정할 수도 있다.
5.3 리다이렉션
- 웹 서버는 종종 성공 메세지 대신 리다이렉션 응답을 반환한다.
- 웹 서버는 요청을 수행하기 위해, 브라우저가 다른 곳으로 가도록 리다이렉트할 수 있다.
- Location 응답헤더는 콘텐츠의 새로운 혹은 선호하는 위치에 대한 URI를 포함한다.
- 30x 상태코드 정리본
- 영구히 리소스가 옮겨진 경우 -
301
- 임시로 리소스가 옮겨진 경우 -
303
,307
- URL 증강 -
303
,307
- 부하 균형 -
303
,307
- 친밀한 다른 서버가 있을 때 -
303
,307
- 디렉터리 이름 정규화
5.3.1 영구히 리소스가 옮겨진 경우
- 리소스는 새 URL이 부여되어 새로운 위치로 옮겨졌거나 이름이 바뀌었을 수 있다.
- 웹 서버는 클라에게 리소스의 이름이 바뀌었으므로, 클라는 북마크를 갱신하거나 할 수 있다고 말해줄 수 있다.
301 Moved Permanetly
상태코드는 이런 종류의 리다이렉트를 위해 사용된다.
5.3.2 임시로 리소스가 옮겨진 경우
- 리소스가 임시로 옮겨지거나, 이름이 변경된 경우, 서버는 클라를 새 위치로 리다이렉트 하길 원할 것이다.
- 이름 변경이 임시적이기 때문에, 서버는 클라가 나중에는 원래 URL로 찾아오고 북마크도 갱신하지 않기를 원한다.
303 See Other
과307 Temporary Redirect
상태코드는 이런 종류의 리다이렉트를 위해 사용된다.
5.3.3 URL 증강
- 서버는 종종 **문맥 정보(Context)**를 포함시키기 위해 재 작성된 URL로 리다이렉트한다.
- 요청이 도착했을 때, 서버는 상태 정보를 내포한 새 URL을 생성하고 사용자를 이 새 URL로 리다이렉트한다.
상태 정보가 추가된 URL은 흔히 뚱뚱한 URL이라고도 부른다.
- 클라는 리다이렉트를 따라가서, 상태 정보가 추가된 완전한 URL을 포함한 요청을 다시 보낸다.
303 See Other
과307 Temporary Redirect
상태코드를 사용한다.
5.3.4 부하 균형
- 과부하된 서버가 요청을 받으면,
서버는 클라를 좀 덜 부하가 걸린 서버로 리다이렉트할 수 있다. 303 See Other
과307 Temporary Redirect
상태코드를 사용한다.
5.3.5 친밀한 다른 서버가 있을 때
- 웹 서버는 어떤 사용자에 대한 정보를 가질 수 있다.
- 서버는 클라를 그 클라에 대한 정보를 갖고 있는 다른 서버로 리다이렉트할 수 있다.
303 See Other
과307 Temporary Redirect
상태코드를 사용한다.
5.3.6 디렉터리 이름 정규화
- 클라가 디렉터리 이름에 대한 URI를 요청하는데 끝에 빗금(
/
)을 빠뜨렸다면,
대부분의 웹 서버는 상대경로가 정상적으로 동작할 수 있도록
클라를 슬래시를 추가한 URI로 리다이렉트한다.
6. 응답 보내기
- 서버는 여러 클라에 대한 많은 커넥션을 가질 수 있다. 그들 중 일부는 아무것도 안 하고 있는 상태이고, 일부는 서버로 데이터를 보내고 있으며, 또 다른 일부는 클라로 돌려줄 응답 데이터를 실어 나르고 있을 것이다.
- 서버는 커넥션 상태를 추적해야 하며,
지속적인 커넥션은 특별히 주의해서 다룰 필요가 있다. - 비지속 커넥션이라면, 서버는 모든 메세지를 전송했을 때 자신 쪽 커넥션을 닫을 것이다.
- 지속적인 커넥션이라면,
서버가Content-Length
헤더를 바르게 계산하기 위해 특별히 주의를 필요로 하는 경우,
클라가 응답이 언제 끝나는지 알 수 없는 경우, 커넥션은 열린 상태를 유지할 것이다.
7. 로깅
- 트랜잭션이 완료되었을 때, 웹서버는 트랜잭션이 어떻게 수행되었는지에 대한 로그를 로그파일에 기록한다.
- 대부분의 웹서버는 로깅에 대한 여러 가지 설정 양식을 제공한다.
- 자세한건 책의 21장..
- HTTP 완벽가이드 책을 보고 이해한 내용을 정리 한 글입니다.
참고자료