본문 바로가기
C

[Tiny web server] serve dynamic content

by 파피요트 2023. 3. 11.

cgi-program을 실행하고 클라이언트측에 출력해보자. 


이론 간단정리

cgi-program이 필요한 인자를 참조하는 과정

클라이언트에서 서버로 URI를 통해 인자가 전달된다.

서버는 프로그램을 실행할 자식 프로세스를 생성한다. 

자식 프로세스는 CGI환경변수에 인자를 설정하고 프로그램을 실행한다.

프로그램은 런타임에 이를 참조한다.

 

자식 프로세스의 context에서 호출되는 주요함수

setenv : CGI 환경변수 QUERY_STRING을 초기화

dup2: 표준 출력을 클라이언트와 연결된 식별자로 redirect

execve : 프로그램을 로드하고 실행

getenv : 환경변수 값을 참조


코드 & 확인

adder.c(cgi-program)

#include "csapp.h"

int main(void) 
{
  printf("adder.c 실행\n");
  char *buf, *p;
  char arg1[MAXLINE],arg2[MAXLINE], content[MAXLINE];
  int n1=0, n2=0;

  if((buf = getenv("QUERY_STRING"))!= NULL){
    p = strchr(buf,'&');
    *p = '\0';
    strcpy(arg1, buf);
    strcpy(arg2, p+1);
    n1 = atoi(arg1);
    n2 = atoi(arg2);
  }

  /* Make the response body */
  sprintf(content, "The answer is: %d + %d = %d\r\n<p>",n1, n2, n1 + n2);

  /* Generate the HTTP response */
  printf("Connection: close\r\n");
  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);
}
/* $end adder */

serve_dynamic( )

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) { //자식 프로세스 fork.
    setenv("QUERY_STRING", cgiargs, 1);  // 자식 프로세스는 QUERY_STRING 환경변수를 받은 인자로 초기화
    printf("cgi-program호출전\n");
    Dup2(fd, STDOUT_FILENO); // 자식 프로세스는 표준 출력을 연결 파일 식별자로 redirect.
    printf("dup2호출후\n");
    Execve(filename, emptylist, environ); // 자식 프로세스는 cgi 프로그램을 로드하고 실행. 
    //everything that the CGI program writes to standard output goes directly to the client process
  }
  Wait(NULL); 
 
}

브라우저 출력확인.


고민한 부분.

if(Fork()==0)의 의미

자식 프로세스는 부모 프로세스의 복사본으로 생성될 때 부모 프로세스로부터 fork( )의 리턴값을 0으로 상속받는다.

(부모 프로세스의 fork( )리턴값은 자식 프로세스의 pid ) 

따라서 다음 코드는 자식 프로세스만 실행한다.

  if (Fork() == 0) { 
    setenv("QUERY_STRING", cgiargs, 1);  
    printf("cgi-program호출전\n");
    Dup2(fd, STDOUT_FILENO); 
    Execve(filename, emptylist, environ); 
    printf("execve종료\n");
  }
 

context switching

fork를 한 이후 부모가 wait 함수를 호출해야 자식 프로세스의 context가 진행된다.

만약 wait 함수를 호출하지 않는다면 부모 프로세스의 context가 계속 진행되어 연결이 끊어지고 새로운 연결을 생성한다.

새로운 연결의 요청 라인을 읽을 수 없기 때문에 그제야 자식 프로세스의 context가 진행된다.

기존의 연결이 끊어졌으므로 cgi-program에서 출력된 결과는 클라이언트 측으로 복사되지 않을 것 같았는데 

예상과는 다르게 브라우저에 결과가 출력됨을 확인할 수 있다. 이 부분이 의문이다.

 

 

 

 

 

 

'C' 카테고리의 다른 글

[Tiny web server] serve static content  (0) 2023.03.09
csapp 연습문제 11.2  (0) 2023.03.01