11.6 종합 설계: 소형 웹 서버
| |
TINY main
반복실행 서버로 명령줄에서 넘겨받은 포트로의 연결 요청 듣는다.
open_listenfd함수를 호출해서 듣기 소켓을 연다.- TINY는 전형적인 무한 서버 루프 실행
- 반복적으로 연결 요청을 접수(line 32), 트랜잭션 수행(line 36), 자신 쪽의 연결 끝을 닫음(line 37)
doit 함수 : 한개의 HTTP 트랜잭션을 처리
| |
- 한 개의 HTTP 트랜잭션 처리.
- 요청 라인을 읽고 분석(line 11~ 14) <- 위 경우
rio_readlineb함수를 사용해서 읽음 - TINY는 GET 메소드만 지원
- 따라서 클라이언트가 다른 메소드(ex. POST)를 요청하면 에러 메시지를 보내고 main 루틴으로 돌아옴(line 15~19)
- 그 후에 연결을 닫고 다음 연결 요청을 기다림
- 아닐때는 다른 요청 헤더들을 무시(line 20)
- 그 후 URI를 파일 이름과 비어 있을 수도 있는 CGI 인자 스트링으로 분석하고, 요청이 정적인지 동적인지 플래그 설정(line 23)
- 이 파일이 디스크 상에 있지 않으면, 에러 메시지를 클라이언트에 보내고 리턴
- 요청이 정적 컨텐츠라면, 이 파일이 보통 파일인지, 읽기 권한을 가지고 있는지 검증(line 31)
- 맞다면 클라이언트에게 정적 컨텐츠 제공(line 36)
- 요청이 동적 컨텐츠라면, 이 파일이 실행 가능한지 검증(line 39)
- 맞다면 동적 컨텐츠 제공(line 44)
Clienterror함수 : 에러 메시지를 클라이언트에게 보낸다.
| |
HTTP 응답을 응답 라인에 적절한 상태 코드와 상태 메시지와 함꼐 클라이언트에 보냄
브라우저 사용자에게 에러를 설명하는 응답 본체에 HTML 파일도 함께 보냄
- HTML 응답은 본체에서 컨텐츠의 크기와 타입을 나타낸다.
- HTML 컨텐츠를 한 개의 스트링으로 만들었고, 그 크기를 쉽게 결정할 수 있음.
read_requesthdrs함수 : 요청 헤더를 읽고 무시한다.
| |
TINY는 요청 헤더 내의 어떤 정보도 사용하지 않음
요청 헤더를 종료하는 빈 텍스트 줄이 line 6에서 체크하고 있는 carriage return과 line feed 쌍으로 구성되어 있음.
TINY parse_uri : HTTP URI를 분석한다.
| |
TINY : static content를 위한 홈 디렉토리가 자신의 현재 디렉토리고, 실행파일의 홈 디렉토리는 /cgi-bin 이라고 가정.
스트링 cgi-bin을 포함하는 모든 URI는 dynamic content를 요청하는 것이라고 가정. 기본 파일 이름은 ./home.html
URI를 파일 이름과 옵션으로 CGI 인자 스트링을 분석
요청이 정적 컨텐츠를 위한것이면(line 5)
- CGI 인자 스트링을 지운다(line 6)
- URI를 ./index.html 같은 상대 리눅스 경로이름(line 7~8)으로 변환
- 만일 URI가 ‘/‘문자로 끝난다면(line 9), 기본 파일 이름을 추가한다(line 10).
요청이 동적 컨텐츠를 위한것이면(line 13)
- 모든 CGI 인자들을 추출(line 14~20)
- 나머지 URI 부분을 상대 리눅스 파일 이름으로 변환(line 21~22)
serve_static함수 : 정적 컨텐츠를 클라이언트에게 서비스한다.
| |
TINY가 지원하는 5개의 정적 컨텐츠 : HTML 파일, 무형식 텍스트 파일, GIF, PNG, JPEG으로 인코딩된 영상
serve_static함수는 지역 파일의 내용을 포함하고 있는 본체를 갖는 HTTP 응답을 보낸다.
- 파일 이름의 접미어(suffix)부분을 검사해 파일 타입을 결정한다.(line 7)
- 클라이언트에 응답 줄과 응답 헤더를 보낸다(line 8~13) <= 빈 줄 1개가 헤더를 종료하고 있다.
- 요청한 파일의 내용을 연결 식별자 fd로 복사해서 응답 본체를 보낸다.
- 읽기 위해서 filename을 오픈하고 식별자를 얻어오는 과정(line 18)
- 리눅스 mmap함수가 요청한 파일을 가상메모리 영역으로 mapping(line 19)
- 파일을 메모리로 mapping한 후 이 식별자는 필요 없으니 파일을 닫음(line 20) => 없으면 메모리 누수 발생
- 실제로 파일을 클라이언트에게 전송(line 21)
rio_writen함수는 주소 srcp에서 시작하는 filesize 바이트(요청한 파일에 mapping 되어있음)를 클라이언트의 연결 식별자로 복사한다.- 매핑된 가상메모리 주소를 반환(line 22)
serve_dynamic함수 : 동적 컨텐츠를 클라이언트에게 서비스한다.
| |
TINY는 자식 프로세스를 fork하고 그 후에 CGI 프로그램을 자식의 컨텍스트에서 실행하며 모든 종류의 동적 컨텐츠를 제공한다.
serve_dynamic함수
- 클라이언트에 성공을 알려주는 응답 라인을 보내는 것으로 시작
- CGI 프로그램은 응답의 나머지 부분을 보내야 함
- CGI 프로그램이 에러를 만날 수 있다는 가능성을 배제하였기에 우리 기대만큼 견고하지 않음
- 응답의 첫 번째 부분을 보낸 후, 새로운 자식 프로세스를 fork(line 11)
- 자식은 QUERY_STRING 환경변수를 요청 URI의 CGI 인자들로 초기화(line 13)
- 실제 서버는 여기서 다른 CGI 환경변수들도 설정하지만 여기선 생략됨
- 자식은 자식의 표준 출력을 연결 파일 식별자로 재지정(line 14)
- CGI 프로그램을 로드하고 실행(line 15)
- CGI 프로그램이 자식 컨텍스트에서 실행되기 때문에
execve함수를 호출하기 전에 존재하던 열린 파일들과 환경변수들에도 동일하게 접근 가능 - 따라서 CGI 프로그램이 표준 출력에 쓰는 모든 것은 직접 클라이언트 프로세스로 부모 프로세스의 어떤 간섭도 없이 전달
- CGI 프로그램이 자식 컨텍스트에서 실행되기 때문에
- 부모는 자식이 종료되어 정리되는 것을 기다리기 위해
wait함수에 블록됌(line 17)
11.7 요약
- 모든 network application 은 client-server model에 기초하고 있다.
- 이 모델을 사용하면 application은 1 개의 server와 1개 이상의 clients 로 구성된다.
- server는 resouce를 관리하고, client에게 service를 제공한다.
- 클라이언트-서버 모델에서의 기본 연산은 client-server transaction이다.
- 이 트랜잭션은 클라이언트로부터의 request와 이에 대한 서버의 response로 이루어진다.
- 클라이언트와 서버는 Internet이라고 하는 글로벌 네트워크를 통해서 통신한다.
- 프로그래머의 관점에서 인터넷은 전 세계적인 규모의 호스트 집단인데 다음 특성을 지녔다.
- 각 Internet host는 IP adress라고 하는 고유한 32비트 이름을 가진다.
- IP 주소 집합은 Internet domain 이름 집합과 대응된다.
- 서로 다른 인터넷 호스트에서의 process들은 connection을 통해서 서로 통신한다.
- 클라이언트와 서버는 socket interface를 사용해서 연결을 수립한다.
- 소켓은 연결의 end point이며 application에게는 file descriptor(식별자)의 형태로 제공된다.
- 소켓 인터페이스는 소켓 식별자를 열고 닫기 위한 함수들을 제공한다.
- 이 식별자들을 서로 읽고 써서 클라리언트와 서버는 통신한다.
- 웹 서버와 이들의 클라이언트들(like 브라우저)은 HTTP protocol을 사용해서 서로 통신한다.
- 브라우저는 서버로부터 static or dynamic contents를 요청한다.
- 정적 컨텐츠를 위한 요청은 서버의 디스크에서 파일을 가져와서 이것을 클라이언트에 돌려주는 방식으로 처리한다.
- 동적 컨텐츠에 대한 요청은 서버에서 child process의 컨텍스트에서 프로그램을 돌리고, 그 출력을 클라이언트로 리턴해서 처리한다.