CGI

42_Cursus·2022년 6월 16일
0

Webserv

목록 보기
4/6
post-thumbnail

CGI

Common Gateway Interface

웹서버는 웹서버 자체로 동적페이지를 처리하지못한다!

웹서버의 역활은 단순히 클라이언트의 요청에 해당하는
HTML 파일을 반환해주는것 뿐이다. (png, jpeg...등등 가능하기는함)

그렇게때문에,
"CGI"는 웹서버가 처리하지못하는 동적페이지를 처리한다.

일반적으로, 웹서버가 클라이언트로부터 요청받는 리소스는
정적리소스이다. 정적인 리소스로 단지 서버는 해당리소스를
http프로토콜에 맞춰 응답하기만 하면된다.
하지만, 동적페이지의 경우 (로그인, 회원가입, 게시물 등록 등)
웹서버는 cgi gateway를 이용하여 외부의 프로그램과 연결하여 결과값을
서버에게 전해줄수있다.

만약, 웹페이지에서 클라이언트가 구구단출력하는 웹 페이지를 요청한다고 가정하자.
1단부터 9단까지 우리는 "1.html", "2.html"...과 같이 여러개의 구구단 페이지를 만들수있다. 하지만 이방법은 극히 비효율적이고 9단까지가 아닌 1000단까지의 기능을 요구한다면, 이러한방법은 셀수도없이 많은 html확장자(정적리소스) 파일을 만들어내야한다.

이러한문제를 해결하기위해서, URL에 QUERY_STRING단에서 파라미터들을 받아 (구구단일때, ex, num=5 : 5단 or num=999 : 999단) cgi프로그램 혹은 cgi 스크립터를 이용하여 결과값을 클라이언트에게 보낼수있다.

이렇게 cgi를 이용한방법은, 과거 정적인 리소스만을 다루던 기존의 웹서버를 변경하지않아도 외부의 cgi프로그램을 이용할수있도록하여 효율적으로 동적리소스를 처리할수있다.


CGI / HTML

  • html의 경우, URL에 요청리소스가 일반적으로 html확장자로 되어있다.
  • CGI의 경우, URL에 요청리소스가 일반적으로 cgi-bin디렉토리에 위치한 cgi확장자로 되어있다.
    (예, 터미널에 whereis php-cgi로 찾아볼수도있다)
    이로인해, 웹서버는 정적리소스/동적리소스를 구분하여 작동할수있다.
  • 더 정확하게는 config파일에서 설정을 할수있다.(다음장에 설명)

CGI의 구현

  1. GET 과 POST의 구분

GET은 HTML 또는 CGI로서 실행된다.
HTML은 그저 서버에 저장된 루트를 따라, 파일을 찾은다음
바디부분에 추가해서 응답으로서 클리언트에서 보내면된다.
하지만, 확장자가 .php같은 경우라면, CGI로서 파일을 실행한다음,
응답의 바디부분에 넣어 보내야한다.

GET과 POST의 CGI : 


GET은 request에서 바디부분이없다.
필요한 변수부분들은 uri에 포함되어 query string으로서 온다.

telnet을 통한 요청 예시)
GET /get/index.php?id=hyungyoo&age=32 HTTP/1.1
Host: localhost

여기서, id=hyungyoo, age=32가 index.php에 넣어야할 변수이다.

// index.php : 
<!DOCTYPE html>
<html>
  <head></head>
  <body>			// GET 중요!
    <p>ID: <?php echo $_GET["id"]; ?></p>
    <p>Age: <?php echo $_GET["age"]; ?></p>
  </body>
</html>

// php-cgi 와 index.php 그리고 query string이
// 자식프로세스에서 execve함수에의해서 실행되고난후 :

<!DOCTYPE html>
<html>
  <head></head>
  <body>
    <p>ID: hyungyoo</p>
    <p>Age:  32</p>
  </body>
</html>


POST는 request에서 바디부분으로 인자가 들어오며,
query string이 없다.

telnet을 통한 요청 예시)
GET /post/post.php HTTP/1.1
Host: localhost
Content-Length: 18

id=hyungyoo&age=32

여기서, id=hyungyoo, age=32가 post.php에 넣어야할 변수이다.

// post.php : 
<!DOCTYPE html>
<html>
  <head></head>
  <body>			// POST 중요!
    <p>ID: <?php echo $_POST["id"]; ?></p>
    <p>Age: <?php echo $_POST["age"]; ?></p>
  </body>
</html>

// php-cgi 와 post.php이
// 자식프로세스에서 execve함수에의해서 실행되고난후
// 부모프로세스에서 write로 자식프로세스에 바디부분을
// 넣어주어야한다, 그후의 결과값 :

<!DOCTYPE html>
<html>
  <head></head>
  <body>
    <p>ID: hyungyoo</p>
    <p>Age:  32</p>
  </body>
</html>
  1. GET / POST 파이프 갯수
    GET은 execve함수를 실행할때, 마지막인자로들어가는
    환경변수안에 query string이 있다.
    그렇기때문에, "자식프로세스"의 결과값을 "부모프로세스" 가 읽어오기만 하면되므로,
    파이프는 read하기위한 파이프 하나만 있으면된다.

    하지만, POST는 "자식프로세스"가 실행된뒤에,
    "부모프로세스"에서 "자식프로세스"로 바디로들어온
    인자들을 write해줘야하는 파이프가 하나더 필요하다.
    그러므로 두개의 파이프를 만들어야한다.

  2. 파이프의 non blocking
    파이프는 non blocking을 해줘야한다.

    )
    fcntl(pipeEx_, F_SETFL, O_NONBLOCK);
    
  3. wait의 위치
    wait는 정확한 위치에 있어야한다.

    
    pid_t		childPid_;
    
    childPid_ = fork();
    
    if (childPid_ < 0) {
    	// fork error
    }
    else if (childPid_ == 0) {
    	//execve 함수실행
       executeChildProcess();
    }
    else {
    	if (currenMethod == "POST" && bodySize_ > 0) {
       		writeToChildProcess();
       }
       wait(NULL);		// 여기!
       readFromChildProcess();
    }
    
  4. 출력값
    일반적으로 출력값이 위에서 예시로 보여준
    인자가 대입된 바디부분으로 알겠지만,
    헤더부분도 몇개 붙어서출력된다.
    Content-Type등등..
    그렇기에, 다시 헤더와 바디부분으로 나눠주고
    스타트라인 (HTTP/1.1 200 ok) 과 나머지 헤더들 (date, content-Length)등등을 붙여주고 요청을 보내야한다.

  5. Content-Length
    가끔 완성단계에서 브라우저에서 응답을 받았음에도, 계속해서 대기상태, 또는 pending상태라면, content-length를 확인해보는것도 좋을것같다.
    body부분에 header부분이 있기때문에, 그냥 size() 함수로 content-length를 설정했을수도있다.
    실제 바디부분보다, content-length가 길다면,
    브라우저는 바디부분을 계속해서 기다리느라 대기상태가된다.


fastCGI

fastCGI는 CGI의 단점을 보완한다.
CGI는 클라이언트로부터 하나의 요청이 들어올때마다, 하나의 자식프로세스를 생성하여 실행한다.
즉, 서버가 여러개의 요청을 받아서 처리를 하는경우, 서버에 부하가 걸릴수있다.
이러한 문제점을 해결하기위해서, fastCGI는 하나의 프로세스로 여러 요청을 받아 실행한다.
(이미 생성된 프로세스를 재활용하는방식)

PHP-FPM

FastCGI Process Manager:
PHP가 fastCGI모드로 동작할수있도록 해준다.
apache에는 PHP모듈이 자체적으로 있지만, nginx에는 없기때문에
따로 nginx와 PHP-FPM을 연동해줘야한다.

nginx에서의 설정방법

location ~ \.php$ {
        root           /var/www/html;
        # php 파일이 존재하는 디렉터리 경로
        
        fastcgi_pass   unix:/var/run/php-fpm/php-fpm.sock;
        # fastcgi_pass : php-fpm.sock이 존재하는 경로
        
        fastcgi_index  index.php;
        # 초기 php 파일명 지정
        
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include        fastcgi_params;
}
profile
etudiant_42

0개의 댓글