[SW사관학교 정글]52일차 TIL : 프록시

김승덕·2022년 11월 9일
0

SW사관학교 정글 5기

목록 보기
92/150
post-thumbnail

Sequential Web Proxy

Sequential Web Proxy는 한번에 하나씩 요청을 처리하는 프록시이다.
프록시를 학습할때 가장 기초적인(?) 내용인것같다.
주석을 통해 열심히 설명하려고 했지만 완전하지 못하다...
시간이 되면 프록시에 대한 내용을 자세히 다루고자 한다...(정말 시간이 된다면...🤪)

#include "csapp.h"
#include <stdio.h>

#define MAX_CACHE_SIZE 1049000
#define MAX_OBJECT_SIZE 102400

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";
static const char *host_header = "Host: %s\r\n"; // 호스트 이름
static const char *connection_header = "Connection: close\r\n"; // connection 헤더
static const char *proxy_connection_header = "Proxy-Connection: close\r\n";

static const char *user_agent_key = "User_Agent";
static const char *host_key = "Host";
static const char *connection_key = "Connection";
static const char *proxy_connection_key = "Proxy_Connection";

void doit(int fd);
void make_header(char *header, char *hostname, char *path, rio_t *rio);
int parse_uri(char *uri, char *hostname, char *port, char *filename);

/*
main(argc, argv)
tiny 함수를 실행할떼 ./tiny 8000 이런식으로 실행한다.
이때 단어의 수가 argc에 들어간다. 여기서는 argc는 2이다.
argv는 배열이고 위의 예시를 통해 알아보면 
argv[0] = ./tiny argv[1] = 8000 이렇게 된다.
*/
int main(int argc, char **argv) {
  
  int listenfd, connfd;
  char hostname[MAXLINE], port[MAXLINE];
  socklen_t clientlen;

  struct sockaddr_storage clientaddr;

  /* Check command line args */
  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);  // 연결 요청 접수
    Getnameinfo((SA *)&clientaddr, clientlen, hostname, MAXLINE, port, MAXLINE, 0);
    printf("Accepted connection from (%s, %s)\n", hostname, port);
    doit(connfd);   // 트랜잭션 수행
    Close(connfd);  // 자신 쪽의 연결 끝을 닫음
  }
  print("%s", user_agent_hdr);
  return 0;
}

/*
 doit(fd)
 한 개의 HTTP 트랜잭션을 처리한다.
 클라이언트의 요청 라인을 확인해서 정적, 동적 컨텐츠를 확인하고 돌려준다.
*/
void doit(int fd)
{
  int clientfd; // 클라이언트 파일 디스크립터
  char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE], server_header[MAXLINE];
  char filename[MAXLINE], hostname[MAXLINE], port[MAXLINE];
  rio_t rio, server_rio;
  size_t n;

  /* Read request line and headers */
  Rio_readinitb(&rio, fd);
  // 요청 라인을 읽고 분석
  Rio_readlineb(&rio, buf, MAXLINE); 
  printf("Request headers:\n");
  printf("%s", buf);
  sscanf(buf, "%s %s %s", method, uri, version);

  parse_uri(uri, hostname, port, filename);

  make_header(server_header, hostname, filename, &rio);

  clientfd = Open_clientfd(hostname, port); // 서버와 프록시랑 연결해주는 식별자(서버의 ip와 포트를 인자로 넣음)
  Rio_readinitb(&server_rio, clientfd); // 공유하는 버퍼 주소 초기 설정(공유하는 공간 만듦)
  Rio_writen(clientfd, server_header, strlen(server_header)); // 버퍼에 있는 내용을 클라이언트 식별자에 입력

  while((n = Rio_readlineb(&server_rio, buf, MAXLINE))!=0)
  {
    printf("%s\n", buf);
    Rio_writen(fd, buf, n);
  }
  Close(clientfd); 
}

/*
 make_header : 필요한 header들을 만들고 묶음
*/
void make_header(char *header, char *hostname, char *path, rio_t *rio)
{
  char buf[MAXLINE], r_host_header[MAXLINE], request_header[MAXLINE], other_header[MAXLINE];
  sprintf(request_header, "GET %s HTTP/1.0\r\n", path);
  while(Rio_readlineb(rio, buf, MAXLINE) > 0){
    if(strcmp(buf, "\r\n")==0){
      break;
    }
    if(!strncasecmp(buf, host_key, strlen(host_key))){
      strcpy(r_host_header, buf); // rio_buf를 host_key에 저장
      continue;
    }
    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))){
      strcat(other_header, buf);
    }
  }
  if(strlen(r_host_header) == 0)
  {
    sprintf(r_host_header, host_header, hostname);
  }

  sprintf(header, "%s%s%s%s%s%s%s", 
          request_header, r_host_header, connection_header,
          proxy_connection_header, user_agent_hdr, other_header, "\r\n");
  return;
}

/*
parse_uri
uri를 파싱해서 나누어서 처리해줌
*/
int parse_uri(char *uri, char *hostname, char *port, char *filename)
{
  char *p;
  char arg1[MAXLINE], arg2[MAXLINE];

  if(p = strchr(uri, '/'))
  {
    sscanf(p + 2, "%s", arg1);
  }
  else
  {
    strcpy(arg1, uri);
  }

  if (p = strchr(arg1, ':')){     
    *p = '\0';
    sscanf(arg1, "%s", hostname);       
    sscanf(p+1, "%s", arg2);            

    p = strchr(arg2, '/');
    *p = '\0';
    sscanf(arg2, "%s", port);           
    *p = '/';
    sscanf(p, "%s", filename);        

  }
  else{                           
    p = strchr(arg1, '/');
    *p = '\0';
    sscanf(arg1, "%s", hostname);
    *p = '/';
    sscanf(p, "%s", filename);

  }
  return;
}

🙋‍♂️ 오늘의 하루는...

이제 프록시가 끝나는 날이다.
매주 끝날때마다 느끼지만 학습하고나서 뭔가 많이 남아있지가 않은거같다.
마음이 조급해지고 집중은 잘 안된다.
다시 한번 마음을 다 잡고 무엇에 집중해야할지를 생각해보자.

profile
오히려 좋아 😎

0개의 댓글