Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- Vue.js
- 스택
- Flutter
- 시스템콜
- Java
- 코드트리
- 4기
- 크래프톤 정글
- 오블완
- 크래프톤정글
- 큐
- CSS
- 자바스크립트
- corou
- 나만무
- HTML
- 백준
- TiL
- 사이드프로젝트
- JavaScript
- 리액트
- defee
- userprog
- 자바
- 핀토스
- 소켓
- 티스토리챌린지
- pintos
- 모션비트
- 알고리즘
Archives
- Today
- Total
미새문지
크래프톤 정글 week06, day52 - Tiny Server Code, 잔디심기 본문
728x90
Tiny Server Code
tiny.c
더보기
#include "csapp.h"
void doit(int fd);
void read_requesthdrs(rio_t *rp);
int parse_uri(char *uri, char *filename, char *cgiargs);
void serve_static(int fd, char *filename, int filesize, char *method);
void get_filetype(char *filename, char *filetype);
void serve_dynamic(int fd, char *filename, char *cgiargs, char *method);
void clienterror(int fd, char *cause, char *errnum, char *shortmsg,
char *longmsg);
// 웹 서버의 주된 동작을 수행하는 Tiny 서버의 메인 함수
int main(int argc, char **argv) {
// 연결 요청을 듣는 listen함수 소켓, 연결을 관리하는 connect함수 소켓
int listenfd, connfd;
// 클라이언트의 호스트 이름과 포트번호를 저장
char hostname[MAXLINE], port[MAXLINE];
// 클라이언트 소켓의 주소 길이와, 클라이언트의 소켓 주소 정보를 저장할 구조체
socklen_t clientlen;
struct sockaddr_storage clientaddr;
/* Check command line args */
// 인자가 2개가 아니라면
if (argc != 2) {
// 사용방법을 출력하고 종료
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(1);
}
// listen 소켓을 생성하고 두 번째 인자인 포트 번호에 바인드하여 연결 요청을 받기 시작한다.
listenfd = Open_listenfd(argv[1]);
// 클라이언트 요청을 계속 받을 수 있게 무한 반복
while (1) {
// 소켓의 주소 길이를 저장
clientlen = sizeof(clientaddr);
// 클라이언트의 연결 요청을 수락하고 클라이언트와의 통신을 위한 새 소켓을 생성
connfd = Accept(listenfd, (SA *)&clientaddr,&clientlen); // line:netp:tiny:accept
// 클라이언트의 소켓 주소 정보를 이용해 호스트이름과 포트번호를 가져온다.
Getnameinfo((SA *)&clientaddr, clientlen, hostname, MAXLINE, port, MAXLINE,0);
// 호스트이름과 포트번호를 가져와 성공적으로 연결이 됐음을 출력
printf("Accepted connection from (%s, %s)\n", hostname, port);
// csapp 11.6문제 에코 구현
// 요청한 데이터를 그대로 되돌려주는 에코 함수
// echo(connfd);
// 클라이언트 요청을 처리하는 doit함수를 호출
doit(connfd); // line:netp:tiny:doit
// 클라이언트 연결을 종료
Close(connfd); // line:netp:tiny:close
}
}
// csapp 11.6문제 에코 구현
// 클라이언트에서 데이터를 받아 그대로 되돌려주는 함수
void echo(int connfd) {
// 읽은 바이트 수를 저장할 변수, 읽은 데이터를 저장할 버퍼, 버퍼링된 I/O연산
size_t n;
char buf[MAXLINE];
rio_t rio;
// rio 구조체를 초기화하고, 클라이언트와 연결한 소켓 파일 디스크립터와 연결한다.
Rio_readinitb(&rio, connfd);
// 클라이언트에서 한 줄을 읽어 buf에 저장하고, 읽은 바이트 수를 n에 저장해 클라이언트에서 데이터를 그만 보낼 때까지
while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0) {
// 서버가 몇 바이트를 받았는지 출력
printf("server received %d bytes\n", (int)n);
// 클라이언트에게 받은 데이터를 다시 돌려준다.
Rio_writen(connfd, buf, n);
}
}
// 클라이언트의 HTTP 요청을 처리하는 메인 함수
// 클라이언트와 연결된 파일 디스크립터 fd를 인자로 받는다.
void doit(int fd){
// is_static:정적 파일의 여부, sbuf:파일정보를 저장
int is_static;
struct stat sbuf;
// HTTP요청라인을 저장, HTTP메소드를 저장, 요청 URI를 저장, HTTP버전을 저장
char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE];
// 파일이름 저장, CGI인자를 저장, 클라이언트와 통신을 위한 rio 구조체 선언
char filename[MAXLINE], cgiargs[MAXLINE];
rio_t rio;
// 클라이언트와 연결된 파일 디스크립터를 사용해 rio 구조체를 초기화한다.
Rio_readinitb(&rio,fd);
// 클라이언트로부터 요청 라인을 읽지 못할 때 함수 종료
if(!Rio_readlineb(&rio,buf,MAXLINE))
return;
// 요청 헤더를 출력한다.
printf("Request headers:\n");
printf("%s",buf);
// 요청라인에서 HTTP 메소드, 요청 uri, HTTP 버전을 가져와서 각 변수에 저장한다.
sscanf(buf, "%s %s %s",method,uri,version);
// if(strcasecmp(method,"GET")){
// csapp 11.11문제 HEAD 메소드
// HTTP 메소드가 GET이나 HEAD가 아닐 경우 501 에러 메세지를 클라이언트에게 전송하고 종료
if(!(strcasecmp(method, "GET") == 0 || strcasecmp(method, "HEAD") == 0)) {
clienterror(fd,method,"501","Not Implemented","Tiny does not implement this method");
return;
}
// 클라이언트로부터 HTTP 요청 헤더를 읽는다.
read_requesthdrs(&rio);
// uri를 분석해 파일이름과 CGI 인자를 추출해서 정적인지 동적인지 체크
is_static = parse_uri(uri,filename,cgiargs);
// 해당 파일의 상태 정보를 sbuf에 넣고, 파일이 존재하지 않으면 에러 메세지를 전송하고 종료
// stat 함수는 실패하면 -1을 성공하면 0을 반환하기 때문에 0보다 작으면 실패 했다는 뜻
if(stat(filename,&sbuf) < 0){
clienterror(fd,filename,"404","Not found","Tiny couldn't find this file");
return;
}
// 요청이 정적 파일을 대상으로 할 때
if(is_static){
// 파일 권한을 나타내는 POSIX 표준에서 정의한 상수
// 파일이 일반 파일이 아니거나 읽기 권한이 없을 경우
if(!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)){
// 403 에러를 전송하고 종료
clienterror(fd,filename,"403","Forbidden","Tiny couldn't read the file");
return;
}
// 파일이름, 파일크기, HTTP 메소드를 인자로 받아 정적 파일을 처리하는 함수를 실행
serve_static(fd,filename,sbuf.st_size, method);
// 만약 요청이 동작 파일을 대상으로 할 때
}else{
// 파일이 일반 파일이 아니거나 읽기 권한이 없을 경우
if(!(S_ISREG(sbuf.st_mode)) || !(S_IXUSR & sbuf.st_mode)){
// 403 에러를 전송하고 종료
clienterror(fd,filename,"403","Forbidden", "Tiny couldn't run the CGI program");
return;
}
// 파일이름, 파일크기, HTTP 메소드를 인자로 받아 동적 파일을 처리하는 함수를 실행
serve_dynamic(fd,filename,cgiargs, method);
}
}
// 클라이언트로부터 HTTP 요청 헤더를 읽어들이는 역할을 하는 함수
// rp는 클라이언트에서 HTTP 요청을 읽는 포인터변수
void read_requesthdrs(rio_t *rp){
// 문자열을 저장할 buf배열을 선언(MAXLINE은 한 줄의 문자열을 받을만한 크기)
char buf[MAXLINE];
// 클라이언트에서 한 줄을 읽어와 buf에 저장하고 출력
// 처음 읽어오는 문자열은 HTTP 요청의 첫 줄인 요청 라인이다.
Rio_readlineb(rp,buf,MAXLINE);
printf("%s", buf);
// 요청 라인 다음에 오는 헤더를 읽는데, HTTP 요청 헤더는 '\r\n'으로 끝나기 때문에
// buf에 저장된 문자열이 '\r\n'이 아닐 때까지 반복한다.
while(strcmp(buf,"\r\n")){
// 클라이언트에서 다음 줄을 읽어서 buf에 저장하고 출력
Rio_readlineb(rp,buf,MAXLINE);
printf("%s",buf);
}
return;
}
// 주어진 URI를 분석하여 파일명과 CGI 인자 등을 추출하는 함수
int parse_uri(char *uri, char *filename, char *cgiargs){
// 포인터 변수 선언
char *ptr;
// 가져온 URI에 cgi-bin 문자열이 포함되어 있지 않을 경우, 즉 정적 컨텐츠를 요청하는 경우
// cgi는 동적에서 컨텐츠를 생성하기 위한 기술이기 때문에 없으면 정적 컨텐츠를 요청할 수 밖에 없다.
if(!strstr(uri,"cgi-bin")){
// 정적에는 CGI인자가 필요 없기 때문에, cgiargs를 빈 문자열로 초기화한다.
strcpy(cgiargs,"");
// 현재 디렉토리에 있기 위해 filename을 .으로 초기화한다.
strcpy(filename,".");
// uri를 파일이름에 추가하여 요청된 파일의 경로를 완성한다.
strcat(filename,uri);
// uri의 마지막 문자는 null문자인 '\0'로 끝나므로 이것을 제외한(-1) 마지막 문자가 /인 경우
// uri가 /로 끝나는 경우에, 즉 디렉토리를 가리키는 경우에
if(uri[strlen(uri)-1] == '/')
// home.html을 추가한다(해당 페이지로 이동하기 위함)
strcat(filename,"home.html");
// 정적 컨텐츠를 요청하는 경우라는 것을 나타내기 위해 1을 반환
return 1;
// 문자열이 포함되어 있는 경우
}else{
// 포인터에 uri에서 ?문자를 찾아 저장한다.
// ?뒤에 오는 문자열은 CGI인자이기 때문
ptr = index(uri, '?');
// 만약 ?문자가 있는 경우
if(ptr){
// ?뒤에 오는 CGI 인자를 cgiargs에 복사한다.
strcpy(cgiargs,ptr+1);
// 포인터에 저장된 ?문자를 null('\0') 문자로 바꿔줌으로써 ?전까지의 문자열로 잘리게 된다.
*ptr = '\0';
// ?문자가 없는 경우
}else{
// cgiargs를 빈 문자열로 초기화한다.
strcpy(cgiargs,"");
}
// 정적 컨텐츠를 요청하는 경우와 동일하게 파일이름을 설정해준다.
strcpy(filename,".");
strcat(filename,uri);
// 동적 컨텐츠를 요청하는 경우라는 것을 나타내기 위해 0을 반환
return 0;
}
}
// 파일타입을 확인해 해당 파일의 MIME 타입을 설정하는 함수
// 파일 이름과 파일 타입을 인자로 받는다.
void get_filetype(char *filename,char *filetype){
// 파일이름이 .html 확장자가 있으면
if(strstr(filename,".html")){
// 해당 확장자를 text/html로 설정한다.
strcpy(filetype,"text/html");
// 파일이름이 .gif 확장자가 있으면
}else if(strstr(filename,".gif")){
// 해당 확장자를 image/gif로 설정한다.
strcpy(filetype,"image/gif");
// 파일이름이 .png 확장자가 있으면
}else if(strstr(filename,".png")){
// 해당 확장자를 image/png로 설정한다.
strcpy(filetype,"image/png");
// 파일이름이 .jpg 확장자가 있으면
}else if(strstr(filename, ".jpg")){
// 해당 확장자를 image/jpeg로 설정한다.
strcpy(filetype,"image/jpeg");
}else{
// 없을 때의 기본 확장자를 text/plain로 설정한다.
strcpy(filetype,"text/plain");
}
}
// 정적 컨텐츠를 제공하는 static 함수
// fd:연결관리파일디스크립터, filename:클라이언트가 요청한 리소스 파일이름, filesize:리소스파일의크기
// method:클라이언트 HTTP요청메소드 - 11.11문제를 위해 추가
void serve_static(int fd, char *filename, int filesize, char *method){
// 파일 디스크립터
int srcfd;
// 파일의 내용을 가리키는 포인터, 파일 타입 배열, 버퍼를 선언
// char *srcp, filetype[MAXLINE], buf[MAXLINE];
// csapp 11.11문제 HEAD 메소드
// buf의 [MAXLINE]은 한 줄의 텍스트를 저장하는데의 충반한 크기이고 [MAXBUF]는 더 큰 크기의 데이터 저장에 사용
char *srcp, filetype[MAXLINE], buf[MAXBUF];
// 파일 이름과 파일 타입을 확인한다.
get_filetype(filename,filetype);
// HTTP 응답의 첫 줄, 작성 응답 상태 코드
sprintf(buf,"HTTP/1.1 200 OK\r\n");
// 작성한 HTTP 응답의 첫 줄을 클라이언트에 전송
Rio_writen(fd,buf,strlen(buf));
// tiny server임을 나타내는 코드
sprintf(buf,"Server: Tiny Web Server\r\n");
// 작성한 HTTP 헤더를 클라이언트에 전송
Rio_writen(fd,buf,strlen(buf));
// 본문의 길이를 나타내는 filesize 출력
sprintf(buf,"Content-length: %d\r\n", filesize);
// 작성한 Content-length 헤더를 클라이언트에 전송
Rio_writen(fd,buf,strlen(buf));
// 본문의 미디어 타입을 나타내는 filetype 출력
sprintf(buf,"Content-type: %s\r\n\r\n",filetype);
// 작성한 Content-type 헤더를 클라이언트에 전송
Rio_writen(fd,buf,strlen(buf));
// csapp 11.11문제 HEAD 메소드
// buf에 저장된 HTTP 응답 헤더 문자열을 출력
printf("Response headers:\n");
printf("%s", buf);
// strcasecmp(두 문자열이 같은지 비교하는 함수)로 요청 메소드가 HEAD인지 확인해 맞으면 종료
if(strcasecmp(method, "HEAD") == 0)
return; // code end
// 요청받은 파일을 읽기전용으로 연다.
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);
// csapp 11.9문제 malloc 구현
// srcfd = Open(filename, O_RDONLY, 0);
// 메모리 매핑 대신 malloc을 사용해 메모리를 할당시킨다.
// srcp = (char *)malloc(sizeof(filesize));
// 파일의 내용을 읽어와 할당한 메모리에 저장한다.
// Rio_readn(srcfd, srcp, filesize);
// Close(srcfd);
// Rio_writen(fd, srcp, filesize);
// free(srcp);
}
// 동적 컨텐츠를 제공하는 dynamic 함수
// fd:연결관리파일디스크립터, filename:클라이언트가 요청한 리소스 파일이름, cgiargs:cgi프로그램에 전달될 인자
// method:클라이언트 HTTP요청메소드 - 11.11문제를 위해 추가
void serve_dynamic(int fd, char *filename, char *cgiargs, char *method){
// 버퍼와 빈 문자열 리스트 선언
char buf[MAXLINE], *emptylist[] = {NULL};
// HTTP 응답 코드 첫 줄을 출력
sprintf(buf,"HTTP/1.1 200 OK\r\n");
// 작성한 HTTP 응답 코드 첫 줄을 클라이언트에 전송
Rio_writen(fd,buf,strlen(buf));
// 이 웹서버가 tiny server임을 알려주는 코드를 출력
sprintf(buf,"Server: Tiny Web Server\r\n");
// 작성한 HTTP 헤더를 클라이언트에 전송
Rio_writen(fd,buf,strlen(buf));
// 자식 프로세스를 생성하여 현재 프로세스가 자식 프로세스라면
// 자식을 생성하면 fork()는 부모에게 자식의 PID를 자식에게 0을 반환한다.
if(Fork() == 0){
// QUERY_STRING이라는 환경변수를 설정하고 method의 변수값으로 지정
setenv("QUERY_STRING", cgiargs, 1);
// csapp 11.11문제 HEAD 메소드
// 클라이언트의 HTTP 요청 메소드를 전달하는데 사용되는 REQUEST_METHOD를 설정
setenv("REQUEST_METHOD", method, 1);
// 클라이언트와 연결된 소켓의 파일 디스크립터를 복제하고, 그 값을 표준 출력으로 설정
// 이 함수 호출 후에는 프로세스가 표준 출력으로 보내는 모든 데이터가 fd를 통해 전송
Dup2(fd,STDOUT_FILENO);
// filename에 지정된 파일을 실행한다.
// emptylist는 프로그램에 전달되는 파라미터를 지정하고, environ은 프로그램의 환경 변수를 지정한다.
Execve(filename,emptylist,environ);
}
Wait(NULL);
}
void clienterror(int fd, char *cause, char *errnum, char *shortmsg, char *longmsg){
char buf[MAXLINE];
sprintf(buf, "HTTP/1.1 %s %s\r\n", errnum, shortmsg);
Rio_writen(fd, buf, strlen(buf));
sprintf(buf, "content-type: text/html\r\n\r\n");
Rio_writen(fd, buf, strlen(buf));
sprintf(buf, "<html><title>Tiny Error</title>");
Rio_writen(fd, buf, strlen(buf));
sprintf(buf, "<body bgcolor=""ffffff"">\r\n");
Rio_writen(fd, buf, strlen(buf));
sprintf(buf, "%s: %s\r\n",errnum,shortmsg);
Rio_writen(fd, buf, strlen(buf));
sprintf(buf,"<p>%s: %s\r\n",longmsg,cause);
Rio_writen(fd, buf, strlen(buf));
sprintf(buf,"<hr><em>The Tiny Web server</em>\r\n");
Rio_writen(fd, buf, strlen(buf));
}
adder.c
더보기
/*
* adder.c - a minimal CGI program that adds two numbers together
*/
/* $begin adder */
#include "csapp.h"
int main(void) {
char *buf, *p, *method;
char arg1[MAXLINE], arg2[MAXLINE], content[MAXLINE];
int n1=0, n2=0;
/* Extract the two arguments */
if ((buf = getenv("QUERY_STRING")) != NULL) {
p = strchr(buf, '&');
*p = '\0';
sscanf(buf, "n1=%d", &n1 );
sscanf(p+1, "n2=%d", &n2 );
// strcpy(arg1, buf);
// strcpy(arg2, p+1);
// A = strchr(buf, 'A');
// B = strchr(buf, 'B');
// *A = '\0';
// *B = '\0';
// strcpy(arg1, A+2);
// strcpy(arg2, B+2);
// n1 = atoi(arg1);
// n2 = atoi(arg2);
}
// 11.11 add code
method = getenv("REQUEST_METHOD");
/* 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);
/* 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");
// 11.11 add code if
if(strcasecmp(method, "HEAD") != 0)
printf("%s", content);
fflush(stdout);
exit(0);
}
/* $end adder */
proxy.c(Sequential 만 작성)
더보기
#include <stdio.h>
#include "csapp.h"
/* Recommended max cache and object sizes */
#define MAX_CACHE_SIZE 1049000
#define MAX_OBJECT_SIZE 102400
// 웹 브라우저의 신원정보를 나타내는 문자열
// Mozilla Firefox 버전 10.0.3을 사용하는 Linux 시스템
static const char *user_agent_hdr = "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:10.0.3) Gecko/20120305 Firefox/10.0.3\r\n";
// HTTP의 Connection 헤더를 설정하며, 이 연결은 요청 후 종료된다.
static const char *conn_hdr = "Connection: close\r\n";
// Proxy-Connection 헤더를 설정하며, 이 연결은 요청 후 종료
static const char *prox_hdr = "Proxy-Connection: close\r\n";
// Host 헤더를 설정하며, %s는 호스트 이름으로 치환될 위치를 나타낸다.
static const char *host_hdr_format = "Host: %s\r\n";
// HTTP GET 요청을 설정하는데 사용하며, %s는 호스트 이름으로 치환될 위치를 나타낸다.
static const char *requestlint_hdr_format = "GET %s HTTP/1.0\r\n";
// HTTP 헤더의 끝을 나타낸다.
static const char *endof_hdr = "\r\n";
// Connection, User-Agent, Proxy-Connection, Host 헤더의 키를 나타낸다.
// 이 키를 사용해 HTTP 헤더의 값을 설정하거나 읽어올 수 있다.
static const char *connection_key = "Connection";
static const char *user_agent_key= "User-Agent";
static const char *proxy_connection_key = "Proxy-Connection";
static const char *host_key = "Host";
void doit(int connfd);
void parse_uri(char *uri,char *hostname,char *path,int *port);
void build_http_header(char *http_header,char *hostname,char *path,int port,rio_t *client_rio);
int connect_endServer(char *hostname,int port,char *http_header);
// 프록시 서버 메인 함수
int main(int argc,char **argv)
{
int listenfd,connfd;
socklen_t clientlen;
char hostname[MAXLINE],port[MAXLINE];
struct sockaddr_storage clientaddr;/*generic sockaddr struct which is 28 Bytes.The same use as sockaddr*/
if(argc != 2){
fprintf(stderr,"usage :%s <port> \n",argv[0]);
exit(1);
}
listenfd = Open_listenfd(argv[1]);
while(1){
clientlen = sizeof(clientaddr);
connfd = Accept(listenfd,(SA *)&clientaddr,&clientlen);
/*print accepted message*/
Getnameinfo((SA*)&clientaddr,clientlen,hostname,MAXLINE,port,MAXLINE,0);
printf("Accepted connection from (%s %s).\n",hostname,port);
/*sequential handle the client transaction*/
doit(connfd);
Close(connfd);
}
return 0;
}
// 클라이언트의 HTTP 함수를 처리하는 함수, 연결 파일 디스크립터를 인자로 받는다.
void doit(int connfd)
{
// 끝 서버의 파일 디스크립터를 저장할 변수 선언
int end_serverfd;
// MAXLINE의 크기를 가진 버퍼, 메소드, URI, 버전을 저장할 변수를 선언
char buf[MAXLINE],method[MAXLINE],uri[MAXLINE],version[MAXLINE];
// 끝 서버에 보낼 HTTP 헤더를 저장할 문자열 배열을 선언
char endserver_http_header [MAXLINE];
/*store the request line arguments*/
// 호스트이름과 경로를 저장할 변수를 선언, 포트번호 변수 선언
char hostname[MAXLINE],path[MAXLINE];
int port;
// 클라이언트와 끝 서버의 rio구조체 선언
rio_t rio,server_rio;
// 클라이언트의 연결 디스크립터에 대한 rio를 초기화
Rio_readinitb(&rio,connfd);
// 클라이언트로부터 한 줄을 읽어 buf에 저장
Rio_readlineb(&rio,buf,MAXLINE);
// 읽은 줄에서 메소드, uri, 버전을 추출하여 저장한다.
sscanf(buf,"%s %s %s",method,uri,version);
// strcasecmp함수는 두 문자열을 비교하는 함수, 0을 반환하면 같다는 의미
// 만약 메소드가 GET이 아니라면
if(strcasecmp(method,"GET")){
// 프록시는 메소드를 구현하지 않는다는 메세지를 출력
printf("Proxy does not implement the method");
return;
}
// uri를 파싱해서 호스트 이름, 경로 포트번호를 추출한다.
parse_uri(uri,hostname,path,&port);
// HTTP 헤더를 작성한다.
build_http_header(endserver_http_header,hostname,path,port,&rio);
// 끝 서버에 연결하고, 이 연결의 파일 디스크립터를 end_serverfd에 저장한다.
end_serverfd = connect_endServer(hostname,port,endserver_http_header);
// 만약 연결이 실패했으면
if(end_serverfd<0){
// 연결 실패 메세지를 출력
printf("connection failed\n");
return;
}
// 끝 서버의 파일 디스크립터 end_serverfd에 대한 server_rio를 초기화
Rio_readinitb(&server_rio,end_serverfd);
// 작성한 HTTP 헤더를 끝 서버에 전송한다.
Rio_writen(end_serverfd,endserver_http_header,strlen(endserver_http_header));
// 읽은 바이트 수를 저장할 변수 선언
size_t n;
// 끝 서버로부터 한 줄씩 읽어서 buf에 저장하고 읽을 줄이 없을 때 까지 반복
while((n=Rio_readlineb(&server_rio,buf,MAXLINE))!=0)
{
// 프록시가 몇 바이트를 받았는지 출력
printf("proxy received %d bytes,then send\n",n);
// 읽은 내용을 클라이언트에 전송한다.
Rio_writen(connfd,buf,n);
}
Close(end_serverfd);
}
// HTTP 헤더를 구성하는 함수
// HTTP헤더, 호스트이름, 경로, 포트, 클라이언트의 rio를 인자로 받는다.
void build_http_header(char *http_header,char *hostname,char *path,int port,rio_t *client_rio)
{
// 크기가 MAXLINE인 버퍼, 요청 헤더, 다른 헤더, 호스트 헤더를 선언
char buf[MAXLINE],request_hdr[MAXLINE],other_hdr[MAXLINE],host_hdr[MAXLINE];
// 요청 라인
// sprintf 함수는 문자열을 형식에 맞게 생성하고, 이를 변수에 저장하는 역할
// requestlint_hdr_format 문자열 내의 %s를 path로 치환한 후 결과 문자열을 요청헤더에 저장
sprintf(request_hdr,requestlint_hdr_format,path);
// 클라이언트의 rio로부터 한 줄씩 buf에 넣으며 읽을 라인이 없을 때까지 반복
while(Rio_readlineb(client_rio,buf,MAXLINE)>0)
{
// 만약 읽은 라인이 헤더의 끝을 나타내는 문자열이면 while문 탈출
// EOF = End Of File이며, 파일의 끝을 표현하기 위해 정의한 -1값을 가지고 있는 상수
if(strcmp(buf,endof_hdr)==0) break;/*EOF*/
// 만약 읽은 라인이 Host:로 시작한다면
if(!strncasecmp(buf,host_key,strlen(host_key)))/*Host:*/
{
// buf의 내용을 호스트 헤더에 저장
strcpy(host_hdr,buf);
continue;
}
// 만약 읽은 라인이 Connection:, Proxy-Connection:, User-Agent:로 시작하지 않으면
if(strncasecmp(buf,connection_key,strlen(connection_key))
&&strncasecmp(buf,proxy_connection_key,strlen(proxy_connection_key))
&&strncasecmp(buf,user_agent_key,strlen(user_agent_key)))
{
// buf의 내용을 다른 헤더에 추가한다.
strcat(other_hdr,buf);
}
}
// 만약 호스트헤더가 비어있다면
if(strlen(host_hdr)==0)
{
//host_hdr_format 문자열 내의 %s를 hostname으로 치환한 후 결과 문자열을 호스트헤더에 저장
sprintf(host_hdr,host_hdr_format,hostname);
}
// 모든 헤더를 합쳐서 http_header를 작성한다.
sprintf(http_header,"%s%s%s%s%s%s%s",
request_hdr,
host_hdr,
conn_hdr,
prox_hdr,
user_agent_hdr,
other_hdr,
endof_hdr);
return ;
}
// inline 함수는 컴파일러가 함수를 호출하는 대신 코드에 직접 삽입하도록 지시하는 키워드
// 호스트이름과 포트를 사용해 끝 서버에 연결하고, 연결의 파일 디스크립터를 반환하는 함수
inline int connect_endServer(char *hostname,int port,char *http_header){
// 포트번호를 문자열로 변환해 저장하는 크기가 100인 포트배열 선언
char portStr[100];
// port의 정수값을 문자열로 변환하고 포트번호배열에 저장
sprintf(portStr,"%d",port);
// 호스트이름과 포트번호배열을 인자로 받아 서버에 연결해서 , 파일 디스크립터를 반환하는 함수
return Open_clientfd(hostname,portStr);
}
// URI를 분석해 호스트이름, 파일경로, 포트를 추출하는 함수
void parse_uri(char *uri,char *hostname,char *path,int *port)
{
// HTTP의 기본 포트인 80으로 설정
*port = 80;
// URI에서 '//' 문자열을 찾아 시작위치를 pos에 저장
char* pos = strstr(uri,"//");
// 만약 '//'이 있다면 pos를 다음 문자로 이동, 없으면 uri을 가리킨다.
pos = pos!=NULL? pos+2:uri;
// pos에서 ':'문자열을 찾아 pos2에 저장, 이는 포트 번호를 지정하는데 사용
char*pos2 = strstr(pos,":");
// 만약 ':'가 있으면
if(pos2!=NULL)
{
// ':'문자를 null로 바꾼다. 이를 통해 호스트이름과 포트번호를 분리
*pos2 = '\0';
// pos에 있는 ':'에서 시작해 문자열 끝까지 호스트이름으로 복사한다.
sscanf(pos,"%s",hostname);
// pos2에 있는 ':'다음에서 시작해 숫자를 port로 나머지 문자열을 path로 복사
sscanf(pos2+1,"%d%s",port,path);
}
// ':'문자를 찾기 못했다면
else
{
// pos에서 '/'문자열을 찾아 pos2에 넣는다
pos2 = strstr(pos,"/");
// pos2 즉, '/'가 존재한다면
if(pos2!=NULL)
{
// pos2의 '/'를 null로 바꾼다.
*pos2 = '\0';
// pos에서 시작해 문자열 끝까지 호스트이름으로 복사한다.
sscanf(pos,"%s",hostname);
// pos2에 '/'를 넣어서
*pos2 = '/';
// '/'문자부터 시작해 나머지 문자열을 path로 복사한다.
sscanf(pos2,"%s",path);
}
// '/'가 존재하지 않으면
else
{
// pos에서 시작해 나머지 문자열을 호스트이름으로 복사한다.
sscanf(pos,"%s",hostname);
}
}
return;
}
오늘의 잔디심기
백준
|
4101
|
자바스크립트
|
브론즈5
|
크냐? |
const fs = require("fs");
const filePath = process.platform === "linux" ? "/dev/stdin" : "./input.txt";
let input = fs.readFileSync(filePath).toString().split("\n");
const bigger = () => {
i = 0;
while(input[i]){
const [num1, num2] = input[i].split(" ").map(Number);
if(num1 == 0 && num2 == 0) {
break;
}
if(num1 > num2) console.log("Yes");
else console.log("No");
i++;
}
}
bigger();
728x90
'크래프톤 정글 > TIL' 카테고리의 다른 글
크래프톤 정글 week07, day55 - 4BSD, Nice, Busy Waiting, 잔디심기 (3) | 2024.03.03 |
---|---|
크래프톤 정글 week07, day53 - CPU 스케줄링 알고리즘, 잔디심기 (6) | 2024.03.01 |
크래프톤 정글 week06, day51 - CGI, WebServer, MIME Type, Proxy, 잔디심기 (2) | 2024.02.28 |
크래프톤 정글 week06, day50 - Telnet, 잔디 심기 (2) | 2024.02.27 |
크래프톤 정글 week06, day49 - 망의 구성 형, 잔디 심기 (2) | 2024.02.26 |