Skip to content

simple chat coding exercise #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 149 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
149 commits
Select commit Hold shift + click to select a range
651f048
added stuff
mypickly Sep 27, 2024
048af78
added stuff
mypickly Sep 27, 2024
d80815c
added stuff
mypickly Sep 27, 2024
c2ed650
added stuff
mypickly Sep 27, 2024
6b81036
added stuff
mypickly Sep 27, 2024
8e49ae8
added tests
mypickly Sep 27, 2024
0fe51a9
added tests
mypickly Sep 27, 2024
c6ce5a0
added tests
mypickly Sep 27, 2024
fa3d0c1
added tests
mypickly Sep 27, 2024
dbde1dd
added tests
mypickly Sep 27, 2024
467a114
added tests
mypickly Sep 27, 2024
27351f2
added tests
mypickly Sep 27, 2024
75e53ac
added tests
mypickly Sep 27, 2024
f704361
added tests
mypickly Sep 27, 2024
9e08467
added tests
mypickly Sep 27, 2024
234a533
added tests
mypickly Sep 28, 2024
65e30c7
added tests
mypickly Sep 28, 2024
738a18f
added tests
mypickly Sep 28, 2024
c9d757f
added tests
mypickly Sep 28, 2024
628c2ac
added tests
mypickly Sep 28, 2024
b4e348b
added tests
mypickly Sep 28, 2024
b8239f7
added tests
mypickly Sep 28, 2024
396da74
added tests
mypickly Sep 28, 2024
fc58fff
added tests
mypickly Sep 28, 2024
abaa8ee
added tests
mypickly Sep 30, 2024
b7180bd
added tests
mypickly Sep 30, 2024
3a4b2cf
added tests
mypickly Sep 30, 2024
9006d37
added tests
mypickly Sep 30, 2024
8bc2452
added tests
mypickly Sep 30, 2024
5993299
added tests
mypickly Sep 30, 2024
6dfe091
added tests
mypickly Sep 30, 2024
6031e00
added tests
mypickly Sep 30, 2024
96c7bce
added tests
mypickly Sep 30, 2024
bdcbc61
feat: adding counters to store stats
mypickly Mar 13, 2025
b1e2d16
updating with appropriate flags for flamegraph
mypickly Mar 13, 2025
332828e
updating with appropriate flags for flamegraph
mypickly Mar 13, 2025
6ea3261
updating with appropriate flags for flamegraph
mypickly Mar 13, 2025
cf82a06
updating with appropriate flags for flamegraph
mypickly Mar 13, 2025
d4328ed
updating with appropriate flags for flamegraph
mypickly Mar 13, 2025
3f3db6c
updating with appropriate flags for flamegraph
mypickly Mar 13, 2025
6b21a63
updating with appropriate flags for flamegraph
mypickly Mar 13, 2025
ed88421
updating with appropriate flags for flamegraph
mypickly Mar 13, 2025
d6c0803
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
77da868
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
dedd8a9
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
ae5f0f2
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
6d7ecdf
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
c376f64
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
a044828
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
f4edc18
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
9f04dea
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
d3b6d4e
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
92c51f2
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
f2b9e48
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
b4e00c4
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
82251ce
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
a2facf4
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
24493fb
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
bb37afc
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
07a730b
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
4f87630
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
66cbe9d
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
7861a5f
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
23af64a
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
f1b81f5
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
bfc9166
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
b2ab226
updating with appropriate flags for flamegraph
mypickly Mar 14, 2025
bc342e9
updating with appropriate flags for flamegraph
mypickly Mar 15, 2025
316818d
updating with appropriate flags for flamegraph
mypickly Mar 15, 2025
5b5535d
updating with appropriate flags for flamegraph
mypickly Mar 15, 2025
e6f4faf
updating with appropriate flags for flamegraph
mypickly Mar 15, 2025
6635d0b
updating with appropriate flags for flamegraph
mypickly Mar 15, 2025
c7054cf
updating with appropriate flags for flamegraph
mypickly Mar 15, 2025
05048cc
feat: adding a web-server
mypickly Mar 17, 2025
1cd5ec6
feat: adding a web-server
mypickly Apr 10, 2025
fc96588
feat: adding a web-server
mypickly Apr 10, 2025
c119b62
feat: adding a web-server
mypickly Apr 10, 2025
0d6643e
feat: adding a web-server
mypickly Apr 10, 2025
54a18fe
feat: adding a web-server
mypickly Apr 10, 2025
758a4ec
feat: adding a web-server
mypickly Apr 10, 2025
8a6e880
feat: adding a web-server
mypickly Apr 10, 2025
53c6186
feat: adding a web-server
mypickly Apr 10, 2025
f211e18
feat: adding a web-server
mypickly Apr 10, 2025
931d344
feat: adding a web-server
mypickly Apr 10, 2025
519b973
feat: adding a web-server
mypickly Apr 10, 2025
86f6c25
feat: adding a web-server
mypickly Apr 10, 2025
7f79247
feat: adding a web-server
mypickly Apr 10, 2025
837526a
feat: adding a web-server
mypickly Apr 10, 2025
963903f
feat: adding a web-server
mypickly Apr 10, 2025
4e89386
feat: adding a web-server
mypickly Apr 10, 2025
69928c0
feat: adding a web-server
mypickly Apr 10, 2025
3f1d832
feat: adding a web-server
mypickly Apr 10, 2025
6938655
feat: adding a web-server
mypickly Apr 10, 2025
c5c7c97
feat: adding a web-server
mypickly Apr 10, 2025
3348d13
feat: adding a web-server
mypickly Apr 10, 2025
07baf24
feat: adding a web-server
mypickly Apr 10, 2025
4f59adc
feat: adding a web-server
mypickly Apr 10, 2025
f13c490
feat: adding a web-server
mypickly Apr 10, 2025
3c16ba7
feat: adding a web-server
mypickly Apr 10, 2025
5bee3bd
feat: adding a web-server
mypickly Apr 10, 2025
9820204
feat: adding a web-server
mypickly Apr 10, 2025
b087e4b
feat: adding a web-server
mypickly Apr 10, 2025
e443f37
feat: adding a web-server
mypickly Apr 10, 2025
a49a042
feat: adding a web-server
mypickly Apr 10, 2025
2e87c01
feat: adding a web-server
mypickly Apr 10, 2025
4e85868
feat: adding a web-server
mypickly Apr 10, 2025
65448d5
feat: adding a web-server
mypickly Apr 14, 2025
dc78280
feat: adding a web-server
mypickly Apr 14, 2025
0c3af54
feat: adding a web-server
mypickly Apr 14, 2025
f0dc73a
feat: adding a web-server
mypickly Apr 14, 2025
0293021
feat: adding a web-server
mypickly Apr 14, 2025
584448b
feat: adding a web-server
mypickly Apr 14, 2025
d3cb3fe
feat: adding a web-server
mypickly Apr 18, 2025
b4fe235
feat: adding a web-server
mypickly Apr 18, 2025
c6ff34d
feat: adding a web-server
mypickly Apr 18, 2025
1ae1b87
feat: adding a web-server
mypickly Apr 18, 2025
f08e40b
feat: adding a web-server
mypickly Apr 19, 2025
1c6e212
feat: adding a web-server
mypickly Apr 19, 2025
2c736f0
feat: adding a web-server
mypickly Apr 19, 2025
9579265
feat: adding a web-server
mypickly Apr 19, 2025
9be41f0
feat: adding a web-server
mypickly Apr 19, 2025
5515bdf
feat: adding a web-server
mypickly Apr 30, 2025
e6fd381
feat: adding a web-server
mypickly Apr 30, 2025
f8899d8
fix: updating utils to include common functions and ds
mypickly Apr 30, 2025
b84dfee
fix: updating utils to include common functions and ds
mypickly Apr 30, 2025
dcfb7bf
fix: updating utils to include common functions and ds
mypickly Apr 30, 2025
e907310
fix: updating utils to include common functions and ds
mypickly Apr 30, 2025
bfc82f8
fix: updating utils to include common functions and ds
mypickly Apr 30, 2025
6633454
fix: updating utils to include common functions and ds
mypickly Apr 30, 2025
184f580
fix: updating utils to include common functions and ds
mypickly Apr 30, 2025
0274a21
fix: updating utils to include common functions and ds
mypickly Apr 30, 2025
97f629d
fix: updating utils to include common functions and ds
mypickly Apr 30, 2025
c102165
fix: updating utils to include common functions and ds
mypickly Apr 30, 2025
a71bc53
fix: updating utils to include common functions and ds
mypickly Apr 30, 2025
05f9274
fix: updating utils to include common functions and ds
mypickly Apr 30, 2025
e215c35
fix: updating utils to include common functions and ds
mypickly Apr 30, 2025
ec117f8
fix: updating utils to include common functions and ds
mypickly Apr 30, 2025
54c01a2
fix: updating utils to include common functions and ds
mypickly Apr 30, 2025
8875a04
fix: updating utils to include common functions and ds
mypickly Apr 30, 2025
f0f122d
fix: updating utils to include common functions and ds
mypickly Apr 30, 2025
e1d2cd4
fix: updating utils to include common functions and ds
mypickly Apr 30, 2025
2a796c9
fix: updating utils to include common functions and ds
mypickly Apr 30, 2025
029db51
fix: updating utils to include common functions and ds
mypickly May 1, 2025
1672e00
fix: updating utils to include common functions and ds
mypickly May 1, 2025
98c3006
fix: updating utils to include common functions and ds
mypickly May 1, 2025
7cd44ad
fix: updating utils to include common functions and ds
mypickly May 2, 2025
b925609
fix: updating utils to include common functions and ds
mypickly May 2, 2025
9b9fef5
fix: updating utils to include common functions and ds
mypickly May 2, 2025
d1da3b8
fix: updating utils to include common functions and ds
mypickly May 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions .idea/codeStyles/codeStyleConfig.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions .idea/dictionaries/rsouvik.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/simple-chat.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "chat_app"
version = "0.1.0"
edition = "2021"

#[package.metadata.release]
#release = false

[profile.release]
debug = true

[target.x86_64-unknown-linux-gnu]
linker = "/usr/bin/clang"
rustflags = ["-Clink-arg=-fuse-ld=lld", "-Clink-arg=-Wl,--no-rosegment"]

[dependencies]
futures-util = { version = "0.3.30", features = ["sink"] }
http = "1.1.0"
#futures-util = "0.3"
tokio = { version = "1.40.0", features = ["full"] }
tokio-websockets = { version = "0.8.0", features = ["client", "fastrand", "server", "sha1_smol"] }
structopt = { version = "0.3", default-features = false }
futures = "0.3.30"
diesel = { version = "2.2.8", features = ["postgres", "mysql"] }
dotenvy = "0.15.7"
actix-web = "4.5.1"
serde = {version = "1.0.27", features = ["derive"] }
serde_json = "1.0.9"
110 changes: 110 additions & 0 deletions src/bin/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use futures_util::stream::StreamExt;
use futures_util::SinkExt;
use http::Uri;
use structopt::StructOpt;
use tokio::io::{AsyncBufReadExt, BufReader};
use tokio_websockets::{ClientBuilder, Message};

#[derive(StructOpt)]
struct Cli {
#[structopt(short, long, default_value = "127.0.0.1")]
host: String,

#[structopt(short, long, default_value = "2000")]
port: String,

#[structopt(short, long)]
username: String,
}

#[tokio::main]
async fn main() -> Result<(), tokio_websockets::Error> {
let args = Cli::from_args();
let ws_url = format!("ws://{}:{}/", args.host, args.port);

let (mut ws_stream, _) = ClientBuilder::from_uri(Uri::from_maybe_shared(ws_url).unwrap())
.connect()
.await?;

let stdin = tokio::io::stdin();
let mut stdin = BufReader::new(stdin).lines();

// Send the join message immediately
ws_stream
.send(Message::text(format!("/join {}", args.username)))
.await?;

loop {
tokio::select! {
incoming = ws_stream.next() => {
match incoming {
Some(Ok(msg)) => {
if let Some(text) = msg.as_text() {
println!("From server: {}", text);
}
},
Some(Err(err)) => return Err(err.into()),
None => return Ok(()),
}
}

res = stdin.next_line() => {
match res {
Ok(None) => return Ok(()),
Ok(Some(line)) => {
if line.starts_with("send ") {
let msg = line[5..].to_string();
ws_stream.send(Message::text(msg)).await?;
} else if line == "leave" {
ws_stream.send(Message::text("/leave".to_string())).await?;
return Ok(());
} else {
println!("Unknown command.");
}
}
Err(err) => return Err(err.into()),
}
}
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use futures_util::stream;
use tokio_websockets::Message;
use futures::*;

#[tokio::test]
async fn test_handle_server_messages() {
let messages = vec![
Message::text("INFO: Welcome to the chat"),
Message::text("ERROR: Username already taken"),
Message::text("Hello from another user"),
];

let ws_stream = stream::iter(messages);

let mut results = vec![];

ws_stream
.for_each(|msg| {
if let Some(text) = msg.as_text() {
if text.starts_with("ERROR:") {
results.push(format!("Error from server: {}", &text[7..]));
} else if text.starts_with("INFO:") {
results.push(format!("Info: {}", &text[6..]));
} else {
results.push(format!("From server: {}", text));
}
}
futures::future::ready(())
})
.await;

assert_eq!(results[0], "Info: Welcome to the chat");
assert_eq!(results[1], "Error from server: Username already taken");
assert_eq!(results[2], "From server: Hello from another user");
}
}
195 changes: 195 additions & 0 deletions src/bin/server.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@

use futures_util::sink::SinkExt;
use futures_util::stream::StreamExt;
use std::collections::HashMap;
use std::error::Error;
use std::net::SocketAddr;
use tokio::net::{TcpListener, TcpStream};
use tokio::sync::{broadcast::{channel, Sender}, Mutex};
use tokio_websockets::{Message, ServerBuilder, WebSocketStream};
use std::sync::Arc;
use actix_web::{web, App, HttpServer, Responder};
use serde::{Serialize, Deserialize};
extern crate actix_web;

//mod webutils;

use chat_app::webutils::{statsall, index, indexPost, User, ServerState};

async fn handle_connection(
addr: SocketAddr,
mut ws_stream: WebSocketStream<TcpStream>,
state: Arc<ServerState>,
) -> Result<(), Box<dyn Error + Send + Sync>> {
// Send a welcome message
ws_stream.send(Message::text("Welcome to chat! Type '/join <username>' to join.".to_string())).await?;

let mut bcast_rx = state.bcast_tx.subscribe();
let mut username: Option<String> = None;

loop {
tokio::select! {
// Handle incoming messages from the client
incoming = ws_stream.next() => {
match incoming {
Some(Ok(msg)) => {
if let Some(text) = msg.as_text() {
if text.starts_with("/join ") {
// Handle user joining
let new_username = text[6..].trim().to_string();
let mut users = state.users.lock().await;

//if users.values().any(|name| &name.0 == &new_username) {
if users.values().any(|name| &name.username == &new_username) {
ws_stream.send(Message::text("Username already taken.".to_string())).await?;
} else {
if users.contains_key(&addr) == true {
/*if let Some(tuple_ref) = users.get(&addr) {
if let Some(fe) = tuple_ref.as_ref().map(|t| &t.0)
&& let Some(se) = tuple_ref.as_ref().map(|t| &t.1) {
users.insert(addr, (fe,se+1));
}
}*/
/*if let Some(fv) = users.get(&addr).cloned().map(|t| &t.0){
let sname = fv;
if let Some(sv) = users.get(&addr).cloned().map(|t| &t.1){
let count = sv;
users.insert(addr, (sname.clone(),count+1));
}
}*/

if let Some(U) = users.get(&addr).cloned() {
//users.insert(addr, (sname.clone(), count + 1));
//users.insert(addr, User(sname.clone(), addr, count+1));
users.insert(addr, User{username: U.username, addr: addr, lifetime_cnt: U.lifetime_cnt+1});
}
/*users.entry(addr).
.and_modify(|entry| entry.1+=1)
.or_insert("default_name".to_string(),1);*/
//users.insert(addr, ((users.get(&addr)).as_ref().0,(users.get(&addr)).as_ref().1+1));
}
else {
users.insert(addr, User{username:new_username.clone(), addr: addr, lifetime_cnt: 0});
}
ws_stream.send(Message::text(format!("Joined as {}", new_username))).await?;
state.bcast_tx.send(format!("{} has joined the chat.", new_username))?;
username = Some(new_username);
}
} else if text == "/leave" {
// Handle user leaving
if let Some(name) = username.take() {
let mut users = state.users.lock().await;
users.remove(&addr);
state.bcast_tx.send(format!("{} has left the chat.", name))?;
return Ok(());
}
} else if let Some(_) = username {
// Broadcast regular messages
state.broadcast_message(&addr, text.into()).await;
} else {
ws_stream.send(Message::text("Please join with '/join <username>' first.".to_string())).await?;
}
}
}
Some(Err(err)) => return Err(err.into()),
None => return Ok(()),
}
}

// Handle messages from the broadcast channel
msg = bcast_rx.recv() => {
ws_stream.send(Message::text(msg?)).await?;
}
}
}
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
let (bcast_tx, _) = channel(16);
let state = Arc::new(ServerState {
users: Arc::new(Mutex::new(HashMap::new())),
bcast_tx: bcast_tx.clone(),
});

//web data processing
//let (web_sender, mut web_receiver) = mpsc::channel::<SwarmWebMessage>(100);

let shared_state = web::Data::new(state.clone());
//Web server
//Start the web server
let server = HttpServer::new(move || {
App::new()
//.app_data(received_data.clone())
.app_data(shared_state.clone())
//.app_data(swarm_controller.clone())
//.route("/", web::post().to(receive_data))
.route("/statsuser", web::get().to(index))
.route("/statsall", web::get().to(statsall))
.route("/updateuser", web::post().to(indexPost))
})
//.bind("127.0.0.1:8080")?
.bind("0.0.0.0:8080")?
.run();
//.await;

// Start the event loop
tokio::spawn(server);

let listener = TcpListener::bind("127.0.0.1:2000").await?;
println!("Listening on port 2000");

loop {
let (socket, addr) = listener.accept().await?;
println!("socket: {:?}", socket);
println!("address: {:?}", addr);
let conn_state = state.clone();

tokio::spawn(async move {
let ws_stream = ServerBuilder::new().accept(socket).await?;
handle_connection(addr, ws_stream, conn_state).await
});
}
}

#[cfg(test)]
mod tests {
use super::*;
use tokio::sync::broadcast;
use tokio_websockets::{Message, ServerBuilder};
use tokio::net::{TcpListener, TcpStream};
use std::net::SocketAddr;

#[tokio::test]
async fn test_user_join() {
let (bcast_tx, _) = broadcast::channel(16);
let state = Arc::new(ServerState {
users: Mutex::new(HashMap::new()),
bcast_tx: bcast_tx.clone(),
});

// Create a TCP listener for the server
let listener = TcpListener::bind("127.0.0.1:8080").await.unwrap();

// Simulate a client connecting to the server
tokio::spawn(async move {
let (client_socket, _) = listener.accept().await.unwrap();
let (mut ws_stream, _) = ServerBuilder::new().accept(client_socket).await.unwrap(); // Correctly destructuring the tuple
handle_connection("127.0.0.1:8080".parse().unwrap(), ws_stream, state.clone()).await.unwrap();
});

// Create a client-side TCP stream
let client_socket = TcpStream::connect("127.0.0.1:8080").await.unwrap();
let (mut client_ws_stream, _) = ServerBuilder::new().accept(client_socket).await.unwrap(); // Correctly destructuring the tuple

// Simulate client sending a message
let msg = Message::text("/join username");
client_ws_stream.send(msg).await.unwrap(); // Client sends a join message

// Simulate receiving the broadcasted message from the server
if let Some(Ok(received)) = client_ws_stream.next().await {
assert!(received.as_text().unwrap().contains("username has joined the chat"));
}
}
}

Loading