11.5 웹 서버

CSAPP11 네트워크 프로그래밍(2)

11.5 웹서버

11.5.1 웹 기초

Clients 와 servers 는 HyperText Transfer Protocol(HTTP)를 사용하여 의사소통한다.

  • Client와 server는 TCP 연결을 설립한다.
  • client는 컨텐츠(content)을 요구한다(request)
  • server는 요청된 컨텐츠에 응답한다(respond).
  • client와 서버는 연결을 끊는다(결국 언젠가는)

11.5.2 Web content

웹 서버는 컨텐츠(content)를 client에게 반환한다.

content : 연관된 MIME(Multipurpose Internet Mail Extension) 타입을 갖는 바이트 배열

ex. MIME 타입의 예시

  • text/html HTML document
  • text/plain Unformatted text
  • image/gif Binary image encoded in GIF format
  • image/png Binary image encoded in PNG format
  • image/jpeg Binary image encoded in JPEG format

Static and Dynamic content

HTTP 응답에서 반환된 컨텐츠는 정적(static)일수도 동적(dynamic)일수도 있다.

  • Static content : 파일에 저장되고 HTTP 요청에 반응하여 회수(retrieved)된다.

    • 예시 : HTML 파일, 사진(image), 음성(audio clips)
    • 요청(request)은 어떤 컨텐츠 파일인지를 확인한다
  • Dynamic content : HTTP 요청에 반응하여 실행중에 만들어진 컨텐츠

    • 예시 : client를 대신하여 실행된 프로그램에 의해 만들어진 컨텐츠
    • 요청(request)은 실행 코드를 가진 파일을 확인한다

Bottom line : 웹 컨텐츠는 server에 의해 관리되는 파일과 연관이 있다.

URL과 어떻게 client와 server가 URL을 이용할까?

  • 파일의 고유한 이름 : URL(Universal Resouce Locator)
  • URL 예시 : http://www.cmu.edu:80/index.html
  • clients는 유추(infer)하기 위해 앞에 오는 것들(prefix,접미사)을 활용한다. => http://www.cmu.edu:80
    • 어떤 종류(프로토콜)의 서버에 접속할 것인가? -> HTTP
    • 서버가 어디에 있는가? -> www.cmu.edu
    • 서버가 어떤 port를 listen 하는가? -> 80
  • server는 뒤에 오는 것들(suffix,접두사)을 활용한다. => /index.html
    • 요청(request)이 정적 컨텐츠인지 동적 컨텐츠인지 결정한다. -> 사실 표준 규칙은 없지만 보통 cgi-bin 디렉토리에 실행파일들이 있다.
    • 파일 시스템에서 파일을 찾는다. -> suffix에서 앞의 ‘/’는 리눅스의 home directory를 나타낸다.(루트 디렉토리 X)
    • suffix가 최소일때는 ‘/’인데, 모든 서버는 이것을 ‘/index.html’ 같은 기본 filename으로 확장한다.

11.5.3 HTTP 트랜잭션

HTTP Requests

HTTP 요청 : 0이나 요청 헤더(request headers) 앞에 오는 요청 라인(request line)

Request line : <method> <uri>(Uniform Resource Identifier), <version>

  • <method>GET, POST, OPTIONS, HEAD, PUT, DELETE, TRACE 중 하나이다. 이중 GET 많이 쓰며, 다룰 예정

  • GET 메소드는 서버에게 URI에 의해 식별되는 내용을 반환할 것을 지시.

  • <uri> 는 일반적으로 프록시(proxies)를 위한 URL이고, 파일 이름과 옵션인 인자들을 포함하는 server를 위한 URL의 접미어(suffix)이다.

    • URL은 URI의 한 종류이다.
  • <version>은 요청(request)이 준수해야할 HTTP 버전이다.

Request headers : <header name>: <header data>

  • 서버에 추가적인 정보를 제공한다.

HTTP Responses

HTTP 응답 : 0이나 응답 헤더(response headers) 앞에 오는 응답 라인(response line)이며 컨텐츠 앞에 올수도 있다. 헤더와 컨텐츠는 빈 줄(“\r\n”)로 구분된다.

Response line : <version> <status code> <status msg>

  • <version> : 응답이 준수해야 할 HTTP 버전
  • <status code> : 3비트 양수, 요청의 특성 나타냄 (numeric status)
  • <status msg> : 상응하는 영어 문자이다.(에러코드를 나타냄)
Status code Status message Description(내용)
200 OK 요청이 에러없이 이루어짐
301 Moved permanently 컨텐츠가 Location header에 있는 hostname으로 이동함
400 Bad request server가 요청을 이해하지 못함
403 Forbidden server가 요청된 파일로 접근하는데 허가가 부족함
404 Not found server가 요청된 파일을 찾을 수 없음
501 Not implemented server가 요청된 방식을 지원하지 않음
505 HTTP version not supported server가 요청의 version을 지원하지 않음

Response headers : <header name>: <header data>

  • 응답에 대한 추가적인 정보 제공
  • Content-Type : 응답 본체 내의 컨텐츠의 MIME 타입
  • COntent-Length : 응답 본체 내의 컨텐츠의 길

HTTP Transaction 의 두가지 예시

1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
whaleshark> telnet www.cmu.edu 80				/* Client : open connection to server */
Trying 128.2.42.52...							/* Telnet prints 3 lines to terminal */
Connected to WWW-CMU-PROD-VIP.ANDREW.cmu.edu.
Escape character is '^]'.
GET / HTTP/1,1									/* Client : request line */
Host : www.cmu.edu								/* Client : required HTTP/1.1 header */
    											/* Client : empty line terminates headeres */
HTTP/1.1 301 Moved permanently					/* Server : response line */
Date : Wed, 05 Nov 2014 17:05:11 GMT			/* Server : followed by 5 response headers */
Server: Apache/1.3.42 (Unix)					/* Server : this is an Apache server */
Location : http://www.cmu.edu/index.shtml		/* Server : page has moved here */
Transfer-Encoding : chunked						/* Server : response body will be chunked */
Content-Type : text/html; charset=...			/* Server : expect HTML in response body */
    											/* Server : empty line terminates headers */
15c												/* Server : first line in response body */
<HTML><HEAD>									/* Server : start of HTML content */
...
</BODY></HTML>									/* Server : end of HTML content */
0												/* Server : last line in response body */
Connection closed by foreign host.				/* Server : closes connection */
  • HTTP 표준에 따라 각 text 라인은 “\r\n”으로 끝나야 함
  • 빈줄(“\r\n”)은 요청과 반응 headers를 종결한다.

2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
whaleshark> telnet www.cmu.edu 80			/* Client : open connection to server */
Trying 128.2.42.52...						/* Telnet prints 3 lines to terminal */
COnnected to WWW-CMU-PROD-VIP.ANDREW.cmu.edu.
Escape chartacter is '^]'.
GET /index.shtml HTTP/1.1					/* Client : request line */
Host : www.cmu.edu							/* Client : required HTTP/1.1 header */
    										/* Client : empty line terminates headers */
HTTP/1.1 200 OK								/* Server : response line */
Date : Wed, 05 Nov 2014 17:37:26 GMT		/* Server : followed by 4 response headers */
Server : Apache/1.3.42 (Unix)
Transfer-Encoding: chunked
Content-Type : text/html ; charset=...
    										/* Server : empty line terminates headers */
1000										/* Server : begin response body */
<html ..>									/* Server : first line of HTML content */
...
</html>
0											/* Server : end response bosdy */
Connection closed by foreign host.			/* Server : close connection */

Tiny Web Server(소형 웹 서버)

교과서에 묘사된 Tiny Web server

  • Tiny 는 연속적인(sequential) 웹 서버이다.
  • 실제 브라우저에 정적, 동적 컨텐츠를 제공한다.(텍스트, HTML, GIF, PNG, JPEG)
  • 239줄의 C코드로 이루어졌다.
  • 실제 웹서버만큼 완성도있거나 튼튼하진 않다.( “\r\n”대신 “\n”으로 라인을 종결 하는 등의 잘못된 HTTP 요청에 의해 깨질수 있다.)

Tiny Operation

  • client에게서 연결(connection)을 받는다.
  • 연결된 소켓을 통해 client에게서 요청(request)을 읽는다.
  • method,<uri>,<version>으로 나뉘어진다. (만약 method가 GET이 아니면, error를 반환한다.)
  • 만약 URI가 “cgi-bin”을 가지고 있으면 동적(dynamic) 컨텐츠를 제공한다.
    • “abcgi-bingo.html”파일을 가졌다면 잘못 작동될지도 모름.
    • 프로그램을 실행하기 위해 프로세스를 fork 한다.
  • 아니라면(“cgi-bin”이 없다면) 정적(static) 컨텐츠를 제공한다.
    • 출력할 파일을 복사한다.

정적 컨텐츠(Static content)를 제공하는 Tiny

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void serve_static(int fd, char *filename, int filesize)
{
    int srcfd;
    char *srcp, filetype[MAXLINE], buf[MAXBUF];

    /* Send response headers to client */
    get_filetype(filename, filetype);
    sprintf(buf, "HTTP/1.0 200 OK\r\n");
    sprintf(buf, "%sServer: Tiny Web Server\r\n", buf);
    sprintf(buf, "%sConnection: close\r\n", buf);
    sprintf(buf, "%sContent-length: %d\r\n", buf, filesize);
    sprintf(buf, "%sContent-type: %s\r\n\r\n", buf, filetype);
    Rio_writen(fd, buf, strlen(buf));

    /* Send response body to client */
    srcfd = Open(filename, O_RDONLY, 0);
    srcp = Mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0);
    Close(srcfd);
    Rio_writen(fd, srcp, filesize);
    Munmap(srcp, filesize);
}

Serving Dynamic content(동적 컨텐츠 제공)

  1. client는 server에게 요청(request)을 보낸다.

  2. 만약 요청 URI가 “/cgi-bin”문자열(string)을 포함하면, Tiny server는 동적 컨텐츠를 위한 요청이라고 여긴다.

  3. server는 자식 프로세스(child process)를 만들고 그 프로세스 내에 있는 URI에 의해 확인된 프로그램을 실행한다.

  4. 자식 프로세스는 동적 컨텐츠를 실행하고 발생시킨다.

  5. server는 자식 프로세스의 컨텐츠를 수정없이 client에게 보낸다.

동적 컨텐츠 제공에서 생각해보아야 할 문제

  • 어떻게 client는 program arguments를 server에게 전달할까?
  • 어떻게 server는 child(process)에게 이 arguments들을 전달할까?
  • 어떻게 server는 요청과 관련된 다른 정보를 child에게 전달할까?
  • 어떻게 server는 child가 만든 컨텐츠를 얻을까(capture)?
  • 위 질문들은 Common Gateway Interface(CGI)에서 구체적으로 다루어진다.

CGI

자식들(children process)은 CGI spec에 따라 작성되기 때문에 종종 CGI programs라고도 불린다.

그러나 살제로는 CGI는 client(브라우저), 서버, 자식 프로세스 간에 정보를 전송하는 간단한 표준을 정의한다.

CGV는 동적 컨텐츠를 만드는 기존의 기준이였으나, 더 빠른 다른 기술들에 의해 대부분 대체되었다.

whaleshark.ics.cs.cmu.edu:15213/cgi-bin/adder?15213&18213

~edu : host

15213 : port

adder : CGI program

15213&18213 : argument

질문 1 : 어떻게 client는 server에게 프로그램 인자들(arguments)를 전달할까?

GET으로 동적 컨텐츠 제공하기

답 : argument는 URI에 첨부된다.

브라우저에 입력된 URL이나 HTML 링크의 URL에 직접적으로 인코딩될 수 있다.

  • http://add.com/cgi-bin/adder?15213&18213
  • addr 은 첨가를 더할 서버에 있는 CGI 프로그램이다.
  • argument list는 “?”로 시작한다.
  • argument는 “&”로 나뉘어진다.
  • 공간은 “+”와 “%20”으로 표현된다.

URL suffix(접미사) : cgi-bin/adder?15213&18213

1
2
3
4
5
6
7
8
9
10
11
12
13
Result displayed on browser:

Welcome to add.com: THE Internet

additional portal.



The answer is: 15213 + 18213 = 33426



Thanks for visiting!

질문 2 : 어떻게 서버가 child에게 arguments를 전달할까?

답 : QUERY_STRING 변수 환경에서

  • ”?” 이후에 모든것을 포함하는 단일 문자열

  • For add: QUERY_STRING="15213&18213"

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    /* Extract the two arguments */
    if ((buf = getenv("QUERY_STRING")) != NULL) {
        p = strchr (buf, '&');
        *p = '\0';
        strcpy(arg1, buf);
        strcpy(arg2, p+1);
        n1 = atoi(arg1);
        n2 = atoi(arg2);
    }
    

질문 3 : 어떻게 서버가 child가 만든 콘텐츠를 가질 수 있는가?

답 : child는 stdout에 출력값을 만든다. 서버는 그 연결된 sockt에 stdout을 재지정(redirect)하기 위해 dup2를 사용한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void serve_dynamic(int fd, char *filename, char *cgiargs)
{
    char buf[MAXLINE], *emptylist[] = {NULL};

    /* Return first part of HTTP response */
    sprintf(buf, "HTTP/1.0 200 OK\r\n");
    Rio_writen(fd, buf, strlen(buf));
    sprintf(buf, "Server: Tiny Web Server\r\n");
    Rio_writen(fd, buf, strlen(buf));

    if (Fork() == 0) { /* Child */
    /* Real server would set all CGI vars here */
    setenv("QUERY_STRING", cgiargs, 1);
    Dup2(fd, STDOUT_FILENO); /* Redirect stdout to client */
    Execve(filename, emptylist, environ) ; /* Run CGI program */
    }
    Wait(NULL) ; /* Parent waits for and reaps child */
}

오직 CGI child process만 컨텐츠 타입과 길이를 알고 있어서, 헤더를 만들어야만 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Make the response body */
sprintf(content, "Welcome to add.com: ");
sprintf(content, "%sTHE Internet addition portal. \r\n<p>", content);
sprintf(content, "%sThe answer is: %d + %d = %d\r\n<p>",
       content, n1, n2, n1 + n2);
sprintf(content, "%sThanks for visiting!\r\n", content) ;

/* Genenrate the HTTP response */
printf("Content-length: %d\r\n", (int)strlen(content));
printf("Content-type: text/html\r\n\r\n");
printf("%s", content);
fflush(stdout);

exit(0);

Serving Dynamic Content With

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
basgh:makoshark> telnet whaleshark.ics.cs.cmu.edu 15213
Trying 128.2.210.175...
Connected to whaleshark.ics.cs.cmu.edu (128.2.210.175).
Escape character is '^]'.
GET /cgi-bin/adder?15213&18213 HTTP/1.0   /* line 5,6 : HTTP request sent by client */

HTTP/1.0 200 OK				/* line 7,8,9 : HTTP response generated by the server */
Server: Tiny Web Server
COnnection: close
Content-length: 117
Content-type: text/html

Welcome to add.com: THE Internet addition portal.
<p>The answer is: 15213 + 18213 = 33426 /* line 10~15 : HTTP response generated by the CGI program */
<p>Thanks for visiting!
Connection closed by foreign host.
bash:makoshark>