사실 어제 한말들 구라였다
어찌해야할지 자세히 적힌 메뉴얼덕에 말도 안되게 노답이다 정도는 아니었다 ㅇㅇ
걍 오바좀 했다
그리고 그럴 만한 사정이 있었다
…
분명 프록시 구현하고 연결해서 서버 돌아가는 거 몇번이고 확인했는데
이상하게도 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;
}