운영체제는 컴퓨터 시스템의 하드웨어를 관리하고 TCP/IP 네트워크 지원, 웹 리소스를 유지하기 위한 파일 시스템, 현재 연산활동을 제어하기 위한 프로세스 관리를 제공함.
HTTP 프로토콜 구현, 웹 리소스 관리, 웹 서버 관리.
웹 서버 소프트웨어와 웹페이지 제공에 특화된 장비(ex 컴퓨터) 모두를 가리킴.
기능, 형태, 크기가 다양함.
웹 서버 형태
다목적 소프트웨어 웹 서버
네트워크에 연결된 표준 컴퓨터 시스템에서 동작함.
아파치, W3C의 직소 같은 오픈 소스 소프트웨어, 마이크로소프트나 아이플래닛의 웹 서버 같은 상용 소프트웨어를 사용할 수 있음.
임베디드 웹 서버
일반 소비자용 제품에 내장될 목적으로 만들어진 작은 웹 서버 (ex. 프린터, 가전제품)
기기를 간편한 웹 브라우저 인터페이스로 관리할 수 있게 해줌.
Perl로 최소 기능 웹 서버를 구현해보자.
#!/usr/bin/perl
use Socket;
use Carp;
use FileHandle;
# (1) use port 8080 by default, unless overridden on command line
$port = (@ARGV ? $ARGV[0] : 8080);
# (2) create local TCP socket and set it to listen for connections
$proto = getprotobyname('tcp');
socket(S, PF_INET, SOCK_STREAM, $proto) die;
setsockopt(S, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) die;
bind(S, sockaddr_in($port, INADDR_ANY)) die;
listen(S, SOMAXCONN) die;
# (3) print a startup message
printf(" <<<Type-O-Serve Accepting on Port %d>>>\n\n",$port);
while (1)
{
# (4) wait for a connection C
$cport_caddr = accept(C, S);
($cport,$caddr) = sockaddr_in($cport_caddr);
C->autoflush(1);
# (5) print who the connection is from
$cname = gethostbyaddr($caddr,AF_INET);
printf(" <<<Request From '%s'>>>\n",$cname);
# (6) read request msg until blank line, and print on screen
while ($line = <C>)
{
print $line;
if ($line =~ /^\r/) { last; }
}
# (7) prompt for response message, and input response lines,
# sending response lines to client, until solitary "."
printf(" <<<Type Response Followed by '.'>>>\n");
while ($line = <STDIN>)
{
$line =~ s/\r//;
$line =~ s/\n//;
if ($line =~ /^\./) { last; }
print C $line . "\r\n";
}
close(C);
}
% type-o-serve.pl 8080
진짜 웹 서버가 하는 일
최신식 상용 웹 서버는 앞선 예제보다 훨씬 복잡하지만, 공통적으로 몇 가지 일을 수행함.
커넥션을 맺는다. → 클라이언트의 접속을 받아들이거나, 원치 않는 클라이언트라면 닫는다.
요청을 받는다. → HTTP 요청 메시지를 네트워크로부터 읽어드린다.
요청을 처리한다. → 요청 메시지를 해석하고 행동을 취한다.
리소스에 접근한다. → 메시지에서 지정한 리소스에 접근한다.
응답을 만든다. → 올바른 헤더를 포함한 HTTP 응답 메시지를 생성한다.
응답을 보낸다. → 응답을 클라이언트에게 돌려준다.
트랜잭션을 로그로 남긴다. → 로그파일에 트랜잭션 완료에 대한 기록을 남긴다.
1: 클라이언트 커넥션 수락
클라이언트가 이미 서버에 대해 열려있는 지속적 커넥션을 갖고 있다면, 클라는 그 커넥션으로 요청을 보낼 수 있음.
그렇지 않다면, 클라는 서버에 새 커넥션을 열어야 함.
클라가 웹 서버에 TCP 커넥션을 요청하면, 웹 서버는 그 커넥션을 맺고 TCP 커넥션에서 IP 주소를 추출하여 커넥션 맞은편에 어떤 클라가 있는지 확인함.
새 커넥션이 맺어지고 받아들여지면, 서버는 새 커넥션을 커넥션 목록에 추가하고 커넥션에서 오가는 데이터를 지켜보기 위한 준비를 함.
웹 서버는 어떤 커넥션이든 마음대로 거절하거나 닫을 수 있음.
클라의 IP 주소나 호스트 명이 인가되지 않았거나 악의적이라고 알려진 것일 경우 커넥션을 닫음.
클라의 신원을 식별하는 기법은 다음같은 것들이 있음.
클라 호스트 명 식별
hostname lookup으로 클라의 IP 주소를 클라의 호스트 명으로 변환하도록 설정함 (IP 로 Domain Name 을 찾는 기능).
웹 서버는 클라의 호스트 명을 구체적인 접근 제어와 로깅을 위해 사용할 수 있음.
hostname lookup은 시간이 걸릴 수 있어 웹 트랜잭션을 느리게할 수 있음. 많은 대용량 웹 서버는 이를 꺼두거나 특정 콘텐츠에 대해서만 켜놓음.
ident로 클라 사용자 알아내기
ident 프로토콜은 서버에게 어떤 사용자 이름이 HTTP 커넥션을 초기화했는지 찾아낼 수 있게 해줌.
만약 클라가 ident 프로토콜을 지원한다면, 클라는 ident를 보내기 위해 서버와 포트 113번으로 커넥션을 맺고 간단한 요청을 함.
하지만 ident는 조직 내부에서 사용하는 경우만 있고, 공공 인터넷에서는 다음의 이유로 동작하지 않음.
많은 클라 PC의 미지원, 가상 IP 주소 미지원
HTTP 트랜잭션 지연
조작하기 쉬움, 클라 사용자 이름 노출로 인한 사생활 침해
2: 요청 메시지 수신
커넥션에 데이터가 도착하면, 웹 서버는 네트워크 커넥션에서 그 데이터를 읽어 들이고 파싱하여 요청 메시지를 구성함.
요청 메시지 파싱 시 다음과 같은 일을 함.
요청 줄을 파싱 하여 요청 메서드, URI, HTTP 버전을 찾는다.
메시지 헤더를 읽는다. 각 헤더는 CRLF(캐리지 리턴 줄바꿈)로 끝난다.
헤더 끝을 의미하는 CRLF로 끝나는 빈 줄을 찾아낸다.
요청 본문이 있으면 읽어 드린다.(길이는 Content-Length 헤더에 정의되어 있음)
💡
요청 메시지를 파싱 할 때 웹 서버는 입력 데이터를 네트워크로부터 불규칙으로 받고 그 커넥션은 언제라도 무효화될 수 있으므로 메모리에 임시 저장해둘 필요가 있음.
속도가 빠른 룩업 테이블에 저장되어 각 필드에 신속하게 접근함.
웹 서버는 고성능일 경우 수천 개의 커넥션을 동시에 열 수 있도록 지원하는 등 커넥션 입출력 처리 아키텍처에 따라 요청을 처리하는 방식이 달라짐.
단일 스레드 웹서버 방식:한 번에 한 번의 요청을 처리하므로 트랜잭션이 완료되면 다음 커넥션을 처리하므로 처리 도중에 온 커넥션은 무시된다. 그러므로 로드가 작은 서버나 type-o-serve와 같은 진단 도구에서만 적당하다.
멀티 프로세스와 멀티스레드 웹서버:여러 개의 프로세스나 고효율 스레드를 할당한다. 커넥션 마다 스레드를 할당하면 너무 많은 메모리나 시스템 리소스를 소비하므로 최대 개수에 제한을 건다.
다중 I/O 서버:대량의 커넥션 처리를 위해 많은 웹서버가 선택한 방식이다. 모든 커넥션은 하나의 프로세스에 감시당하며 실제로 해야 할 일이 있을 때만 커넥션을 열어준다.
다중 멀티스레드 웹 서버:멀티스레딩과 다중화를 결합한 방식
💡
프로세스 : 어떤 프로그램의 자신만의 변수 집합을 갖는 하나의 독립된 제어 흐름.
스레드 : 프로세스의 더 빠르고 효율적인 버전.
→ 이 둘 모두 하나의 프로그램이 여러 작업을 동시에 할 수 있게 해줌.
3: 요청 처리
웹 서버가 요청을 받으면, 서버는 요청으로부터 메서드, 리소스, 헤더, 본문을 얻어내어 처리함.
4: 리소스의 매핑과 접근
웹 서버가 클라에 콘텐츠 등의 리소스를 전달하려면, 요청 메시지의 URI에 대응하는 알맞은 콘텐츠나 콘텐츠 생성기를 웹 서버에서 찾아 그 콘텐츠의 원천을 식별해야 함.
docroot
일반적으로 웹 콘텐츠를 위해 웹 서버 파일 시스템의 폴더를 예약하는 데 이 문서를 의미함.
웹 서버는 요청 메시지에서 URI를 가져와 문서 루트 뒤에 붙임.
요청 URI : /specials/saw-blade.gif
docroot : /user/local/httpd/files
서버 리소스 : /user/local/httpd/files/specials/saw-blade.gif
이때 ../와 같이 문서 루트 위의 파일을 보려고 하는 URI는 허용하지 말아야 함.
가상 호스팅된 docroot를 만들어 두 웹 사이트를 HTTP host 헤더나 서로 다른 IP 주소를 이용해 구분할 수 있음.
사용자 홈 디렉터리 docroots로 사용자들이 한 대의 웹 서버에서 각자의 개인 웹 사이트를 만들도록 할 수 있음. 슬래쉬(/)와 물결(~) 다음에 사용자 이름이 오는 것으로 시작함. ex) /~lillie/index.html
디렉터리 목록
웹서버는 경로가 파일이 아닌 디렉터리를 가리키는 URL 요청을 받을 수 있음.
그 경우 보통 다음과 같은 행동을 취한다.
에러를 반환함.
디렉터리 대신 특별한 '색인 파일'을 반환함.
디렉터리를 탐색해서 그 내용을 담은 HTML 페이지를 반환함.
보통 그 디렉터리 안의 index.html을 반환함.
DirectoryIndex index.html index.htm home.html home.htm과 같이 아파치 웹 서버에서 아파치가 디렉터리 URL 요청에 대한 응답으로 나열된 파일 중 하나를 찾게 함.
Options -Indexes를 통해 디렉터리 색인 파일 자동 생성을 끌 수도 있음. 열거한 HTML이 반환됐을 때 발견할 수 없는 파일도 드러나게 된다는 단점이 있음.
동적 콘텐츠 리소스 매핑
웹 서버는 요청에 맞게 콘텐츠를 생성하는 프로그램에 URL를 매핑할 수 있음 (동적 리소스 매핑).
예를 들어, ScriptAlias /cgi-bin/ /usr/local/etc/httpd/cgi-programs/는 URI 경로가 /cgi-bin/으로 시작한다면 /usr/local/etc/httpd/cgi-programs/에서 프로그램을 찾아 실행하라는 의미임.
AddHandlercgi-script .cgi처럼 특정 확장자의 파일만 실행하도록 설정할 수 있음.
오늘날은 마이크로소프트의 액티브 서버 페이지와 자바 서블릿과 같은 한층 더 강력하고 효과적인 서버사이드 동적 콘텐츠 지원 기능을 갖고 있음.
Server-Side Includes (SSI)
만약 어떤 리소스가 SSI를 포함하고 있으면 서버는 그 리소스의 콘텐츠를 클라이언트에게 보내기 전에 처리함.
접근 제어
웹 서버는 각각의 리소스에 접근 제어를 할당할 수 있음.
접근이 제어된 리소스에 대한 요청이 도착했을 때 웹 서버는 클라의 IP주소에 근거해 접근을 제어할 수 있고 혹은 리소스에 접근하기 위한 비밀번호를 물어볼 수 있음.
5: 응답 만들기
한번 서버가 리소스를 식별하면, 서버는 요청 메서드로 서술되는 동작을 수행한 뒤 응답 메시지를 반환함.
응답 상태 코드, 응답 헤더, (생성될 경우) 응답 본문
만약 트랜잭션이 응답 본문을 생성한다면, 그 내용을 다음을 포함한 응답 메시지와 함께 돌려보냄.
응답 본문의 MIME 타입을 서술하는 Content-Type 헤더.
응답 본문의 길이를 서술하는 Content-Length 헤더.
실제 응답 본문 내용.
웹 서버는 응답 본문의 MIME 타입을 결정해야 함.
MIME 타입 테이블 예시.
이 외에도 매직 타이핑, 유형 명시, 유형 협상 등 특정한 MIME 타입을 갖도록 설정할 수 있음.
웹 서버는 종종 성공 메시지 대신 리다이렉션 응답을 반환함.
리다이렉션 응답은 3XX 상태 코드로 지칭됨.
Location 응답 헤더는 콘텐츠의 새로운 혹은 선호하는 위치에 대한 URI를 포함함.
유용하게 사용되는 경우
영구히 리소스가 옮겨진 경우: 301(Moved Permanently) 상태 코드를 사용
임시로 리소스가 옮겨진 경우: 임시적이므로 서버는 클라이언트가 나중에 원래 URL로 찾아올 수 있도록 북마크를 갱신하지 않기를 원한다. 303(See Other), 307(Temporary Redirect) 상태 코드가 주로 이럴 때 사용된다.
URL 증강: 303, 307 상태 코드를 사용하며 보통 상태 정보를 내포한 채 새 URL을 생성함. 트랜잭션 간 상태를 유지하는 유용한 방법.
부하 균형: 과부하된 서버 요청을 받으면 부하가 적은 서버로 리다이렉트 시켜주는 것으로 303, 307을 사용한다.
친밀한 다른 서버가 있을 때: 303, 307을 주로 사용하며 클라에 대한 정보를 갖고 있는 다른 서버로 리다이렉트할 수 있음.
디렉터리 이름 정규화: 클라이언트가 / 를 빠뜨렸다면 대부분 웹서버는 정상적으로 동작할 수 있도록 /를 붙인 URL로 리다이렉트 함.
6: 응답 보내기
웹 서버는 여러 클라이언트의 커넥션을 가질 수 있음. 그들 중 일부는 아무것도 하지 않고, 일부는 데이터를 요청하고 있고, 일부는 응답 데이터를 전송하고 있을 것임.
그러므로 서버는 커넥션 상태를 추적해야 하며 지속적인 커넥션은 특별히 주의해야 함.
비지속적인 커넥션이라면, 서버는 모든 메시지를 전송했을 때 자신 쪽의 커넥션을 닫을 것이다.
지속적인 커넥션이라면, 서버가 Content-Length 헤더를 바르게 계산하기 위해 특별한 주의를 필요로 하는 경우나, 클라가 응답이 언제 끝나는지 알 수 없는 경우, 커넥션은 열린 상태를 유지함.
7: 로깅
마지막으로 트랜잭션이 완료되었을 때 웹 서버는 트랜잭션이 어떻게 수행되었는지에 대한 로그를 로그파일에 기록함.