HTTP ํบ์๋ณด๊ธฐ with Rust
HTTP๋ TCP ํ๋กํ ์ฝ ์์ ์์ฑ๋ ํ
์คํธ ๊ธฐ๋ฐ์ ํ๋กํ ์ฝ์ด๋ค.
์์ "์น"์ด๋ผ๊ณ ํ๋ ๊ฒ๋ค์ ์ ๋ถ HTTP๋ก ์์ฑ๋ ์๋ฒ๋ฅผ ๋งํ๋ค.
๊ทธ๋์ ๋ชจ๋ ์น์ฌ์ดํธ๋ HTTP๋ก ์์ฑ๋๋ค.
๋ฒ์ ํ์คํ ๋ฆฌ
HTTP 1.1
1999๋
์ ์ฒ์ ๋์๋ค.
์ง์ ํ HTTP์ ์ญ์ฌ๋ 1.1๋ถํฐ ์์ํ๋ค๊ณ ๋ณด๋ฉด ๋๋ค.
๋๋ถ๋ถ์ ์น ํ๊ฒฝ์ ๋ค HTTP 1.1์ ์ง์ํ๊ณ , ์ฌ์ฉํ๋๋ฐ๋ค, ๋๋ถ๋ถ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ํ๋ ์์ํฌ์์๋ ์ด๊ฒ ๊ธฐ๋ณธ๊ฐ์ด๋ค.
๊ทธ๋ฅ ๋ธ๋ผ์ฐ์ ์ผ์ ์ด๋ ๋ค์ด๊ฐ๋ฉด ๋ค HTTP 1.1๋ก ๋์ํ๋ค.
HTTP 2
2015๋
์ ๋์๋ค.
HTTP 1.1์ ๋จ์ํ๊ณ ์ ๋์ํ์ง๋ง ๋จ์ ์ด ๊ฝค ๋ง์๋๋ฐ, ์ฑ๋ฅ์ ์ธ ์ธก๋ฉด์์ ๊ฐ์ ์ ๋ง์ด ๊ฐํ ๊ฒ ๋ฒ์ 2๋ค.
gRPC ํ๋กํ ์ฝ์ด HTTP 2 ์์์ ๋์ํ๋ค.
1. Multiplexed Stream
์๋ฅผ ๋ค์ด, 1.1์ ๋ฆฌ์์ค๋ฅผ ์ฐจ๋ก๋๋ก๋ง ๋ก๋ํ๊ธฐ ๋๋ฌธ์ ํ ๋ฆฌ์์ค๊ฐ ๋๋ ค์ง๋ฉด ๋ค๋ฅธ ๋ฆฌ์์ค๋ค๋ ๊ฐ์ ธ์ค์ง ๋ชปํ๋ ๋นํจ์จ์ ์ธ ๋์์ด ์์๋๋ฐ, HTTP2์์๋ ๋์ ํ๋์ด ๊ฐ๋ฅํ multiplexed stream ๊ธฐ๋ฅ์ด ์ถ๊ฐ๋๋ค.
์ ๊ทธ๋ฆผ๊ณผ ๊ฐ๋ค.
2. ํค๋ ์์ถ
๋์ผํ Header ์ ์ก ๋ฐ๋ณต์ผ๋ก ์ธํ ์ฑ๋ฅ ๋ญ๋น๋ฅผ ๋ง๊ธฐ ์ํด ํค๋๋ฅผ ์์ถํด์ ์ ์กํ๋ HPACK ๊ธฐ๋ฒ์ด ์ถ๊ฐ๋๋ค.
3. ์๋ฒ ํธ์
HTTP 2๋ ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ์๊ฒ ์ผ๋ฐฉ์ ์ผ๋ก ๋ณด๋ด๋๊ฒ ์๋๋ผ, ์๋ฒ๊ฐ ํด๋ผ์ด์ธํธ์๊ฒ ๋ณด๋ด๋ ๊ฒ๋ ๊ฐ๋ฅํ๋ค.
์์ฒญํ ๊ฒ์ด๋ผ๊ณ ์์๋๋ ์ ์ ๋ฆฌ์์ค ๋ฐ์ดํฐ๋ฅผ ์์ฒญํ๊ธฐ ์ ์ ๋ฏธ๋ฆฌ ๋ณด๋ด์ฃผ๋ ๊ฑฐ๋ผ์, ์บ์์ ๊ฐ๋
์ ๊ฐ๊น๋ค.
HTTP 3 (quic)
2022๋
์ ๋ฐํ๋๋ค.
๋๊ฒฉ๋ณ์ด ์ผ์ด๋ ๋ฒ์ ์ด๋ค.
์ฌ๊ธฐ์๋ TCP๋ฅผ ๋ฒ๋ฆฌ๊ณ UDP ์์ผ์ ๊ธฐ๋ฐ์ผ๋ก ์ฌ๊ตฌํ์ ํ๋ค! TCP์ ๋ถํ์ํ ๋ ๊ฑฐ์ํ ๊ตฌ์ฑ์์๋ค์ด ๋๋ฌด ๋ง์์ ๊ทธ๊ฑธ ๋์ด๋ด๋ ๊ฒ์ด ์ฃผ ๋ชฉ์ ์ด์๋ค.
์์ง ์์ฝ์์คํ ์ด ์ถฉ๋ถํ ๊ฐ์ถฐ์ ธ์์ง๋ ์๋ค๋ ๊ฒ์ด ํฐ ๋จ์ ์ด๋ค.
์ ๋ฐ์ ์ผ๋ก ์ฑ๋ฅ์ด ๋ ๋น ๋ฅธ๋ฐ๋ค, HTTP2 ๋๋น ํจํท ์์ค์ ๋ํ ์ฒ๋ฆฌ๊ฐ ๋ ์ข์์ก๋ค.
IP ๋ณ๊ฒฝ
IP๊ฐ ๋ฐ๋์ด๋ ์ฐ๊ฒฐ์ด ์ ์ง๋๋ ๊ธฐ๋ฅ์ด ์ถ๊ฐ๋๋ค.
TLS
TLS(SSL)๊ฐ ๊ธฐ๋ณธ์ผ๋ก ๋ค์ด๊ฐ๋ค. ๊ฒ๋ค๊ฐ ํธ๋์
ฐ์ดํฌ์ ๋์์ ์ฒ๋ฆฌ๋๊ธฐ ๋๋ฌธ์ ํจ์ฌ ๋นจ๋ผ์ง๋ค.
Zero RTT(Round Trip Time)
HTTP์๋ ์๋ณต ์๊ฐ(RTT)์ด๋ผ๋ ๊ฐ๋
์ด ์์๋ค.
ํธ๋์
ฐ์ดํฌ๋ฅผ ์ํด์ ๋ถํ์ํ ๋คํธ์ํฌ ์ง์ฐ์๊ฐ์ด ๋ง์ด ๋ฐ์ํ์๋๋ฐ, HTTP3์์๋ ์ด๋ฏธ ์ฐ๊ฒฐ๋ ์๋ฒ์์๋ ํธ๋์
ฐ์ดํฌ๋ฅผ ํ์ง ์๋๋ค. ๋ฌผ๋ก ์ต์ด ์ฐ๊ฒฐ์์๋ ํ๋ค.
์ฅ๋จ์
์์ฒญ<->์๋ต์ ๊ตฌ์กฐ๊ฐ ๋ช
ํํ๊ฒ ๋ถ๋ฆฌ๋์ด์๊ณ , ์บ์, ์ธ์ฝ๋ฉ ๋ฑ ์ฌ๋ฌ๊ฐ์ง ํ์ํ ๊ธฐ๋ฅ๋ค์ด ์ ๋ถ ๋ค ์๋น๋๋ฐ๋ค, ํ
์คํธ ๊ธฐ๋ฐ์ ํ๋กํ ์ฝ์ด๋ผ ์ฌ์ฉ์ด๋ ๋ถ์์ด ๋งค์ฐ ์ฝ๋ค๋ ์ฅ์ ์ด ์๋ค.
๊ฒ๋ค๊ฐ ๋ชจ๋ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์์ ์๋ฒฝํ ๊ตฌํ์ฒด๋ค์ ์ ๊ณตํ๊ณ ์๋ค๋ ์ ์์ MSA์์์ RPC์๋ ๊ฐ์ฅ ๊ฐ๋ ฅํ ์ฅ์ ์ ๊ฐ๊ณ ์๋ค. ์ด๊ฑด gRPC๋ ๋ฐ๋ผ์ค์ง ๋ชปํ๋ ๋ถ๋ถ์ด๋ค.
๋์ ํต์ง ํ ์คํธ๋ก๋ง ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ค๋ณด๋, ๋ฌด๊ฑฐ์ด ๋ฐ์ดํฐ๋ฅผ ์ค์ด๋๋ฅผ๋๋ ์กฐ๊ธ ๋๋ฆด ์๋ ์๋ค๋ ๋จ์ ์ด ์๋ค. ๊ทธ๊ฒ ๊ฑธ๋ฆฌ๋ฉด gRPC๋ฅผ ์ฐ๋ฉด ๋๋ค.
์์ฃผ ๋ง์ ๊ณณ์ ์ฌ์ฉ๋๊ณ ์๊ณ , ์์ผ๋ก๋ ๋์ฑ ๊ทธ๋ด ๊ฒ์ด๋ค.
๊น๋ณด๊ธฐ
TCP ์์ผ์ ๊ธฐ๋ฐ์ผ๋ก HTTP 1.1๋ฅผ ์ง์ ๊ตฌํํด๋ณด๋ฉด์ ๊ทธ ๊ตฌ์กฐ๋ฅผ ์ดํดํด๋ณด๋ ์๊ฐ์ ๊ฐ๋๋ก ํ๊ฒ ๋ค.
์ฌ์ค ๊ณจ์น์ํ๊ณ ๋ณต์กํ๊ฑด ์ด๋ฏธ TCP ๋ ์ด์ด์์ ๋ค ์ฒ๋ฆฌํด์ฃผ๊ธฐ ๋๋ฌธ์, ์๊ฐ๋ณด๋ค ๊ทธ๋ฆฌ ๋ณต์กํ๊ฑฐ๋ ์ด๋ ค์ด๊ฑด ๋ณ๋ก ์๋ค.
๋ง์ฝ ๋ค์๊ณผ ๊ฐ์ด API๋ฅผ ํธ์ถํ๋ค๋ฉด

๋ค์๊ณผ ๊ฐ์ ํํ๋ก TCP ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ๋๋ค.
์๋๋ wireshark์ tcp stream์ผ๋ก ์กฐํํ ๋ด์ฉ์ด๋ค.
๋ป๊ฑด์์ด ์์ฒญ์ด๊ณ , ํผ๋ฐ์์ด ์๋ต ๋ฐ์ดํฐ๋ค.
๊ทธ๋ฅ ์ ๊ฑธ ์ฃผ๊ณ ๋ฐ๊ณ ํ์ฑํ๋ ๊ฒ๋ง ๊ตฌํํด์ฃผ๋ฉด ๋๋ค..
Server: ์์ฒญ ๋ฐ์๋ณด๊ธฐ
๋จผ์ ์๋ฒ ๊ป๋ฐ๊ธฐ๋ถํฐ ๋ง๋ค์ด๋ณด๊ฒ ๋ค.
์ฐ์ TCP ์์ฒญ์ ๋ฐ์์, ๊ทธ ์์ฒญ์ ๋ธ๋ฆฐ ํจํท ๋ฐ์ดํฐ๋ฅผ ํ๋ฒ ๊น๋ณด๊ฒ ๋ค.
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpListener;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let listener = TcpListener::bind("0.0.0.0:8888").await?;
loop {
let (mut socket, _) = listener.accept().await?;
tokio::spawn(async move {
let mut read_buffer = [0; 1024];
loop {
let n = match socket.read(&mut read_buffer).await {
// socket closed
Ok(n) if n == 0 => return,
Ok(n) => n,
Err(e) => {
eprintln!("failed to read from socket; err = {:?}", e);
return;
}
};
let text = String::from_utf8(Vec::from(read_buffer)).unwrap();
println!("Client Says: {:?}", text);
// ๋๋ตํ๊ธฐ...
let write_buffer = [0; 1024];
if let Err(e) = socket.write_all(&write_buffer[0..n]).await {
eprintln!("failed to write to socket; err = {:?}", e);
return;
}
}
});
}
}
์คํํ๊ณ
๋ธ๋ผ์ฐ์ ๋ก ์ฐ๋ฌ๋ณด๋ฉด
์ ์์ ๋์ง ์๋๋ผ๋, ๋ธ๋ผ์ฐ์ ์์ ์์๋ณด๋ธ ์์ฒญ ๋ฐ์ดํฐ๋ ์ฝ์ด๋ณผ ์ ์์ ๊ฒ์ด๋ค.
๊ทธ๋ผ ๋๋ต ์ด๋ฐ์์ผ๋ก ๋ฐ์ดํฐ๊ฐ ๋ ๋ผ์จ๋ค.
์ ๊ธฐ ๋ค์ด์๋๊ฒ ๋ค ์์ฒญ headers๋ค์ด๋ค.
ํ
์คํธ ๊ธฐ๋ฐ์ด๋ผ ์์๋ณด๊ธฐ๋ ์ฐธ ์ฝ๋ค.
์๋ฒ์์๋ ์ ๊ฑธ ๊ทธ๋๋ก ํ์ฑํด์ ์ฌ์ฉํ๋ฉด ๋๋ ๊ฒ์ด๋ค.
์ด๋ฐ์์ผ๋ก๋ค๊ฐ
Server: ์๋ต ๋ณด๋ด๊ธฐ
์ด๋ฒ์๋ ์๋ต๊น์ง ๊ตฌ์ฑํด์ ๋ธ๋ผ์ฐ์ ์์๋ ์ ์ ๋์์ด ๋๊ฒ๋ ๋ง๋ค์ด๋ณด๊ฒ ๋ค.
๋ฐฉ๋ฒ์ ๊ทธ๋ฆฌ ์ด๋ ต์ง ์๋ค.
์๋ต ํ
์คํธ์ ํฌ๋งท์ ์๋์ ๊ฐ์ ํํ์ธ๋ฐ
์๋ต ์ํ
์๋ต ํค๋1
์๋ต ํค๋2
...
์๋ต ํค๋N
์๋ต ๋ณธ๋ฌธ
์ ๋๋ก ๋ฑ ๊ตฌ์ฑํด์ ๋ณด๋ด์ฃผ๋ฉด ๋๋ค.
ํ์ ํค๋๋ Content-Length ์ ๋๋ค.
์ ๋๊ฐ๊ณ ๋ค์ ์คํํด๋ณด๋ฉด
์ด์ ๋ ๊ฒ์ด๋ค.
์ ์ฒด ์ฝ๋
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpListener;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let listener = TcpListener::bind("0.0.0.0:8888").await?;
loop {
let (mut socket, _) = listener.accept().await?;
tokio::spawn(async move {
let mut read_buffer = [0; 1024];
loop {
let n = match socket.read(&mut read_buffer).await {
// socket closed
Ok(n) if n == 0 => return,
Ok(n) => n,
Err(e) => {
eprintln!("failed to read from socket; err = {:?}", e);
return;
}
};
println!("read {} bytes", n);
let text = String::from_utf8(Vec::from(&read_buffer[0..n])).unwrap();
println!("Client Says: {:?}", text);
let mut method = "NONE";
let mut uri = "";
let mut host = "";
for line in text.split("\r\n") {
match line {
line if line.starts_with("GET") => {
method = "GET";
uri = line.split(" ").nth(1).unwrap();
}
line if line.starts_with("POST") => {
method = "POST";
uri = line.split(" ").nth(1).unwrap();
}
line if line.starts_with("Host") => {
host = line.split(" ").nth(1).unwrap();
}
_ => {}
}
}
println!("{method} {host}{uri}");
// ๋๋ตํ๊ธฐ...
let response_headers =
vec!["HTTP/1.1 200 OK", "Content-Length: 11", "", "Hello World"];
let response_messages = response_headers.join("\r\n");
println!("{response_messages}");
if let Err(e) = socket.write_all(response_messages.as_bytes()).await {
eprintln!("failed to write to socket; err = {:?}", e);
return;
}
}
});
}
}Client ๊ตฌํํด๋ณด๊ธฐ
์ด๋ฒ์๋ client๋ฅผ ์ง์ ๊ตฌํํด์ ์๋ ์๋
์ด ์๋ฒ์ ์์ฒญ์ ๋ ๋ ค๋ณด๊ฒ ๋ค.
๋ด๊ฐ ๋ง๋ ๊ฒ ์๋๋ผ express๋ก ๊ตฌํ๋ ์๋ฒ๋ค.
Client๋ TCP ์์์ ๊ตฌํํ๋๊ฑฐ๋ผ๋ฉด ์ฝ๋ค.
์๋์ ํํ๋ก ์์ฒญ ๋ฐ์ดํฐ๋ฅผ ๋ง์์ tcp๋ก ์๊ณ , ๋ค์ ์๋ต์ ๋ฐ์์ค๋ฉด ๊ทธ๋ง์ด๋ค.
METHOD ๊ฒฝ๋ก HTTP๋ฒ์
์์ฒญ ํค๋1
์์ฒญ ํค๋2
...
์์ฒญ ํค๋N
์์ฒญ ๋ฐ๋
์ด๋ ๊ฒ ๋ง์ด๋ค.
๊ทธ๋ผ ์ ๋์๊ฐ ๊ฒ์ด๋ค.

์ ์ฒด ์ฝ๋
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::TcpStream,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut client = TcpStream::connect("localhost:8110").await.unwrap();
let messages = vec![
"GET / HTTP/1.1",
"Host: localhost:8110",
"Connection: keep-alive",
"",
"",
];
client
.write_all(messages.join("\r\n").as_bytes())
.await
.unwrap();
let mut buffer = [0; 1024];
client.read(&mut buffer).await.unwrap();
println!("{}", String::from_utf8_lossy(&buffer));
Ok(())
}์ฐธ์กฐ
https://developer.mozilla.org/ko/docs/Web/HTTP/Messages
https://doc.rust-lang.org/book/ch20-01-single-threaded.html
https://www.cloudflare.com/ko-kr/learning/performance/http2-vs-http1.1/