jun-wiki

View My GitHub Profile

Posts (Latest 10 updated) :
Read all


사실 어제 한말들 구라였다

구라

어찌해야할지 자세히 적힌 메뉴얼덕에 말도 안되게 노답이다 정도는 아니었다 ㅇㅇ

걍 오바좀 했다

그리고 그럴 만한 사정이 있었다



분명 프록시 구현하고 연결해서 서버 돌아가는 거 몇번이고 확인했는데

이상하게도 driver.sh로 테스트를 돌리면

타임 오-바 싹다 꺼버려 하고 그냥 바로 연결 실패하는 거였다

왜 그런지 도무지 알 수가 없어서

방법 알 때까지는 프록시 모르는척 글쓸 생각이었는데

다행히도 오늘 해결했다

기본 파일 구조에 .proxy, tiny, echo 이런식으로 폴더명이 있으니

당연히 아, 각 폴더마다 C파일 만들어 주면 빌드 되겠구나! 하지
흥, 폴더 밖에다가 proxy.c 만들어야겠군 하지는 않는다

그치만 답은 밖에다가 만드는 거였다…

어쨰선지는 모르겠지만 자꾸 과거의 잔재 비슷하게 남아 빌드하는데 방해해

깨끗이 빌드하는 법 배워 해결했지만…

또, 문제가 발생했다…

tiny서버랑proxy서버 연뒤에 정보 받아올려하면
바로 무량공처 맞고 정지당해 무한로딩에 빠져버린다는 거였다

.proxy 파일에 숨어있던 proxy.c 꺼낸덕에 driver로 테스트 가능했지만
이 무한로딩 땜에 0점 맞아와버렸다…


아무리봐도 코드상 문제가 없어보였다만…

딱 한글자 때문에 문제가 생겼던 거였다

헤더 읽고 쓸데 없는 정보 빼고 필요한거 넣고 하는 과정에서 하드코딩으로 길이 입력했는데

Rio_writen(serverfd, "Connection: close\r\n", 19);
Rio_writen(serverfd, "Proxy-Connection: close\r\n", 25);
Rio_writen(serverfd, "Accept-Encoding: identity\r\n", 29);

문제점을 알겠는가?

Rio_writen(serverfd, "Connection: close\r\n", 19);
Rio_writen(serverfd, "Proxy-Connection: close\r\n", 25);

🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽🔽
Rio_writen(serverfd, "Accept-Encoding: identity\r\n", 29);
🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼🔼

저 길이 입력하는데 27글자인데 29이라 입력한거 떄문이었다

어지러워서 그냥 숫자 하드코딩하는거 안하기로 결심했다

Rio_writen(serverfd, "Connection: close\r\n", strlen("Connection: close\r\n"));
Rio_writen(serverfd, "Proxy-Connection: close\r\n", strlen("Proxy-Connection: close\r\n"));
Rio_writen(serverfd, "Accept-Encoding: identity\r\n", strlen("Accept-Encoding: identity\r\n"));

걍 strlen으로 받아주리고 말이다

숫자 입력하는 방식의 하드코딩 그거 잠깐 편하려 했다가 개고생했다

아무튼 이거 수정한덕에 40점 받아왔다

일단 프록시 서버 구현 1단계는 훌륭히 했다

70점 만점이고 동시성 구현시 15점, 캐싱 구현시 15점이니

나머지 2개 구현하면 만점이다


이번에 구현했던건
여러 요청 왔을때 프록시가 순차적으로 받게 도와주는 역할 하는거였다

기능 추가적으로 구현 성공하면 글 쓰겠다



코드:

#include "csapp.h"

static void client_error(int fd, int status, const char *shortmsg, const char *longmsg) {
    char buf[MAXLINE], body[MAXBUF];
    snprintf(body, sizeof(body),
             "<html><title>Proxy Error</title>"
             "<body>%d %s<br>%s</body></html>", status, shortmsg, longmsg);
    snprintf(buf, sizeof(buf), "HTTP/1.0 %d %s\r\n", status, shortmsg); Rio_writen(fd, buf, strlen(buf));
    snprintf(buf, sizeof(buf), "Content-Type: text/html\r\n");           Rio_writen(fd, buf, strlen(buf));
    snprintf(buf, sizeof(buf), "Content-Length: %zu\r\n\r\n", strlen(body)); Rio_writen(fd, buf, strlen(buf));
    Rio_writen(fd, body, strlen(body));
}

static int parse_uri(const char*, char*, size_t, char*, size_t, char*, size_t);
static void forward_request_headers(int, rio_t*, const char*, const char*, const char*, const char*);
static void relay_response(int, int);


static void handle_client(int connfd) {
    rio_t rio;
    char reqline[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE];

    Rio_readinitb(&rio, connfd);
    if (!Rio_readlineb(&rio, reqline, MAXLINE)) return;  // 빈 요청 처리
    printf(">>> %s", reqline);

    // 요청라인 파싱
    if (sscanf(reqline, "%s %s %s", method, uri, version) != 3) {
        client_error(connfd, 400, "Bad Request", "Malformed request line");
        return;
    }

    char host[MAXLINE], port[MAXLINE], path[MAXLINE];
    if (parse_uri(uri, host, sizeof(host), port, sizeof(port), path, sizeof(path)) < 0) {
        client_error(connfd, 400, "Bad Request", "Only http://host[:port]/path is supported");
        return;
    }
    
    // 1단계: GET만 허용
    if (strcasecmp(method, "GET") && strcasecmp(method, "HEAD")) {
        client_error(connfd, 501, "Not Implemented", "Only GET/HEAD supported for now");
        return;
    }

    int serverfd = open_clientfd(host, port);
    if (serverfd < 0) {
        // -2: getaddrinfo 실패(호스트 이름/포트 이상)
        // -1: 그 외 소켓/연결 에러
        client_error(connfd, 502, "Bad Gateway", "Cannot connect to upstream");
        return;
    }

    // TODO: 여기서 헤더 재작성 후 serverfd로 전송 → 응답 복사
    forward_request_headers(serverfd, &rio, method, path, host, port);
    relay_response(serverfd, connfd);
    Close(serverfd);
}


int parse_uri(const char *uri,
              char *host, size_t hostsz,
              char *port, size_t portsz,
              char *path, size_t pathsz)
{
    if (strncasecmp(uri, "http://", 7) != 0) {
        return -1;
    }
    const char *p = uri + 7;

    const char *slash = strchr(p, '/');
    size_t hostport_len = 0;

    if (slash) {
        hostport_len = (size_t)(slash - p);
        snprintf(path, pathsz, "%s", slash);
    } else {
        hostport_len = strlen(p);
        snprintf(path, pathsz, "/");
    }

    char hostport[MAXLINE];
    if (hostport_len >= sizeof(hostport)) return -1;
    memcpy(hostport, p, hostport_len);
    hostport[hostport_len] = '\0';

    char *colon = strchr(hostport, ':');
    if (colon) {
        *colon = '\0';
        snprintf(host, hostsz, "%s", hostport);
        snprintf(port, portsz, "%s", colon + 1);
    } else {
        snprintf(host, hostsz, "%s", hostport);
        snprintf(port, portsz, "80");
    }

    if (host[0] == '\0' || path[0] == '\0') return -1;
    return 0;
}


// 헤더 읽기 ver 과제용
static const char *UA_HDR =
"User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:10.0.3) "
"Gecko/20120101 Firefox/10.0.3\r\n";

static void forward_request_headers(int serverfd, rio_t *client_rio,
                            const char *method,
                            const char *path,
                            const char *host,
                            const char *port) {
    char buf[MAXLINE];
    int n;

    // 1) 요청라인: HTTP/1.0
    n = snprintf(buf, sizeof(buf), "%s %s HTTP/1.0\r\n", method, path);
    Rio_writen(serverfd, buf, n);

    // 2) 클라 헤더는 끝까지 읽어서 '그냥 버림'
    while ((n = Rio_readlineb(client_rio, buf, sizeof(buf))) > 0) {
        if (!strcmp(buf, "\r\n")) break; // 빈 줄이면 헤더 종료
    }

    // 3) 보낼 최소 헤더 세트만 추가
    if (!strcmp(port, "80"))
        n = snprintf(buf, sizeof(buf), "Host: %s\r\n", host);
    else
        n = snprintf(buf, sizeof(buf), "Host: %s:%s\r\n", host, port);
    Rio_writen(serverfd, buf, n);

    Rio_writen(serverfd, UA_HDR, strlen(UA_HDR));
    Rio_writen(serverfd, "Connection: close\r\n", strlen("Connection: close\r\n"));
    Rio_writen(serverfd, "Proxy-Connection: close\r\n", strlen("Proxy-Connection: close\r\n"));

    Rio_writen(serverfd, "Accept-Encoding: identity\r\n", strlen("Accept-Encoding: identity\r\n"));

    // 4) 헤더 종료
    Rio_writen(serverfd, "\r\n", 2);
}

static void relay_response(int serverfd, int clientfd) {
    rio_t srio; char buf[MAXBUF];
    Rio_readinitb(&srio, serverfd);
    ssize_t n;
    while ((n = Rio_readnb(&srio, buf, sizeof(buf))) > 0) {
        Rio_writen(clientfd, buf, n);
    }
}


int main(int argc, char **argv) {
    Signal(SIGPIPE, SIG_IGN);
    if (argc != 2) { fprintf(stderr, "usage: %s <port>\n", argv[0]); exit(1); }

    int listenfd = Open_listenfd(argv[1]);
    while (1) {
        struct sockaddr_storage clientaddr; socklen_t len = sizeof(clientaddr);
        int connfd = Accept(listenfd, (SA*)&clientaddr, &len);
        handle_client(connfd);
        Close(connfd);
    }
    return 0;
}