-
[SW 정글 55일차] HEAD 메소드는 왜 쓸까?기타/SW 사관학교 정글 2021. 9. 26. 16:02
이번 웹 서버 구현 과제를 진행하면서 컴퓨터 시스템 책에 있는 과제문제에 HEAD 메소드 요청에 대한 응답을 구현하라는 것이 있었다.
이번 주 화요일(2021.09.23)에 HEAD메소드가 무엇인지 알아보고 구현을 했는데 지금 돌아보니 왜 HEAD를 쓰는지는 안알아봤다.
그래서 생각난 김에 정리해보려고 한다.
일단은 간단하게 HEAD 메소드가 무엇인지 알아보자.
HEAD 메서드는 리소스를 GET메서드로 요청했을 때 응답으로 오는 헤더부분만 요청하는 메소드이다.
HEAD 메서드에 대한 응답은 본문(body부분)을 가져선 안되며, 본문이 존재하더라도 무시해야 한다.
그러나, 응답으로 받는 헤더에는 본문 콘텐츠를 설명하는 개체 헤더는 포함할 수 있다.
개체 해더는 entity-body에 대한 메타데이터를 필드로 가지고 있는 헤더이다.
여기서, entity-body의 필드로는 Allow, Content-Encoding, Content-Language, Content-Length, Content-Location, Content-MD5, Content-Range, Content-Type, Expires, Last-Modified, extension-header 가 있다.여기서 개체 헤더에 담긴 정보들은 GET 메서드로 동일한 리소스를 요청했을 때의 본문에 대한 정보들을 담고 있다.
즉, HEAD로 요청했을 때, 본문이 비어있다고해서 비어있는 본문에 대한 정보를 담고있는 개체 헤더가 아니라는 것이다.
그러면 이러한 header부분만 요청하는 HEAD 메소드는 왜 쓰는 것일까?
HEAD 메소드를 쓰는 이유는 아래와 같다.
HEAD 메소드는 종종 caching을 사용하는 클라이언트가 가장 최근에 접속한 이후로 document가 바뀌었는지를 보기위해 사용한다.
또한, 요청에 쓰인 hypertext links의 validity(타당성), accessibility(접근성), recent modification(최근 수정사항)을 테스트하기 위해 사용되기도 한다.
아무래도 본문에 포함된 정보 모두를 얻는 것이 위와 같이 어떠한 목적으로 header부분만 원한다면 굳이 GET 요청을 통해 body부분을 포함한 큰 응답 데이터를 받는 것보다는 header부분만 가진 응답을 받는 것이 더 효율적이라고 생각한다.
이러한 이론을 얻고 직접 tiny.c의 코드를 바꾼 것은 아래와 같다.
일단은 doit()에서 request line의 method가 HEAD도 받을 수 있도록 바꿔준다.
if (!(strcasecmp(method, "GET") == 0 || strcasecmp(method, "HEAD") == 0)) { clienterror(fd, method, "501", "Not implemented", "Tiny does not implement this method"); return; }
그리고 우리는 정적인 파일을 제공해주는 serve_static()과 동적 파일을 제공해주는 serve_dynamic()이 있는데 이 부분에서 response header부분만 보낼 수 있도록 바꿔줘야한다.
먼저, serve_static()를 바꾼 것이다.
그냥 간단하게 인자로 method를 받도록 해주고 method가 GET일 때만 response body를 보낼 수 있도록 조건문을 넣어주면 된다.
void serve_static(int fd, char *filename, int filesize, char *method) { 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)); printf("Response headers:\n"); printf("%s", buf); if(strcasecmp(method, "GET") == 0) { /* Send response body to client */ srcfd = Open(filename, O_RDONLY, 0); // srcp = Mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0); // solved problem 11.9 srcp = malloc(filesize); Rio_readn(srcfd, srcp, filesize); Close(srcfd); Rio_writen(fd, srcp, filesize); // Munmap(srcp, filesize); free(srcp); } }
다음으로 serve_dynamic()를 바꾼 것이다.
여기서는 response body를 만드는 부분이 있는 것이 아닌 application program으로 이어주는 것이므로 REQUEST_METHOD라는 환경변수에 인자로 받은 method로 설정하여 application program에서 처리해주도록 해준다.
void serve_dynamic(int fd, char *filename, char *cgiargs, char *method) { 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); // method를 cgi-bin/adder.c에 넘겨주기 위해 환경변수 set setenv("REQUEST_METHOD", method, 1); Dup2(fd, STDOUT_FILENO); /* Redirect stdout to client */ Execve(filename, emptylist, environ); /* Run CGI program */ } Wait(NULL); /* Parent waits for and reaps child */ }
serve_dynamic()을 위와 같이 바꾸고 application program인 adder.c를 아래와 같이 바꿔주면 된다.
/* * adder.c - a minimal CGI program that adds two numbers together */ /* $begin adder */ #include "csapp.h" int main(void) { char *buf, *p, *arg1_p, *arg2_p, *method; char arg1[MAXLINE], arg2[MAXLINE], content[MAXLINE], val1[MAXLINE], val2[MAXLINE]; int n1=0, n2=0; /* Extract the two arguments */ if ((buf = getenv("QUERY_STRING")) != NULL) { p = strchr(buf, '&'); *p = '\0'; strcpy(arg1, buf); strcpy(arg2, p+1); // fnum=value에서 value만 추출하기 위한 작업 arg1_p = strchr(arg1, '='); *arg1_p = '\0'; strcpy(val1, arg1_p+1); // snum=value에서 value만 추출하기 위한 작업 arg2_p = strchr(arg2, '='); *arg2_p = '\0'; strcpy(val2, arg2_p+1); //문자를 정수로 전환하는 atoi함수 n1 = atoi(val1); n2 = atoi(val2); } method = getenv("REQUEST_METHOD"); /* Make the response body */ sprintf(content, "QUERY_STRING=%s", buf); 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); /* 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"); // method가 GET일 경우에만 response body보냄 if (strcasecmp(method, "GET") == 0) { printf("%s", content); } fflush(stdout); exit(0); }
[오늘의 나는 어땠을까?]
오늘은 아침 일찍 나와서 알고리즘 문제 풀이로 하루를 시작했지만 공부량을 따지면 그닥이다...
집중해서 이번 주차를 잘 마무리했어야하는데 아쉬움이 많이 남는다.
조금 더 선택과 집중을 잘 하여 얻어간 것이 많았어야했는데 목표했던 것을 못미치는 것 같다.
나태해진 것일까...
솔직히 정글을 들어와서부터 한 집중력에 비해 지금의 나는 많이 집중력이 떨어진 것이 사실이다.
어떻게 해서든 다시 집중력을 높여야한다.
이제 OS주차가 끝나고 최종 프로젝트를 마무리하면 나의 노력에 대한 결과물을 확인할 날이 온다.
절대 후회라는 단어를 연상하지 말아야 한다.
지금 이 순간에 최선을 다해야한다.
다시 0주차 때의 집중력으로 만들어 최대의 효율성으로 많은 것을 나의 것으로 만들도록 하자.
'기타 > SW 사관학교 정글' 카테고리의 다른 글
[SW 정글 56일차] Pintos 입문하면서 용어 정리하기 (0) 2021.09.28 SW 정글 7주차 회고 (0) 2021.09.27 [SW 정글 54일차] IP 5계층 (0) 2021.09.26 [SW 정글 53일차] 소켓을 구현하긴 했는데 나는 소켓을 완벽히 이해했을까 (0) 2021.09.25 [SW 정글 52일차] 오늘은 일기만.. (0) 2021.09.24