Il presente documento descrive il Progetto 2.1 – Ruggine: App di Chat Testuale, sviluppato per il corso di Programmazione di sistema.
L’obiettivo del progetto è la realizzazione di un’applicazione client/server per la gestione di una chat testuale, con funzionalità di iscrizione, creazione e gestione di gruppi, e monitoraggio delle prestazioni del server.
L’applicazione, chiamata Ruggine Chat, è stata progettata con particolare attenzione all’efficienza in termini di consumo di CPU e dimensioni dell’eseguibile, garantendo inoltre portabilità su più piattaforme.
- Manuel Marrone – S336919
- Edoardo Cecchini – S339169
- Antonio Ceglia – S349052
- Antonino Labate – S345224
La documentazione prodotta è articolata in più sezioni, ciascuna finalizzata a descrivere il progetto da punti di vista differenti: utilizzo, progettazione e verifica tramite test.
L’applicazione Ruggine Chat è una piattaforma di chat testuale progettata per lo scambio di messaggi all’interno di un’organizzazione, come ad esempio un’azienda o un’università.
Il sistema consente agli utenti di:
- Creare conversazioni individuali o di gruppo
- Inviare e ricevere messaggi in tempo reale
Sistema operativo
- Windows 10 o successivo
- macOS 12 o successivo
Browser supportati
- Chrome, Firefox, Edge, Safari (versioni aggiornate)
Connessione di rete
- Rete locale aziendale/universitaria/casalinga
Requisiti software
- Node.js: ≥ 18.18.0 (alcune dipendenze ESLint e Vite lo richiedono)
- npm: ≥ 9.x (incluso con Node 18)
- Rust: ≥ 1.63 (consigliato installare la versione LTS più recente, es. Rust 1.80+, tramite rustup)
- Scaricare l’applicazione dal repository ufficiale: https://github.com/PdS2425-C2/G53.git
- Se fornita come archivio
.zipo.tar.gz, estrarre il pacchetto - Il progetto è organizzato in due cartelle principali:
client/→ applicazione client (React + Vite)src/→ server API (Rust + Axum)
Frontend
cd client
npm installBackend
Non richiede installazioni manuali: Cargo scaricherà automaticamente le dipendenze indicate in Cargo.toml al primo cargo run.
Modalità sviluppo
Backend:
cd src
cargo runFrontend:
cd client
npm run devIl frontend sarà accessibile su http://localhost:5173 (porta Vite predefinita) e comunicherà con il backend su http://localhost:3000.
- Avviare sia backend che frontend come descritto nella sezione Installazione.
- Aprire il browser e collegarsi a:
- http://localhost:5173 (sviluppo)
- Inserire un username univoco (non sono ammessi duplicati).
-
Home
Panoramica delle funzioni principali: utenti attivi, notifiche ricevute, inviti, creazione chat. -
Utenti Attivi
Lista degli utenti attualmente connessi.
Ogni utente ha lo stato: Disponibile o Occupato. -
Chat di Gruppo
- Cliccare sul pulsante dedicato.
- Selezionare almeno 2 utenti e inviare un invito.
- Se uno degli invitati accetta, il creatore riceve una notifica.
- Tramite la notifica si accede alla chat di gruppo.
-
Chat Privata
- Cliccare sul pulsante dedicato.
- Selezionare un utente e inviare un invito.
- Se l’invitato accetta, il creatore riceve una notifica.
- Tramite la notifica si accede alla chat privata.
-
Logout
Al click sul pulsante Logout:- L’utente si disconnette da eventuali chat attive.
- L’account viene eliminato dalla sessione corrente.
- Non risulta più nella lista utenti connessi.
- Avviare l’applicazione (frontend + backend)
- Accedere con un username univoco
- Consultare la lista utenti attivi
- Avviare una chat privata o di gruppo
- Scambiare messaggi in tempo reale
- Abbandonare la chat al termine della conversazione
- Effettuare il logout oppure avviare una nuova chat
- L’app non si avvia → Controllare che sia frontend che backend siano avviati correttamente
- Non vedo altri utenti online → Verificare che ci siano effettivamente altre sessioni attive collegate
- Non ricevo notifiche di invito → Controllare che il backend sia in esecuzione (WebSocket attivo)
- Errore di accesso → Verificare di aver scelto un username non già in uso
- Connessione persa → Controllare la rete o eventuali firewall che bloccano la comunicazione tra frontend e backend
┌─────────────────────┐ ┌─────────────────────┐
│ │ │ │
│ FRONTEND │ │ BACKEND │
│ │ │ │
│ React + Vite │◄──────────────────►│ Rust + Axum │
│ │ WebSocket │ │
│ ┌───────────────┐ │ HTTP REST │ ┌───────────────┐ │
│ │ WebSocketChat │ │ │ │ WebSocket │ │
│ │ ChatInvites │ │ │ │ Handler │ │
│ │ ActiveUsers │ │ │ │ │ │
│ │ LoginForm │ │ │ │ Chat Manager │ │
│ └───────────────┘ │ │ │ │ │
│ ┌───────────────┐ │ │ │ User Manager │ │
│ │ WebSocket │ │ │ │ │ │
│ │ Context │ │ │ │ State Manager │ │
│ └───────────────┘ │ │ └───────────────┘ │
│ │ │ │
│ Port: 5173 │ │ Port: 3000 │
└─────────────────────┘ └─────────────────────┘
- Backend: Rust con Axum framework
- Frontend: React con Vite
- Comunicazione: WebSocket + HTTP REST API
- Serializzazione: JSON (serde)
- CORS: Abilitato per sviluppo cross-origin
package.json: definisce le dipendenze NPM necessarie al progetto.vite.config.js: contiene la configurazione di Vite per il build e lo sviluppo.
App.jsx: rappresenta il componente root dell’app React.main.jsx: è l’entry point che inizializza l’applicazione.index.css: raccoglie gli stili globali.
API.mjs: file che raccoglie le funzioni per la comunicazione REST con il backend.
Layout.jsx: definisce il layout principale dell’applicazione.Navbar.jsx: gestisce la barra di navigazione.Home.jsx: funge da dashboard per la creazione di nuove chat.- Funzionalità: selezione utenti disponibili, creazione chat private o di gruppo, invio inviti, interfaccia intuitiva per la scelta della modalità.
LoginForm.jsx: implementa il form di autenticazione.ActiveUsers.jsx: permette il monitoraggio degli utenti connessi.- Funzionalità: lista utenti online in tempo reale, indicatori di stato (disponibile/occupato), statistiche sulle connessioni, ordinamento con l’utente corrente in primo piano.
ChatInvites.jsx: gestisce il sistema di inviti alle chat.- Funzionalità: ricezione e gestione inviti, accettazione/rifiuto, notifiche chat pronte, navigazione automatica alla chat.
WebSocketChat.jsx: rappresenta l’interfaccia principale per le conversazioni.- Funzionalità: visualizzazione messaggi in tempo reale, invio messaggi, gestione chat private e di gruppo, tracking utenti in chat, notifiche di abbandono, indicatori “solo in chat”.
WebSocketContext.jsx: definisce il context per la gestione dei WebSocket, permettendo di condividere lo stato e le funzioni tra i componenti dell’app.
-
main.rs: Entry Point e orchestrazione- Inizializza e avvia il server Axum
- Configura le policy CORS per il frontend React
- Definisce tutti gli endpoint REST API (es. /api/login, /api/users)
- Configura l’endpoint WebSocket /ws
- Inizializza il sistema di tracking CPU
- Crea e distribuisce lo stato condiviso dell’applicazione
-
state.rs: Gestione dello stato condiviso- Strutture principali: ConnectedUser, AppState
- Gestisce utenti connessi, tracking CPU, inviti in attesa, conteggio utenti per chat attive, set delle chat private già avviate
- Dati accessibili thread-safe tramite Arc<Mutex<>>
-
websocket.rs: Comunicazioni real-time- Gestisce tutte le connessioni WebSocket e il routing dei messaggi
- Funzioni principali: websocket_handler, handle_socket, smistamento messaggi per MessageType
- Cleanup automatico alla disconnessione
-
chat.rs: Logica dei messaggi- Gestisce invio e broadcasting dei messaggi tra utenti
- Funzioni: broadcast_chat_message, broadcast_user_left
- Filtering intelligente basato su chat_id, integrazione monitoraggio CPU
-
user.rs: Gestione utenti e broadcasting generale- Funzioni: broadcast_user_joined, broadcast_user_status_changed, send_users_list, broadcast_to_all
-
invites.rs: Sistema inviti chat- Funzioni: send_chat_invite, handle_invite_response
- Routing intelligente per inviti privati o di gruppo, gestione session ID e notifiche “chat ready”
-
tracking.rs: Monitoraggio chat e utenti- Funzioni: init_chat_tracking, add_user_to_chat_tracking, remove_user_from_chat_tracking, check_and_notify_alone_in_chat
-
notifications.rs: Sistema notifiche- Funzioni: invalidate_chat_ready_notifications
-
routes.rs: Endpoint HTTP REST- GET /: health check
- GET /api/users: lista utenti connessi
- POST /api/login: validazione username
- POST /api/users/:username/availability: aggiornamento disponibilità
-
performance.rs: Monitoraggio performance- Funzione update_cpu_time
-
cpu_log.rs: Logging performance- Salvataggio asincrono su file ogni 2 minuti
-
types.rs: Definizioni tipi e strutture- User, ChatMessage, ChatInvite, ChatInviteResponse, WebSocketMessage, MessageType
Client Server
------ ------
| |
|--- WebSocket Connect ---------------->|
| |
|--- MessageType::Login --------------->|
| {username: "alice"} |
| |
| |-- Verifica unicità username
| |-- Genera session_id univoco
|<-- LoginSuccess/LoginError -----------|
| {session_id: "uuid-123"} |
|<-- UserJoined broadcast -------------|
|<-- UsersList aggiornata -------------|
- Controllo Duplicati: server verifica unicità username
- Generazione Session ID: UUID univoco per ogni connessione
- Registrazione Utente: memorizzato in AppState
- Broadcast: notifica a tutti gli utenti connessi
- Stato Globale Condiviso: AppState
- Eventi che Aggiornano la Lista: UserJoined, UserLeft, UserStatusChanged
Inviter (Alice) Server Invitee (Bob)
| | |
|--- ChatInvite ---->| |
| {target: "bob", chat_type: Private}|
| |--- ChatInvite ------>|
| | |
| |<-- ChatInviteResponse|
| | {accepted: true} |
|<-- ChatReady ------| |
| {chat_id, inviter, accepted_by} |
| |--- Sistema message -->|
|<-- Sistema message| "Bob è entrato" |
Sender Server Receiver
| | |
|--- ChatMessage ------>| |
| |--- Broadcast -------->|
|<-- Echo (if same chat)| |
- Chat Privata: 2 utenti, tracking abbandono definitivo
- Chat di Gruppo: ≥3 utenti, gestione inviti multipli e abbandoni
- Rust per backend: Zero-cost Abstractions, Memory Safety, Predictable Performance
- React + Vite per frontend: Startup rapido, HMR, build ottimizzate, architettura a componenti
- WebSocket per chat: comunicazione bidirezionale, messaggi istantanei, aggiornamenti presenza, gestione inviti e notifiche in tempo reale
- Performance e leggerezza
- Architettura Real-Time efficiente
- Scalabilità e robustezza
- Semplicità d’uso e manutenibilità
- Persistenza e storage in memoria
- Autenticazione basica username-only
- Sicurezza e privacy non implementate
- Persistenza su database
- Autenticazione completa e profili utenti
- Sicurezza avanzata (end-to-end encryption, rate limiting)
- Performance testing per N utenti simultanei
- Backend Rust: 2,02 MB
- Frontend bundle Vite: 0,88 MB
- Latenza media: 10 Byte 162.23 µs
- Latenza media: 1000 Byte 291.38 µs
- Latenza media: 10000 Byte 1416.41 µs
- Latenza media: 3404.77 µs
- Latenza minima: 3115 µs
- Latenza massima: 4894 µs
- Latenza media: 590.438.18 ms
- Latenza minima: 575.902 ms
- Latenza massima: 656.374 ms
- Latenza media: 5305.48 µs
- Latenza minima: 5051 µs
- Latenza massima: 5548 µs
- Latenza media: 711.18778 ms
- Latenza minima: 699.307 ms
- Latenza massima: 752.172 ms
- Stack Tecnologico: Rust + Axum + WebSocket (backend), React + Vite (frontend)
- Architettura del Sistema: stato condiviso thread-safe, sistema canali per messaging, protocollo WebSocket custom, tracking intelligente chat
- Approccio iterativo, milestone incrementali
- Divisione chiara responsabilità frontend/backend
- Pair programming e code review sistematiche
- Lezioni: Rust garantisce sicurezza, monitoraggio integrato facilita ottimizzazioni
- Miglioramenti: persistenza, scalabilità orizzontale, sicurezza, monitoring, implementazione CI/CD, rilevamento regressioni performance
Il progetto Ruggine Chat ha raggiunto tutti gli obiettivi prefissati, offrendo un sistema robusto, performante e maintainibile, con un team capace di affrontare contesti professionali reali.
Il progetto Ruggine Chat implementa test di integrazione end-to-end che verificano il funzionamento dei principali componenti del sistema in scenari realistici.
- Avvio di un server Axum reale (porta effimera)
- Connessione di client WebSocket multipli simulando il comportamento del frontend
- Scambio di messaggi JSON tra client e server
- Verifica della correttezza degli output ricevuti
- Test di integrazione backend con server WebSocket in-process
- Ogni test utilizza un server dedicato con stato isolato
- Connessioni client simulate tramite
tokio-tungstenite - Messaggi JSON inviati e ricevuti secondo protocollo di produzione
-
test_duplicate_login_rejected
- Obiettivo: verificare il rifiuto di login con username duplicato
- Scenario: due client tentano login con lo stesso username
- Verifica: il secondo client riceve
LoginError
-
test_invite_delivery_private
- Obiettivo: verificare l'invio e ricezione di inviti privati
- Scenario: Alice invia invito privato a Bob
- Verifica: Bob riceve l'invito con ID corretto
-
test_group_message_broadcast_to_all_members
- Obiettivo: verificare il broadcast di messaggi di gruppo
- Scenario: Alice, Bob, Carol in gruppo; Alice invia messaggio
- Verifica: Bob e Carol ricevono il messaggio
-
test_multiple_chats_correct_deliver
- Obiettivo: verificare l'isolamento tra chat diverse
- Scenario: due gruppi separati (Alice-Bob, Carol-Dave); Alice invia messaggio
- Verifica: Bob riceve il messaggio; Carol e Dave non ricevono il messaggio
- Inizializza un server Axum con stato isolato
- Bind su porta 0 per evitare conflitti
- Configurazione CORS minimale simulando il server di produzione
- Ritorna URL WebSocket e handle del task server
Vantaggi:
- Isolamento: ogni test ha server dedicato
- Realismo: server identico a quello reale
- Port-free: nessun conflitto di porte tra test
- Stabilisce connessione WebSocket al server di test
- Divide stream in lettura/scrittura per gestione asincrona
- Crea canale tipizzato per messaggi deserializzati
- Spawna task per conversione automatica di WsMessage in WebSocketMessage
Vantaggi:
- Tipizzazione: messaggi JSON automaticamente deserializzati
- Serializza payload del messaggio in formato WebSocket standard
- Wrappa in struttura
{ message_type, data } - Invia tramite WebSocket come frame testuale
Vantaggi:
- Protocollo: formato identico a client di produzione
- Semplicità: API semplificata per invio messaggi
- Riceve messaggi asincroni con predicato di filtraggio
- Timeout configurabile per prevenire deadlock
- Consuma messaggi non corrispondenti (drop semantics)
- Ritorna primo messaggio che soddisfa il predicato
Vantaggi:
- Predicati flessibili: closure per matching custom
- Resource management: cleanup automatico messaggi
- Attende conferma server che utente sia entrato in chat
- Verifica messaggi
UserStatusChangedcon stato corretto
Vantaggi:
- Sincronizzazione: evita race conditions
- File:
tests/chat_flow.rs - Tipologia: test di integrazione con server in-process (usano
create_appdella crate e WebSocket reali)
I test avviano un server Axum in-process su porta effimera, si connettono via WebSocket e verificano i flussi scambiando messaggi JSON come farebbe il client.
-
Rifiuto del login con nome duplicato
- Primo client:
Login→ attesoLoginSuccess - Secondo client stesso username → atteso
LoginError
- Primo client:
-
Invio e ricezione di un invito privato
aliceebobfannoLoginaliceinviaChatInvitedi tipoPrivate { target: "bob" }bobriceveChatInvitecon lo stessoid
-
Broadcast messaggio di gruppo
alice,bob,carolfannoLogin- Tutti inviano
UserStatusChangedconchatId = "group-1" aliceinviaChatMessagedi tipoGroupconchat_id = group-1bobecarolricevono il messaggio
-
Avvio server in-process:
- Creazione
AppStateeRouterconfullstack_app::create_app - TcpListener su porta effimera, server servito con
axum::servein un task - URL WS usato dai client:
ws://{addr}/ws
- Creazione
-
Connessione e I/O WebSocket:
- Client usano
tokio-tungsteniteper aprire connessione e leggere/scrivere frame - Helper:
connect_client(ws_url)apre connessione e inoltra messaggi JSONsend_ws(mt, payload)inviaWebSocketMessage { message_type, data }recv_until(pred, timeout)attende messaggio che soddisfa predicatowait_user_in_chat(u, chatId)attendeUserStatusChangedcoerente
- Client usano
Login duplicato
- C1 → Server:
Login { username: "mario" } - Server → C1:
LoginSuccess - C2 → Server:
Login { username: "mario" } - Server → C2:
LoginError
Invito privato
- A → Server:
Login { "alice" } - B → Server:
Login { "bob" } - A → Server:
ChatInvite { Private { target: "bob" }, id, chat_id, ... } - Server → B:
ChatInvite { id, ... }
Messaggio di gruppo
- A/B/C → Server:
Login - A/B/C → Server:
UserStatusChanged { chatId: "group-1", inChat: true } - A → Server:
ChatMessage { Group { members: [...] }, chat_id: "group-1" } - Server → B,C:
ChatMessage { ... }
- Runtime e I/O:
tokio,futures-util - WebSocket client:
tokio-tungstenite(dev-dependency) - Serializzazione/utility:
serde,serde_json,uuid,chrono - Server:
axum,tower-http(CORS)
Prerequisiti: Rust e Cargo installati
Comandi (PowerShell):
cargo test
cargo test --test chat_flow- I test avviano il server in-process su porta libera; non serve alcun server esterno
- I timeout sono conservativi per stabilità dei test


