refactor server and move buffer code to lib

This commit is contained in:
David Westgate 2023-12-03 17:12:28 -08:00
parent c1d3f48fa3
commit c67f358d4a
3 changed files with 97 additions and 99 deletions

View File

@ -69,6 +69,7 @@ fn process_message(msg_bytes: &[u8], nick: &str) {
}
codes::error::NICKNAME_COLLISION => {
eprintln!("Nickname already in use on server. Connect again with a different one");
std::process::exit(1);
}
codes::error::SERVER_FULL => {
eprintln!("Server is full. Try again later");
@ -102,11 +103,11 @@ fn process_message(msg_bytes: &[u8], nick: &str) {
}
}
None => {
eprintln!("Malformed message recieved");
eprintln!("Malformed message recieved: {}", params);
}
},
None => {
eprintln!("Malformed message recieved");
eprintln!("Malformed message recieved: {}", params);
}
}
}

View File

@ -33,3 +33,44 @@ pub fn clear() {
}
pub const SPACE_BYTES: &[u8] = &[0x20];
pub fn one_op_buf(opcode: u8) -> [u8; 1] {
[opcode]
}
pub fn two_op_buf(opcode0: u8, opcode1: u8) -> [u8; 2] {
[opcode0, opcode1]
}
pub fn one_param_buf(opcode: u8, param: &str) -> Vec<u8> {
let opcode_buf: &[u8; 1] = &[opcode];
let param_buf: &[u8] = param.as_bytes();
let out_buf: Vec<u8> = [opcode_buf, param_buf].concat();
out_buf
}
pub fn two_param_buf(opcode: u8, param0: &str, param1: &str) -> Vec<u8> {
let opcode_buf: &[u8; 1] = &[opcode];
let param0_buf: &[u8] = param0.as_bytes();
let param1_buf: &[u8] = param1.as_bytes();
let out_buf: Vec<u8> = [opcode_buf, param0_buf, SPACE_BYTES, param1_buf].concat();
out_buf
}
pub fn three_param_buf(opcode: u8, param0: &str, param1: &str, param2: &str) -> Vec<u8> {
let opcode_buf: &[u8; 1] = &[opcode];
let param0_buf: &[u8] = param0.as_bytes();
let param1_buf: &[u8] = param1.as_bytes();
let param2_buf: &[u8] = param2.as_bytes();
let out_buf: Vec<u8> = [
opcode_buf,
param0_buf,
SPACE_BYTES,
param1_buf,
SPACE_BYTES,
param2_buf,
]
.concat();
out_buf
}

View File

@ -8,7 +8,7 @@ use std::{
};
use prompted::input;
use rust_irc::{clear, codes, SPACE_BYTES};
use rust_irc::{clear, codes, one_op_buf, one_param_buf, three_param_buf, two_op_buf};
const SERVER_ADDRESS: &str = "0.0.0.0:6667";
@ -28,20 +28,7 @@ impl Server {
}
fn message_room(room: &str, msg: &str, sender: &str, server: &Arc<Mutex<Server>>) {
let code_bytes = &[codes::MESSAGE_ROOM];
let room_bytes = room.as_bytes();
let msg_bytes = msg.as_bytes();
let sender_bytes = sender.as_bytes();
let out_buf: &Vec<u8> = &[
code_bytes,
room_bytes,
SPACE_BYTES,
sender_bytes,
SPACE_BYTES,
msg_bytes,
]
.concat();
let out_buf: Vec<u8> = three_param_buf(codes::MESSAGE_ROOM, room, sender, msg);
let mut guard: std::sync::MutexGuard<'_, Server> = server.lock().unwrap();
let server: &mut Server = guard.deref_mut();
@ -49,7 +36,6 @@ fn message_room(room: &str, msg: &str, sender: &str, server: &Arc<Mutex<Server>>
//2: Make sure sender is a member of the room, ifn error
//3: Message all non-sender users in the room the message, ifnone error empty room
//4: Message the sender RESPONSE_OK
println!("checking room {} ", room);
let room_users: Option<&Vec<String>> = server.rooms.get(room);
let mut sender_stream = server
.users
@ -72,14 +58,16 @@ fn message_room(room: &str, msg: &str, sender: &str, server: &Arc<Mutex<Server>>
for user in users {
if user.eq(sender) {
//4
sender_stream.write_all(&[codes::RESPONSE_OK]).unwrap();
sender_stream
.write_all(&one_op_buf(codes::RESPONSE_OK))
.unwrap();
} else {
//3
let recipient_stream: Option<&mut TcpStream> = server.users.get_mut(user);
match recipient_stream {
Some(str) => {
println!("Sending msg {:?}", out_buf.to_ascii_lowercase());
str.write_all(out_buf).unwrap();
str.write_all(&out_buf).unwrap();
}
None => {
eprintln!("Server error: could not find user");
@ -102,14 +90,7 @@ fn message_room(room: &str, msg: &str, sender: &str, server: &Arc<Mutex<Server>>
}
fn broadcast(op: u8, server: &Arc<Mutex<Server>>, message: &str) {
let size: usize = message.len() + 1;
let mut out_buf: Vec<u8> = vec![0; size];
out_buf[0] = op;
for i in 1..size {
out_buf[i] = *message.as_bytes().get(i - 1).unwrap();
}
let out_buf: Vec<u8> = one_param_buf(op, message);
let mut unlocked_server: std::sync::MutexGuard<'_, Server> = server.lock().unwrap();
let streams: std::collections::hash_map::ValuesMut<'_, String, TcpStream> =
unlocked_server.users.values_mut();
@ -123,11 +104,11 @@ fn disconnect_all(server: &Arc<Mutex<Server>>) {
let users: std::collections::hash_map::ValuesMut<'_, String, TcpStream> =
guard.users.values_mut();
users.for_each(|user: &mut TcpStream| {
user.write_all(&[codes::QUIT]).unwrap();
user.write_all(&one_op_buf(codes::QUIT)).unwrap();
user.shutdown(std::net::Shutdown::Both).unwrap();
})
}
/// Handle possible user commands from the client
fn handle_client(
server: &Arc<Mutex<Server>>,
stream: &mut TcpStream,
@ -135,52 +116,49 @@ fn handle_client(
cmd_bytes: &[u8],
param_bytes: &[u8],
) {
// handle user commands
match cmd_bytes[0] {
codes::REGISTER_NICK => {
stream
.write_all(&[codes::ERROR, codes::error::ALREADY_REGISTERED])
.unwrap();
let buf_out: [u8; 2] = two_op_buf(codes::ERROR, codes::error::ALREADY_REGISTERED);
stream.write_all(&buf_out).unwrap();
}
codes::LIST_ROOMS => {
let unlocked_server: std::sync::MutexGuard<'_, Server> = server.lock().unwrap();
let mut buf_out: Vec<u8> = Vec::new();
buf_out.extend_from_slice(&[codes::RESPONSE]);
let mut rooms: String = String::new();
for room in unlocked_server.rooms.keys() {
buf_out.extend_from_slice(room.as_bytes());
buf_out.extend_from_slice(SPACE_BYTES);
rooms.push_str(room);
rooms.push(' ');
}
let buf_out: Vec<u8> = one_param_buf(codes::RESPONSE, &rooms);
stream.write_all(&buf_out).unwrap();
}
codes::LIST_USERS => {
let unlocked_server: std::sync::MutexGuard<'_, Server> = server.lock().unwrap();
let mut buf_out: Vec<u8> = Vec::new();
buf_out.extend_from_slice(&[codes::RESPONSE]);
let mut users: String = String::new();
for user in unlocked_server.users.keys() {
buf_out.extend_from_slice(user.as_bytes());
buf_out.extend_from_slice(SPACE_BYTES);
users.push_str(user);
users.push(' ');
}
let buf_out: Vec<u8> = one_param_buf(codes::RESPONSE, &users);
stream.write_all(&buf_out).unwrap();
}
codes::LIST_USERS_IN_ROOM => {
let room: String = String::from_utf8_lossy(param_bytes).to_string();
let unlocked_server: std::sync::MutexGuard<'_, Server> = server.lock().unwrap();
let mut buf_out: Vec<u8> = Vec::new();
buf_out.extend_from_slice(&[codes::RESPONSE]);
match unlocked_server.rooms.get(&room) {
Some(l) => {
for ele in l {
buf_out.extend_from_slice(ele.as_bytes());
buf_out.extend_from_slice(SPACE_BYTES);
let mut user_list: String = String::new();
for user in l {
user_list.push_str(user);
user_list.push(' ');
}
let buf_out: Vec<u8> = one_param_buf(codes::RESPONSE, &user_list);
stream.write_all(&buf_out).unwrap();
}
None => {
stream
.write_all(&[codes::ERROR, codes::error::INVALID_ROOM])
.unwrap();
let buf_out: [u8; 2] = two_op_buf(codes::ERROR, codes::error::INVALID_ROOM);
stream.write_all(&buf_out).unwrap();
}
}
}
@ -204,11 +182,11 @@ fn handle_client(
let p: String = String::from_utf8_lossy(param_bytes).to_string();
message_all_senders_rooms(server, nickname, &p, stream);
stream.write_all(&[codes::RESPONSE_OK]).unwrap();
stream.write_all(&one_op_buf(codes::RESPONSE_OK)).unwrap();
}
codes::KEEP_ALIVE => {
stream.write_all(&[codes::RESPONSE_OK]).unwrap();
stream.write_all(&one_op_buf(codes::RESPONSE_OK)).unwrap();
}
//A message sent just to the users of the room passed in, except the client nickname
@ -247,30 +225,16 @@ fn message_all_senders_rooms(
) {
let rooms: Vec<String> = get_rooms_of_user(server, sender);
let mut guard: std::sync::MutexGuard<'_, Server> = server.lock().unwrap();
let sender_bytes: &[u8] = sender.as_bytes();
let code_bytes: &[u8] = &[codes::MESSAGE_ROOM];
let message_bytes: &[u8] = message.as_bytes();
let space_bytes: &[u8] = &[0x20];
for room in rooms {
let room_bytes: &[u8] = room.as_bytes();
let users: Vec<String> = guard.rooms.get(&room).unwrap().clone();
let out_buf: &Vec<u8> = &[
code_bytes,
room_bytes,
space_bytes,
sender_bytes,
space_bytes,
message_bytes,
]
.concat();
let out_buf: Vec<u8> = three_param_buf(codes::MESSAGE_ROOM, &room, &sender, &message);
for user in users {
if !user.eq(sender) {
let stream: Option<&mut TcpStream> = guard.users.get_mut(&user);
stream.unwrap().write_all(out_buf).unwrap();
stream.unwrap().write_all(&out_buf).unwrap();
}
}
stream.write_all(&[codes::RESPONSE_OK]).unwrap();
stream.write_all(&one_op_buf(codes::RESPONSE_OK)).unwrap();
}
}
@ -300,7 +264,7 @@ fn register_nick(server: &Arc<Mutex<Server>>, nickname: &str, stream: &mut TcpSt
let addr: String = clone.peer_addr().unwrap().to_string();
unlocked_server.users.insert(nickname.to_string(), clone);
stream.write_all(&[codes::RESPONSE_OK]).unwrap();
stream.write_all(&one_op_buf(codes::RESPONSE_OK)).unwrap();
println!("{} has registered nickname {}", addr, nickname);
}
}
@ -309,14 +273,13 @@ fn register_nick(server: &Arc<Mutex<Server>>, nickname: &str, stream: &mut TcpSt
/// Provide feedback about what room was just joined, and which rooms the user may be in
fn join_room(server: &Arc<Mutex<Server>>, user: &str, room: &str, stream: &mut TcpStream) {
let mut unlocked_server: std::sync::MutexGuard<'_, Server> = server.lock().unwrap();
let err_buf: [u8; 2] = two_op_buf(codes::ERROR, codes::error::ALREADY_IN_ROOM);
match unlocked_server.rooms.get_mut(room) {
Some(l) => {
for ele in l.iter_mut() {
if ele == user {
stream
.write_all(&[codes::ERROR, codes::error::ALREADY_IN_ROOM])
.unwrap();
stream.write_all(&err_buf).unwrap();
return;
}
}
@ -331,10 +294,8 @@ fn join_room(server: &Arc<Mutex<Server>>, user: &str, room: &str, stream: &mut T
let rooms: Vec<String> = get_rooms_of_user(server, user);
let rooms_expanded: String = rooms.join(",");
let response: String = format!("Joined {}. Current rooms: {}", room, rooms_expanded);
let res_bytes: &[u8] = response.as_bytes();
let code_bytes: &[u8] = &[codes::RESPONSE];
let out: &Vec<u8> = &[code_bytes, res_bytes].concat();
stream.write_all(out).unwrap();
let out_buf: Vec<u8> = one_param_buf(codes::RESPONSE, &response);
stream.write_all(&out_buf).unwrap();
}
/// Remove a user from a room, handling possible error cases.
@ -351,29 +312,23 @@ fn leave_room(server: &Arc<Mutex<Server>>, user: &str, room: &str, stream: &mut
let rooms: Vec<String> = get_rooms_of_user(server, user);
let rooms_expanded: String = rooms.join(",");
let response: String = format!("Left {}. Current rooms: {}", room, rooms_expanded);
let code_bytes: &[u8] = &[codes::RESPONSE];
let res_bytes: &[u8] = response.as_bytes();
let out: &Vec<u8> = &[code_bytes, res_bytes].concat();
stream.write_all(out).unwrap();
let out_buf: Vec<u8> = one_param_buf(codes::RESPONSE, &response);
stream.write_all(&out_buf).unwrap();
} else if l.len() == before_len {
stream
.write_all(&[codes::ERROR, codes::error::INVALID_ROOM])
.unwrap();
let err_buf: [u8; 2] = two_op_buf(codes::ERROR, codes::error::INVALID_ROOM);
stream.write_all(&err_buf).unwrap();
} else {
drop(unlocked_server);
let rooms: Vec<String> = get_rooms_of_user(server, user);
let rooms_expanded: String = rooms.join(",");
let response: String = format!("Left {}. Current rooms: {}", room, rooms_expanded);
let code_bytes: &[u8] = &[codes::RESPONSE];
let res_bytes: &[u8] = response.as_bytes();
let out: &Vec<u8> = &[code_bytes, res_bytes].concat();
stream.write_all(out).unwrap();
let out_buf: Vec<u8> = one_param_buf(codes::RESPONSE, &response);
stream.write_all(&out_buf).unwrap();
}
}
None => {
stream
.write_all(&[codes::ERROR, codes::error::INVALID_ROOM])
.unwrap();
let err_buff: [u8; 2] = two_op_buf(codes::ERROR, codes::error::INVALID_ROOM);
stream.write_all(&err_buff).unwrap();
}
}
}
@ -450,23 +405,24 @@ pub fn start() {
}
Err(_) => {
eprintln!("Error parsing client");
stream.write_all(&[codes::QUIT]).unwrap();
let out_buf: [u8; 1] =
one_op_buf(codes::QUIT);
stream.write_all(&out_buf).unwrap();
break;
}
}
}
} else {
stream
.write_all(&[
codes::ERROR,
codes::error::NOT_YET_REGISTERED,
])
.unwrap();
let err_buff: [u8; 2] = two_op_buf(
codes::ERROR,
codes::error::NOT_YET_REGISTERED,
);
stream.write_all(&err_buff).unwrap();
}
}
Err(_) => {
eprintln!("Error parsing client");
stream.write_all(&[codes::QUIT]).unwrap();
stream.write_all(&one_op_buf(codes::QUIT)).unwrap();
}
}
});