diff --git a/src/handle_client.rs b/src/handle_client.rs new file mode 100644 index 0000000..b95dc7a --- /dev/null +++ b/src/handle_client.rs @@ -0,0 +1,50 @@ +use base64::{Engine as _, engine::general_purpose::STANDARD}; +use sha1::{Digest, Sha1}; +use std::io::{Read, Result, Write}; +use std::net::TcpStream; + + +use crate::read_header::read_header; + +pub fn handle_client(mut stream: TcpStream) -> Result<()> { + let mut buffer = [0; 512]; + + let bytes_read = stream.read(&mut buffer).expect("Failed to read stream"); + + let request = String::from_utf8_lossy(&buffer[..bytes_read]); + + if request.contains("Upgrade: websocket") { + let mut websocket_key = String::new(); + for line in request.lines() { + if line.starts_with("Sec-WebSocket-Key:") { + websocket_key = Some(line.split(":").nth(1).unwrap().trim().to_string()).unwrap(); + } + } + + let mut hasher = Sha1::new(); + hasher.update(format!( + "{}258EAFA5-E914-47DA-95CA-C5AB0DC85B11", + websocket_key + )); + let hashed_key = hasher.finalize(); + let accepted_key = STANDARD.encode(hashed_key); + + stream.write_all( + format!( + "HTTP/1.1 101 Switching Protocols\r\n\ + Upgrade: websocket\r\n\ + Connection: Upgrade\r\n\ + Sec-WebSocket-Accept: {}\r\n\r\n", + accepted_key + ) + .as_bytes(), + )?; + + loop { + let _read_frame = read_header(&mut stream); + } + } + stream.write_all(b"HTTP/1.1 200 OK\r\n")?; + stream.write_all(b"Content-Type: text/html\r\n")?; + Ok(()) +} \ No newline at end of file diff --git a/src/index.html b/src/index.html deleted file mode 100644 index cd480f4..0000000 --- a/src/index.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - Rust Custom Server - - -

Hello from the Rust Server!

-

This page was served by a custom HTTP server written in Rust. - nice hello -

- - - - - diff --git a/src/main.rs b/src/main.rs index 61004dd..4b7d9dd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,192 +1,12 @@ -use base64::{Engine as _, engine::general_purpose::STANDARD}; -use sha1::{Digest, Sha1}; -use std::io::{Read, Result, Write}; -use std::net::{TcpListener, TcpStream}; -use std::str::FromStr; -use std::{fs, thread}; +use std::io::Result; +use std::net::TcpListener; +use std::thread; -struct FrameHeader { - fin: bool, - opcode: u8, - payload_len: u64, - mask: bool, - masking_key: Option<[u8; 4]>, - message:Option, -} - -enum Message { - Text(String), - Binary(Vec), - Ping(String), - Pong(String), - Close(String), - Continue(String), -} - - -fn send_message(stream: &mut TcpStream, msg: &str) -> Result<()> { - let payload = msg.as_bytes(); - - let mut frame = Vec::new(); - - //first byte - let fin = 1 << 7; - let opcode = 0x1; - frame.push(fin | opcode); - - //second byte - let mask = 0x0; - let payload_length = payload.len() as u8; - frame.push(mask | payload_length); - - frame.extend_from_slice(payload); - - let _ = stream.write_all(&frame); - Ok(()) -} - -fn read_header(stream: &mut TcpStream) -> Result { - //read the first two bytes which will contain FIN and OPCODE flags and its size will be u8 - let mut first_two = [0u8; 2]; - let _ = stream.read_exact(&mut first_two); - - let byte_one = first_two[0]; - let byte_two = first_two[1]; - - //here we are doing AND operation and extraction from the first byte only - //1 & 0 = 0,1 & 1 =1 - //if fin =1 final frame, else continuous - let fin = (byte_one & 0b1000_0000) != 0; - - //opcode tells us about type of the message being sent by the client - let opcode = byte_one & 0b0000_1111; - - // here we will extract from the 2nd byte it will contain if the payload is masked or not and payload - // if mask = 1 masked else not - let mask = (byte_two & 0b1000_0000) != 0; - let mut payload_len = (byte_two & 0b0111_1111) as u64; - - // here we are putting the checks for the payload len - // normally payload len is 125 - // if it is 126 means the msg is larger and maybe in continuation - // 127 is the larget len for a payload and takes 8 bytes of the space - if payload_len == 126 { - let mut next_two = [0u8; 2]; - let _ = stream.read_exact(&mut next_two); - payload_len = u16::from_le_bytes(next_two) as u64; - } else if payload_len == 127 { - let mut next_eight = [0u8; 8]; - let _ = stream.read_exact(&mut next_eight); - payload_len = u64::from_le_bytes(next_eight) as u64; - } - - //here we will get the masking key if the payload is mask - // this is rule from client to server it should be mask - // from server to client it shouldn't - let masking_key = if mask { - let mut key = [0u8; 4]; - let _ = stream.read_exact(&mut key); - Some(key) - } else { - None - }; - - //now we are converting the masked data to unmasked data - let mut masked_data = vec![0u8;payload_len as usize]; - let _ = stream.read_exact(&mut masked_data); - let key = masking_key.unwrap(); - let mut decoded_data = vec![0u8;payload_len as usize]; - - - if mask { - for i in 0..payload_len { - let idx:usize = i.try_into().unwrap(); - decoded_data[idx] = masked_data[idx] ^ key[idx % 4]; - } - } +mod read_header; +mod send_message; +mod handle_client; - // here we will match all the types opcode can send and accordingly respond back - let message : Option = match opcode { - 0x1 => { - Some(Message::Text(String::from_utf8(decoded_data).expect("undefined byte"))) - } - 0x2 => { - Some(Message::Binary(decoded_data)) - } - 0x9 => { - Some(Message::Ping(String::from_str("recieved ping").expect("undefined msg"))) - } - 0xA => { - Some(Message::Pong(String::from_str("client's pong").expect("undefined msg"))) - } - 0x8 => { - Some(Message::Close(String::from_str("client's close").expect("undefined msg"))) - } - 0x0 => { - Some(Message::Continue((String::from_str("Continuation").expect("undefined msg")))) - } - _=>None - }; - - - Ok(FrameHeader { - fin, - opcode, - payload_len, - mask, - masking_key, - message - }) -} - -fn handle_client(mut stream: TcpStream) -> Result<()> { - let mut buffer = [0; 512]; - - let bytes_read = stream.read(&mut buffer).expect("Failed to read stream"); - - let request = String::from_utf8_lossy(&buffer[..bytes_read]); - - if request.contains("Upgrade: websocket") { - let mut websocket_key = String::new(); - for line in request.lines() { - if line.starts_with("Sec-WebSocket-Key:") { - websocket_key = Some(line.split(":").nth(1).unwrap().trim().to_string()).unwrap(); - } - } - - let mut hasher = Sha1::new(); - hasher.update(format!( - "{}258EAFA5-E914-47DA-95CA-C5AB0DC85B11", - websocket_key - )); - let hashed_key = hasher.finalize(); - let accepted_key = STANDARD.encode(hashed_key); - - stream.write_all( - format!( - "HTTP/1.1 101 Switching Protocols\r\n\ - Upgrade: websocket\r\n\ - Connection: Upgrade\r\n\ - Sec-WebSocket-Accept: {}\r\n\r\n", - accepted_key - ) - .as_bytes(), - )?; - loop { - let read_header = read_header(&mut stream); - send_message(&mut stream, "hola"); - } - } - - let contents = fs::read("src/index.html").unwrap(); - - stream.write_all(b"HTTP/1.1 200 OK\r\n")?; - stream.write_all(b"Content-Type: text/html\r\n")?; - stream.write_all(format!("Content-Length: {}\r\n\r\n", contents.len()).as_bytes())?; - stream.write_all(&contents)?; - - Ok(()) -} +use handle_client::handle_client; pub fn main() -> Result<()> { let listener = TcpListener::bind("127.0.0.1:8080")?; diff --git a/src/read_header.rs b/src/read_header.rs new file mode 100644 index 0000000..c0c3af4 --- /dev/null +++ b/src/read_header.rs @@ -0,0 +1,111 @@ +use std::io::{Read, Result}; +use std::net::TcpStream; +use std::str::FromStr; + +pub struct FrameHeader { + fin: bool, + opcode: u8, + payload_len: u64, + mask: bool, + masking_key: Option<[u8; 4]>, + message: Option, +} + +pub enum Message { + Text(String), + Binary(Vec), + Ping(String), + Pong(String), + Close(String), + Continue(String), +} + +pub fn read_header(stream: &mut TcpStream) -> Result { + //read the first two bytes which will contain FIN and OPCODE flags and its size will be u8 + let mut first_two = [0u8; 2]; + let _ = stream.read_exact(&mut first_two); + + let byte_one = first_two[0]; + let byte_two = first_two[1]; + + //here we are doing AND operation and extraction from the first byte only + //1 & 0 = 0,1 & 1 =1 + //if fin =1 final frame, else continuous + let fin = (byte_one & 0b1000_0000) != 0; + + //opcode tells us about type of the message being sent by the client + let opcode = byte_one & 0b0000_1111; + + // here we will extract from the 2nd byte it will contain if the payload is masked or not and payload + // if mask = 1 masked else not + let mask = (byte_two & 0b1000_0000) != 0; + let mut payload_len = (byte_two & 0b0111_1111) as u64; + + // here we are putting the checks for the payload len + // normally payload len is 125 + // if it is 126 means the msg is larger and maybe in continuation + // 127 is the larget len for a payload and takes 8 bytes of the space + if payload_len == 126 { + let mut next_two = [0u8; 2]; + let _ = stream.read_exact(&mut next_two); + payload_len = u16::from_le_bytes(next_two) as u64; + } else if payload_len == 127 { + let mut next_eight = [0u8; 8]; + let _ = stream.read_exact(&mut next_eight); + payload_len = u64::from_le_bytes(next_eight) as u64; + } + + //here we will get the masking key if the payload is mask + // this is rule from client to server it should be mask + // from server to client it shouldn't + let masking_key = if mask { + let mut key = [0u8; 4]; + let _ = stream.read_exact(&mut key); + Some(key) + } else { + None + }; + + //now we are converting the masked data to unmasked data + let mut masked_data = vec![0u8; payload_len as usize]; + let _ = stream.read_exact(&mut masked_data); + let key = masking_key.unwrap(); + let mut decoded_data = vec![0u8; payload_len as usize]; + + if mask { + for i in 0..payload_len { + let idx: usize = i.try_into().unwrap(); + decoded_data[idx] = masked_data[idx] ^ key[idx % 4]; + } + } + + // here we will match all the types opcode can send and accordingly respond back + let message: Option = match opcode { + 0x1 => Some(Message::Text( + String::from_utf8(decoded_data).expect("undefined byte"), + )), + 0x2 => Some(Message::Binary(decoded_data)), + 0x9 => Some(Message::Ping( + String::from_str("recieved ping").expect("undefined msg"), + )), + 0xA => Some(Message::Pong( + String::from_str("client's pong").expect("undefined msg"), + )), + 0x8 => Some(Message::Close( + String::from_str("client's close").expect("undefined msg"), + )), + 0x0 => Some(Message::Continue( + String::from_str("Continuation").expect("undefined msg"), + )), + _ => None, + }; + + Ok(FrameHeader { + fin, + opcode, + payload_len, + mask, + masking_key, + message, + }) +} diff --git a/src/send_message.rs b/src/send_message.rs new file mode 100644 index 0000000..61f64ef --- /dev/null +++ b/src/send_message.rs @@ -0,0 +1,23 @@ +use std::io::{Result, Write}; +use std::net::TcpStream; + +pub fn send_message(stream: &mut TcpStream, msg: &str) -> Result<()> { + let payload = msg.as_bytes(); + + let mut frame = Vec::new(); + + //first byte + let fin = 1 << 7; + let opcode = 0x1; + frame.push(fin | opcode); + + //second byte + let mask = 0x0; + let payload_length = payload.len() as u8; + frame.push(mask | payload_length); + + frame.extend_from_slice(payload); + + let _ = stream.write_all(&frame); + Ok(()) +} \ No newline at end of file