websocket and data:
This commit is contained in:
parent
964c90d2f2
commit
1893eb0599
5 changed files with 65 additions and 50 deletions
0
main.rs
Normal file
0
main.rs
Normal file
56
src/main.rs
56
src/main.rs
|
|
@ -10,7 +10,7 @@ use tokio::io::AsyncWriteExt;
|
||||||
use tokio::net::{TcpListener, TcpStream};
|
use tokio::net::{TcpListener, TcpStream};
|
||||||
use tokio::time;
|
use tokio::time;
|
||||||
|
|
||||||
use crate::websoket_connection::WebsocketConnection;
|
use crate::websoket_connection::{FrameType, WebsocketConnection};
|
||||||
use crate::{
|
use crate::{
|
||||||
request::{Connection, ServerPath},
|
request::{Connection, ServerPath},
|
||||||
response::{Response, ResponseCode, ResponseHeader},
|
response::{Response, ResponseCode, ResponseHeader},
|
||||||
|
|
@ -50,52 +50,34 @@ async fn handle_http_connection(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
println!("Timed out");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
println!("{req:?}");
|
|
||||||
|
|
||||||
// DEBUG: Print the path matching
|
|
||||||
let matchable = req.path.to_matchable();
|
let matchable = req.path.to_matchable();
|
||||||
println!("Path matchable: {:?}", matchable);
|
|
||||||
println!("Path as slice: {:?}", matchable.as_slice());
|
|
||||||
|
|
||||||
let response = match matchable.as_slice() {
|
let response = match matchable.as_slice() {
|
||||||
["public", file] => {
|
["public", file] => {
|
||||||
println!("Matched public file: {}", file);
|
|
||||||
match Response::from_file(Path::new(format!("./public/{file}").as_str())) {
|
match Response::from_file(Path::new(format!("./public/{file}").as_str())) {
|
||||||
Ok(resp) => resp,
|
Ok(resp) => resp,
|
||||||
Err(_) => Response::new().with_code(ResponseCode::NotFound),
|
Err(_) => Response::new().with_code(ResponseCode::NotFound),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
["websocket"] => {
|
["websocket"] => match WebsocketConnection::initialize_connection(req, stream).await {
|
||||||
println!("WebSocket path matched!");
|
Ok(ws) => {
|
||||||
println!("Initializing WebSocket connection...");
|
return Ok(Some(ws));
|
||||||
match WebsocketConnection::initialize_connection(req, stream).await {
|
|
||||||
Ok(ws) => {
|
|
||||||
println!("WebSocket connection established successfully!");
|
|
||||||
return Ok(Some(ws));
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("WebSocket initialization failed: {}", e);
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
Err(e) => {
|
||||||
[] => {
|
return Err(e);
|
||||||
println!("Matched root path, redirecting");
|
}
|
||||||
Response::new()
|
},
|
||||||
.with_code(ResponseCode::PermanentRedirect)
|
[] => Response::new()
|
||||||
.with_header(ResponseHeader::Connection(Connection::KeepAlive))
|
.with_code(ResponseCode::PermanentRedirect)
|
||||||
.with_header(ResponseHeader::Location(
|
.with_header(ResponseHeader::Connection(Connection::KeepAlive))
|
||||||
ServerPath::from_str("/public/index.html").unwrap(),
|
.with_header(ResponseHeader::Location(
|
||||||
))
|
ServerPath::from_str("/public/index.html").unwrap(),
|
||||||
}
|
)),
|
||||||
other => {
|
_ => Response::new().with_code(ResponseCode::NotFound),
|
||||||
println!("No match, path was: {:?}", other);
|
|
||||||
Response::new().with_code(ResponseCode::NotFound)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
response.respond(&mut stream).await?;
|
response.respond(&mut stream).await?;
|
||||||
stream.flush().await?;
|
stream.flush().await?;
|
||||||
|
|
@ -103,7 +85,6 @@ async fn handle_http_connection(
|
||||||
if req.headers.contains(&request::RequestHeader::Connection(
|
if req.headers.contains(&request::RequestHeader::Connection(
|
||||||
request::Connection::Close,
|
request::Connection::Close,
|
||||||
)) {
|
)) {
|
||||||
println!("Connection closed");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -114,6 +95,11 @@ async fn handle_websocket(mut web_socket: WebsocketConnection) -> tokio::io::Res
|
||||||
loop {
|
loop {
|
||||||
let message = web_socket.read_next_message().await?;
|
let message = web_socket.read_next_message().await?;
|
||||||
|
|
||||||
println!("{:?}", message.data);
|
if message.frame_type == FrameType::TextFrame {
|
||||||
|
println!("{}", String::from_utf8_lossy(&message.data));
|
||||||
|
web_socket
|
||||||
|
.send_message(FrameType::TextFrame, "message_received".as_bytes())
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,8 +66,6 @@ impl Upgrade {
|
||||||
impl FromStr for Upgrade {
|
impl FromStr for Upgrade {
|
||||||
type Err = tokio::io::Error;
|
type Err = tokio::io::Error;
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
println!("{s}");
|
|
||||||
|
|
||||||
match s.split('/').collect::<Vec<&str>>().as_slice() {
|
match s.split('/').collect::<Vec<&str>>().as_slice() {
|
||||||
[protocol, version] => Ok(Self {
|
[protocol, version] => Ok(Self {
|
||||||
protocol: Protocol::from_str(protocol)?,
|
protocol: Protocol::from_str(protocol)?,
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ use crate::{
|
||||||
use tokio::io::{self, AsyncWriteExt};
|
use tokio::io::{self, AsyncWriteExt};
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Response {
|
pub struct Response {
|
||||||
http_version: Box<str>,
|
http_version: Box<str>,
|
||||||
code: ResponseCode,
|
code: ResponseCode,
|
||||||
|
|
@ -19,22 +20,24 @@ impl Response {
|
||||||
pub async fn respond(self, stream: &mut TcpStream) -> Result<(), io::Error> {
|
pub async fn respond(self, stream: &mut TcpStream) -> Result<(), io::Error> {
|
||||||
let binding = self.to_str();
|
let binding = self.to_str();
|
||||||
let mut output = binding.as_bytes().to_vec();
|
let mut output = binding.as_bytes().to_vec();
|
||||||
output.extend_from_slice(b"\r\n");
|
|
||||||
|
|
||||||
if !self.data.is_empty() {
|
if !self.data.is_empty() {
|
||||||
output.extend_from_slice(format!("Content-Length: {}", self.data.len()).as_bytes());
|
output.extend_from_slice(format!("Content-Length: {}", self.data.len()).as_bytes());
|
||||||
output.extend_from_slice(b"\r\n\r\n");
|
output.extend_from_slice(b"\r\n\r\n");
|
||||||
output.extend_from_slice(&self.data);
|
output.extend_from_slice(&self.data);
|
||||||
|
} else {
|
||||||
|
output.extend_from_slice(b"\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
stream.write_all(output.as_slice()).await?;
|
stream.write_all(output.as_slice()).await?;
|
||||||
|
stream.flush().await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_str(&self) -> Box<str> {
|
pub fn to_str(&self) -> Box<str> {
|
||||||
format!(
|
format!(
|
||||||
"{} {}\r\n{}",
|
"{} {}\r\n{}\r\n",
|
||||||
self.http_version,
|
self.http_version,
|
||||||
self.code.to_code(),
|
self.code.to_code(),
|
||||||
self.headers
|
self.headers
|
||||||
|
|
@ -111,6 +114,7 @@ impl Response {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum ResponseCode {
|
pub enum ResponseCode {
|
||||||
Continue,
|
Continue,
|
||||||
SwitchingProtocols,
|
SwitchingProtocols,
|
||||||
|
|
@ -244,6 +248,7 @@ impl ResponseCode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum ResponseHeader {
|
pub enum ResponseHeader {
|
||||||
ContentType(Content),
|
ContentType(Content),
|
||||||
CacheControl(CacheControl),
|
CacheControl(CacheControl),
|
||||||
|
|
@ -273,6 +278,7 @@ impl ResponseHeader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum CacheControl {
|
pub enum CacheControl {
|
||||||
NoStore,
|
NoStore,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,15 +42,43 @@ pub enum FrameType {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebsocketConnection {
|
impl WebsocketConnection {
|
||||||
pub async fn send_message(&self) -> io::Result<()> {
|
pub async fn send_message(&mut self, frame_type: FrameType, data: &[u8]) -> io::Result<()> {
|
||||||
todo!()
|
let mut header = Vec::with_capacity(14); // Max header size for 127-length payload
|
||||||
|
|
||||||
|
// First byte: FIN (1) + RSV1-3 (000) + opcode
|
||||||
|
let opcode = match frame_type {
|
||||||
|
FrameType::TextFrame => 0x1,
|
||||||
|
FrameType::BinaryFrame => 0x2,
|
||||||
|
FrameType::Ping => 0x9,
|
||||||
|
FrameType::Pong => 0xA,
|
||||||
|
FrameType::ConnectionClose => 0x8,
|
||||||
|
_ => panic!("No other type should by passed to this function"),
|
||||||
|
};
|
||||||
|
header.push(0b1000_0000 | opcode); // FIN = 1
|
||||||
|
|
||||||
|
// Second byte: MASK bit = 0 (server -> client frames are NOT masked)
|
||||||
|
let payload_len = data.len();
|
||||||
|
if payload_len < 126 {
|
||||||
|
header.push(payload_len as u8);
|
||||||
|
} else if payload_len <= u16::MAX as usize {
|
||||||
|
header.push(126);
|
||||||
|
header.extend_from_slice(&(payload_len as u16).to_be_bytes());
|
||||||
|
} else {
|
||||||
|
header.push(127);
|
||||||
|
header.extend_from_slice(&(payload_len as u64).to_be_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send header + payload
|
||||||
|
self.stream.write_all(&header).await?;
|
||||||
|
self.stream.write_all(data).await?;
|
||||||
|
self.stream.flush().await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_next_message(&mut self) -> io::Result<DataFrame> {
|
pub async fn read_next_message(&mut self) -> io::Result<DataFrame> {
|
||||||
let first_line = self.parse_single_block().await?;
|
let first_line = self.parse_single_block().await?;
|
||||||
|
|
||||||
println!("Block read");
|
|
||||||
|
|
||||||
let mut data = first_line.data;
|
let mut data = first_line.data;
|
||||||
let frame_type = first_line.message_type;
|
let frame_type = first_line.message_type;
|
||||||
|
|
||||||
|
|
@ -184,7 +212,7 @@ impl WebsocketConnection {
|
||||||
let result = hasher.finalize();
|
let result = hasher.finalize();
|
||||||
let result = BASE64_STANDARD.encode(result);
|
let result = BASE64_STANDARD.encode(result);
|
||||||
|
|
||||||
Response::new()
|
let rep = Response::new()
|
||||||
.with_code(crate::response::ResponseCode::SwitchingProtocols)
|
.with_code(crate::response::ResponseCode::SwitchingProtocols)
|
||||||
.with_header(crate::response::ResponseHeader::Upgrade(Upgrade {
|
.with_header(crate::response::ResponseHeader::Upgrade(Upgrade {
|
||||||
protocol: Protocol::Websocket,
|
protocol: Protocol::Websocket,
|
||||||
|
|
@ -196,11 +224,8 @@ impl WebsocketConnection {
|
||||||
.with_header(crate::response::ResponseHeader::Other {
|
.with_header(crate::response::ResponseHeader::Other {
|
||||||
header_name: "Sec-WebSocket-Accept".into(),
|
header_name: "Sec-WebSocket-Accept".into(),
|
||||||
header_value: result.into(),
|
header_value: result.into(),
|
||||||
})
|
});
|
||||||
.respond(&mut stream)
|
rep.respond(&mut stream).await?;
|
||||||
.await?;
|
|
||||||
|
|
||||||
stream.flush().await?;
|
|
||||||
|
|
||||||
Ok(Self { stream })
|
Ok(Self { stream })
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue