Compare commits

...

206 Commits

Author SHA1 Message Date
Michael Bailey af011a48fe
Added expose commands to docker file (#27) 2024-09-19 11:51:41 +01:00
Michael Bailey f27a12b01f
Added AWS push action. (#26) 2024-09-12 00:04:43 +01:00
Michael Bailey 3e475656f0
Removed Clinet tests. (#25)
removed all tests in client folder, as they arent used
2024-09-09 20:54:57 +01:00
Michael Bailey bf80015145
Updated github action files to better complete CI tasks. (#24)
Updated Dockerfile to build a more correct,
2024-09-09 20:36:50 +01:00
Michael Bailey b8c83e7853
Update create-docker-image.yml 2024-09-09 17:53:26 +01:00
Michael Bailey 25582d2e55
Update rust.yml 2024-09-09 17:51:49 +01:00
Michael Bailey 313b115537
Made other changes to GRPC implementation to clean it up (#23)
* added protocol crate for the program, also removed unused crates

* remove client code

* removed example plugin

* cleaned up foundation, added protocol crate to it

* removed lua scripts

* removed server ctl

* migrated tp prost instead of protobuf crate

* removed scripting code

* moved protocol crate to foundation, created helper functions for encoding and decoding

* Createde Cursive client for testing protocol stuff

* added docs?

* overriding server to remove actix, and simplify design

* linting the server

* moved network message handling into NetworkConnection struct

* linting

* cleaning up prints

* writing better log messages

* added better debug messages

* purged all axtix modules, to be replaced with new components

* ok, so i kind of got carried away here with the server and client.

But essentially, the server has been rewritten to follow protobug up untill the point of connecting a client.

the client can fetch info from the server, but cannot initiate a request to connect.

I am weorkibng on both in tandem, and there will be losts of big commits :D

* removing client stuff again, cause it's not going well

* added client networking structs to the foundation crate

* moved client stuff into foundation.

* created Json structures

* created protobuf structures

* revamped the connection and protocolm structure to allow addition of other protocols

* fixed issue with erronious request input from clients

* removed select statements

* Updated logging messages
2024-09-09 17:48:09 +01:00
Michael Bailey c1e4a22099
Update rust master merge action workflow 2024-09-09 17:44:10 +01:00
Michael Bailey 0bfbd9c747
Grpc-manager (#22)
* added protocol crate for the program, also removed unused crates

* remove client code

* removed example plugin

* cleaned up foundation, added protocol crate to it

* removed lua scripts

* removed server ctl

* migrated tp prost instead of protobuf crate

* removed scripting code

* moved protocol crate to foundation, created helper functions for encoding and decoding

* Createde Cursive client for testing protocol stuff

* added docs?

* overriding server to remove actix, and simplify design

* linting the server

* moved network message handling into NetworkConnection struct

* linting

* cleaning up prints

* writing better log messages

* added better debug messages

* purged all axtix modules, to be replaced with new components

* ok, so i kind of got carried away here with the server and client.

But essentially, the server has been rewritten to follow protobug up untill the point of connecting a client.

the client can fetch info from the server, but cannot initiate a request to connect.

I am weorkibng on both in tandem, and there will be losts of big commits :D

* removing client stuff again, cause it's not going well

* added client networking structs to the foundation crate

* moved client stuff into foundation.

* created Json structures

* created protobuf structures

* revamped the connection and protocolm structure to allow addition of other protocols

* fixed issue with erronious request input from clients

* removed select statements
2024-05-30 20:42:42 +01:00
michael bailey e6905cb6b2
Adding client updates on addition and removal (#21)
* adding user update support

* Adding public key storage

* replaced duplicate fields

* changed messaegs to include a type property

* added encryption example

* Moved threads to tokio async

* Moved threads to tokio async

* Created global message support

* created basic ui that counts

* Created connection abstraction

This abstracts a TcpStream await to use any serialisable types.

* exposed server as lib

+created lib output for server
+ added extra dependencies
+ added functions to retrieve the port of a server

* updated connection

+ added manager trait
+ updated connection to use more idomatic rust code converted enums to Strigns rather that &str
+

* added network Manager

tl;dr, as the title says

* Update network_manager.rs

Converted over to new messaging system.

* meant to add this earlier

* Right bare with me...

This doesnt work for now but i have a system that works.

After this commit i will be resuming the smaller changes.

I needed to do this to find a new approach.

God save my mind.

* Impl IManager for ClientManager

* Added client manager tests

* Stripped server of functinoality

This is temporary whilst the client manager is being tested.

* Update client_manager.rs

swapped lock type for tokio lock

* Update client_manager.rs

- Added type arguments to Client manager
- deprecated start method

* Update client_manager.rs

- implemented handle channel method from old start method.
- added notes to possible issues.

* Update client_manager.rs

+ updated lifetime bounds.

* Update client_manager.rs

+ created function to get the number of connected clients

* Update client_manager.rs

+ created function to add/remove clients to the client manager.

* Update server.rs

- refactored server to simplify message passing

* Update client.rs

meant to add this earlier

* Update messages.rs

formatting

* moved some messages around

* Update server.rs

- removed redundant messages

* Update client_manager.rs

bunch more changes

yes i know i'm rubbish at this plz don't say anything :(

* Added Message Broadcasting functionality

* cleared up warnings

* Update client.rs

- removed redundant fields

* Update client.rs

added global broadcasting

* Added the ability to disconnect from the server

* Update network_manager.rs

this protects the network manager from crashing when a erroneous message is sent

* implemented message sending between users

* Update client.rs

added tokio, to protect client connections.

* Added Lua dependency

* Made server, Lua scriptable.

* added basic scripting abilities to the server

* Update client.rs

fixed stray connection

* made client Lua scriptable

* Made client manager Lua scriptable

* Adde fields to server to get client manager

* Updated testing script

* added client indexing to client manager

* added basic callback support to ClientManager

* moved Lua structs to separate module

* added arbitrary self types

* Created example plugin crate

* Added libloading dependency

* Created plugin trait and create function type

* created basic PluginManager

* Added plugin manager to server

* added modules

* Updated example plugin

* added plugin manager to server

* modified plugin module visibility

* updated example plugin

* updated plagin interface

* created plugin interface

* updated plugin init process

* updated example plugin

* cleaned up lib folder

* fixed cloning issues with plugin manager

* updated plugin trait implementation

* updated module definitions

* upadted rust fmt rules for imports

* fixoing formatting and ther errors. This is a pain to look through. i'm sorry :(

* minor fix

* Fixed plugin not functioning with tokio

* Added plugin lifecycle events

added lifecycle events to the plugin entry.

This allows plugins to be in a stopped paused or running state

* Adjusted visibility modifiers

* added basics of an event system

* updated imports.

* added function to get plugin entry as interface to plugin

* updated plugin example

* fixing linting errors

* updated event architecture

* renamed responder module

* added documention

* created a event result builder

* renamed responder

* updated example with interface setting

* moved event system to foundation

* added back tokio to foundation

* modified visibility

* added functions for IResponder

* more module mess

* made event generic

* made responder generic

* added basic plugin event propogation.

* updated plugin event handler.

* same because vscode?

* made plugin event generic

* RIGHT I'M MOVING TO ACTIX

* Started work on actor conversion

* implemented GetInfo for actix server

* implementing clients using actors

* added ability to add and remove clients

* updated foundation with comments and better messages

* Update Cargo.toml

+ added tokio stream

* added auto connection closing to Connection

* Update actix_server.rs

+ added ClientConnection handlers

* added ability to get server updates

* added basic messaging functonality

* deleted old server version

* renamed files to match std structure

* reformatted project

* renamed old files

* renamed serverActor to server

* added proper prelude

* Update client.rs

minor bug fixes

* updated clap

* made server configurable

* added port configurability

~ moved components to seperate modules
~ added builder and config to network manager

* way too much happened here

+ added scriptable version of the server, networkManager and clientManager

+ added lua engine creation
+also added unfinished rhai support
+ also did some restructuring

* made client manager and clients scriptable

* added more scriptable objects

* Created config manager with path read functionality

* fixed network manager message result types

* added bootstrapper actor

* moved arg matcher funtion.

* created singleton config manager using once pattern.

* removed bootstrapper and created config builder

* fixed panic on file not existing

* ignoring config_file

* added config manager to network manager

* updated some docs

* added config support to network manager, changed lifecycle a bit as well

* removed redundant handlers

* added configuration through args support to config manager and network manager.

* added config manager support to server and removed old serverConfig references

* performed cleanup of network manager

* performed cleanup of server

* updated config manager api to use optionals. This leads to pre-configuration and easier readability and understanding.

* updated server and network manager to new style for setting config values

* cleaned up prelude linting errors

* cleaned up network listener

* cleaned up connection initator

* cleaned up connection actor

* cleaned up lua manager

* cleaned up rhai manager

* cleaned up config manager

* cleaned up client manager

* cleaned up client actor

* cleaned up scriptable interfaces

* updated client uuid version

* fixed client cargo toml

* undone previous change

* moved arg parser to folder as file

* uhh... changes and formatting

* created chat manager and added it to the client manager

* added global message spport to client manager

* refixed network manager (again again)

* added debug messages

* refactord client code

* refactored messages and added new message types

* refactord network manager module name

* resolved warning in connection

* refactored message into foundation, added get message support for clients

* Fixed messages being sent to sender

* fixed not implemented panic, and field misnaming

* turned some references into weak variaties, to prevent memory leaks.

* turned ref into weak ref

* removing  strong references from scriping managers

* fixing lints

* making moe things weak repferences

* refactored connection initiator into spereate files

* accedentally ran cargo fmt instead of on one file

* removed the read loop, and replaced it with a recurrent messages.

* added docker files and automated build scripts

* Added client updates for addition and removal of clients
2024-01-03 23:35:26 +00:00
michael bailey ddb886df03
merge develop into master (#20)
* adding user update support

* Adding public key storage

* replaced duplicate fields

* changed messaegs to include a type property

* added encryption example

* Moved threads to tokio async

* Moved threads to tokio async

* Created global message support

* created basic ui that counts

* Created connection abstraction

This abstracts a TcpStream await to use any serialisable types.

* exposed server as lib

+created lib output for server
+ added extra dependencies
+ added functions to retrieve the port of a server

* updated connection

+ added manager trait
+ updated connection to use more idomatic rust code converted enums to Strigns rather that &str
+

* added network Manager

tl;dr, as the title says

* Update network_manager.rs

Converted over to new messaging system.

* meant to add this earlier

* Right bare with me...

This doesnt work for now but i have a system that works.

After this commit i will be resuming the smaller changes.

I needed to do this to find a new approach.

God save my mind.

* Impl IManager for ClientManager

* Added client manager tests

* Stripped server of functinoality

This is temporary whilst the client manager is being tested.

* Update client_manager.rs

swapped lock type for tokio lock

* Update client_manager.rs

- Added type arguments to Client manager
- deprecated start method

* Update client_manager.rs

- implemented handle channel method from old start method.
- added notes to possible issues.

* Update client_manager.rs

+ updated lifetime bounds.

* Update client_manager.rs

+ created function to get the number of connected clients

* Update client_manager.rs

+ created function to add/remove clients to the client manager.

* Update server.rs

- refactored server to simplify message passing

* Update client.rs

meant to add this earlier

* Update messages.rs

formatting

* moved some messages around

* Update server.rs

- removed redundant messages

* Update client_manager.rs

bunch more changes

yes i know i'm rubbish at this plz don't say anything :(

* Added Message Broadcasting functionality

* cleared up warnings

* Update client.rs

- removed redundant fields

* Update client.rs

added global broadcasting

* Added the ability to disconnect from the server

* Update network_manager.rs

this protects the network manager from crashing when a erroneous message is sent

* implemented message sending between users

* Update client.rs

added tokio, to protect client connections.

* Added Lua dependency

* Made server, Lua scriptable.

* added basic scripting abilities to the server

* Update client.rs

fixed stray connection

* made client Lua scriptable

* Made client manager Lua scriptable

* Adde fields to server to get client manager

* Updated testing script

* added client indexing to client manager

* added basic callback support to ClientManager

* moved Lua structs to separate module

* added arbitrary self types

* Created example plugin crate

* Added libloading dependency

* Created plugin trait and create function type

* created basic PluginManager

* Added plugin manager to server

* added modules

* Updated example plugin

* added plugin manager to server

* modified plugin module visibility

* updated example plugin

* updated plagin interface

* created plugin interface

* updated plugin init process

* updated example plugin

* cleaned up lib folder

* fixed cloning issues with plugin manager

* updated plugin trait implementation

* updated module definitions

* upadted rust fmt rules for imports

* fixoing formatting and ther errors. This is a pain to look through. i'm sorry :(

* minor fix

* Fixed plugin not functioning with tokio

* Added plugin lifecycle events

added lifecycle events to the plugin entry.

This allows plugins to be in a stopped paused or running state

* Adjusted visibility modifiers

* added basics of an event system

* updated imports.

* added function to get plugin entry as interface to plugin

* updated plugin example

* fixing linting errors

* updated event architecture

* renamed responder module

* added documention

* created a event result builder

* renamed responder

* updated example with interface setting

* moved event system to foundation

* added back tokio to foundation

* modified visibility

* added functions for IResponder

* more module mess

* made event generic

* made responder generic

* added basic plugin event propogation.

* updated plugin event handler.

* same because vscode?

* made plugin event generic

* RIGHT I'M MOVING TO ACTIX

* Started work on actor conversion

* implemented GetInfo for actix server

* implementing clients using actors

* added ability to add and remove clients

* updated foundation with comments and better messages

* Update Cargo.toml

+ added tokio stream

* added auto connection closing to Connection

* Update actix_server.rs

+ added ClientConnection handlers

* added ability to get server updates

* added basic messaging functonality

* deleted old server version

* renamed files to match std structure

* reformatted project

* renamed old files

* renamed serverActor to server

* added proper prelude

* Update client.rs

minor bug fixes

* updated clap

* made server configurable

* added port configurability

~ moved components to seperate modules
~ added builder and config to network manager

* way too much happened here

+ added scriptable version of the server, networkManager and clientManager

+ added lua engine creation
+also added unfinished rhai support
+ also did some restructuring

* made client manager and clients scriptable

* added more scriptable objects

* Created config manager with path read functionality

* fixed network manager message result types

* added bootstrapper actor

* moved arg matcher funtion.

* created singleton config manager using once pattern.

* removed bootstrapper and created config builder

* fixed panic on file not existing

* ignoring config_file

* added config manager to network manager

* updated some docs

* added config support to network manager, changed lifecycle a bit as well

* removed redundant handlers

* added configuration through args support to config manager and network manager.

* added config manager support to server and removed old serverConfig references

* performed cleanup of network manager

* performed cleanup of server

* updated config manager api to use optionals. This leads to pre-configuration and easier readability and understanding.

* updated server and network manager to new style for setting config values

* cleaned up prelude linting errors

* cleaned up network listener

* cleaned up connection initator

* cleaned up connection actor

* cleaned up lua manager

* cleaned up rhai manager

* cleaned up config manager

* cleaned up client manager

* cleaned up client actor

* cleaned up scriptable interfaces

* updated client uuid version

* fixed client cargo toml

* undone previous change

* moved arg parser to folder as file

* uhh... changes and formatting

* created chat manager and added it to the client manager

* added global message spport to client manager

* refixed network manager (again again)

* added debug messages

* refactord client code

* refactored messages and added new message types

* refactord network manager module name

* resolved warning in connection

* refactored message into foundation, added get message support for clients

* Fixed messages being sent to sender

* fixed not implemented panic, and field misnaming

* turned some references into weak variaties, to prevent memory leaks.

* turned ref into weak ref

* removing  strong references from scriping managers

* fixing lints

* making moe things weak repferences

* refactored connection initiator into spereate files

* accedentally ran cargo fmt instead of on one file

* removed the read loop, and replaced it with a recurrent messages.

* added docker files and automated build scripts
2023-12-01 21:48:28 +00:00
michael bailey 596dd0db05
Update README.md (#16)
* Update README.md

+ added feature, todo and goals section.

* Update README.md
2021-08-03 21:54:04 +01:00
Mitch161 2caff23ff2
Merge pull request #10 from Mitch161/Development
Development into Master
2021-08-03 21:53:15 +01:00
michael bailey f43ceb07df
Create rust.yml (#3)
+ added a workflow file for CI
2021-04-13 21:54:20 +01:00
michael bailey 5aa4f8caf6
Pulling basic server functionality into development (#9)
* removed redundant files

* moved files to new foundation library

* added new foundation crate

* added new client program crate

* added new server program crate

* added new serverctl program crate

* change toml to be a workspace instead of a project

* implementing more connection to network functionality

* implementing more connection to network functionality

* Implemented IMessageable for client, client manager as well as basic commands in netmgr

* fixing blocking issues with componenets

* adding network stream queuefor handling connections in a non blocking way

* ffixing blocking calls in network manager

* adding threading support to prevent blocking calls

* running rust formatter

* Created Client threads and implemented connect command

* fixed client not disconnecting issue

* adding messaging support between clients

* Implemented client messaging through the server

* removing unnecessary prints and thread delays

* adding support for updating clients
2021-04-13 18:17:58 +01:00
michael bailey 0572d0d0e9
Merge pull request #8 from Mitch161:consolidation-branch
Consolidation branch -> development branch
2021-03-30 07:29:41 +01:00
michael-bailey cf16367d51 removing dead code 2021-03-20 12:38:42 +00:00
michael-bailey 8f3d1549ca fixed issue with channel blocking 2021-03-18 23:37:48 +00:00
michael-bailey 53ff1858f6 implemented client manager tick function 2021-03-18 23:24:48 +00:00
michael-bailey d6c4baf556 Implemented client add functionality in the server. 2021-03-18 20:02:56 +00:00
michael-bailey f3ab1f37da Added client connecting condition to network manager 2021-03-18 19:55:04 +00:00
michael-bailey 30d9e5ba2d modified client to multiple params instead of map 2021-03-18 17:03:15 +00:00
michael-bailey 7d749d0de0 Moved TcpListender form Server To NetworkManage. 2021-03-18 16:52:03 +00:00
michael-bailey 436975e3c5 Moved TcpListender form Server To NetworkManage. 2021-03-18 16:42:42 +00:00
michael-bailey a5eff0bd32 merge server implementation branch 2021-03-18 16:05:27 +00:00
michael-bailey bb5fbdc43b removed redundant function 2021-03-18 15:57:34 +00:00
michael-bailey b53a63fd54 setting up json based message protocol 2021-03-18 11:50:12 +00:00
michael-bailey 19832f0aa1 added basic server messages. 2021-03-18 10:39:11 +00:00
michael-bailey 232effad14 implemented basic connection handling in the server 2021-03-18 10:39:11 +00:00
michael-bailey a8c37225ae implemented basic client manager functions for new architecture 2021-03-18 10:39:11 +00:00
michael-bailey 22a0d68255 implemented basic messageing system for clients. 2021-03-18 10:38:46 +00:00
michael-bailey cd81b1e250 redesigned client for the new exec structure 2021-03-18 10:38:46 +00:00
michael-bailey db72977d2f fixing owner structure. 2021-03-18 10:38:46 +00:00
michael-bailey 3ff3e531a1 moving server changes to client manager 2021-03-18 10:38:46 +00:00
michael-bailey 5b9d91e44e implemented basic connection handling in the server 2021-03-18 10:38:46 +00:00
michael-bailey 1966f80bc6 moving server changes to client manager 2021-03-18 10:38:09 +00:00
michael-bailey 546e566c9b Fixed immediate warnings 2021-03-18 10:37:47 +00:00
michael-bailey 965231cde9 added basic network manager implementation 2021-03-18 10:36:18 +00:00
michael-bailey 042f7e1007 added basic server messages. 2021-03-18 09:42:53 +00:00
michael-bailey b1be92ed02 implemented basic connection handling in the server 2021-03-18 09:26:49 +00:00
michael-bailey b1b8107ce7 implemented basic client manager functions for new architecture 2021-03-18 09:20:39 +00:00
michael-bailey e92096b6ad implemented basic messageing system for clients. 2021-03-18 08:55:21 +00:00
michael-bailey a493eddd50 redesigned client for the new exec structure 2021-03-17 18:37:06 +00:00
michael-bailey 194e954733 fixing owner structure. 2021-03-17 10:54:51 +00:00
michael-bailey f00d8cc7f7 moving server changes to client manager 2021-03-17 10:54:51 +00:00
michael-bailey 5f6ecdd839 added implemetation of info command on te server 2021-03-17 10:54:51 +00:00
michael-bailey 14154ebd4e implemented basic connection handling in the server 2021-03-17 10:54:51 +00:00
michael-bailey 5b7258d3ab added from vector trait to commands 2021-03-17 10:53:43 +00:00
michael-bailey 9b5cb18693 moved commands into lib module 2021-03-17 10:53:43 +00:00
michael-bailey b66fdd62b3 added implemetation of info command on te server 2021-03-17 09:56:51 +00:00
michael-bailey 71fe467ca2 implemented basic connection handling in the server 2021-03-16 10:11:31 +00:00
michael-bailey 6c89f34151 added from vector trait to commands 2021-03-16 10:11:03 +00:00
michael-bailey ca4b0259b9 moved commands into lib module 2021-03-16 09:41:35 +00:00
michael-bailey 585926ebed moving server changes to client manager 2021-03-15 17:21:23 +00:00
michael-bailey 5e49056992 Fixed immediate warnings 2021-03-15 17:18:39 +00:00
michael-bailey 962293b32f added basic implementations for server 2021-03-15 17:11:50 +00:00
michael-bailey 4c7c68c2a5 implementing common traits for server 2021-03-15 16:51:32 +00:00
michael-bailey 7731e18d8b implemeting client functionality and changed types 2021-03-12 17:17:48 +00:00
michael-bailey f4bd223d12 Created traits for struct ownership and impl them for client|manager 2021-03-12 14:31:34 +00:00
michael-bailey 9de6969eb8 Implementing core Client Manager methods 2021-03-11 20:22:20 +00:00
michael-bailey 3591318270 Fixed immediate warnings 2021-03-11 20:22:20 +00:00
michael-bailey e082971df0 Fixed immediate warnings 2021-03-11 14:06:05 +00:00
michael-bailey 1ecccf6d67 implemented eq and ord fror client 2021-03-11 13:25:37 +00:00
michael-bailey 96450d5da2 Update mod.rs
+ added self referenceing to teh client manager with a weak reference

+ added channels to the client manager
2021-02-08 21:12:10 +00:00
michael-bailey 8df5ac848b tidying up client struct 2021-02-08 21:12:10 +00:00
michael-bailey 87a2a6b6da Moving files around and implementing more of the client manager. 2021-02-08 21:12:10 +00:00
michael-bailey f8a1364645 Update server v3 tests 2021-02-08 21:12:10 +00:00
michael-bailey b0ed33b5c7 Update main_menu.rs
fixed accidental renaming
2021-02-08 21:12:10 +00:00
michael-bailey d1fce2060c adding documentation 2021-02-08 21:12:10 +00:00
michael-bailey 9a22addbb9 Update server_view_controller.rs 2021-02-08 21:12:10 +00:00
michael-bailey 3fb7c345ba Update server_v3.rs
added unit tests
2021-02-08 21:12:10 +00:00
michael-bailey 5b60f9282d Update main_menu.rs
renamed the main menu function
2021-02-08 21:12:10 +00:00
michael-bailey 13dbf4050f Update control_panel.rs 2021-02-08 21:12:10 +00:00
michael-bailey 9b5912f9eb Update control_panel.rs
fixed issue where i didnt put anything into a child function call
2021-02-08 21:12:10 +00:00
michael-bailey 28694107d4 Update client_v3.rs
removed redundant error clause in handle connection
2021-02-08 21:12:10 +00:00
michael-bailey 0dc93ba9ce Update server_v3.rs
fixed unused variable warning
2021-02-08 21:12:10 +00:00
michael-bailey ccb29581fb Update server_v3.rs
fixed reference issue with options
2021-02-08 21:12:10 +00:00
michael-bailey cae3a81cfb Update server_v3.rs
fixed the start function
2021-02-08 21:12:10 +00:00
michael-bailey b98ce05d32 Update server_v3.rs
renamed serverstate states
2021-02-08 21:12:10 +00:00
michael-bailey 72f0ca0139 added eq and PartialEq support to serverState 2021-02-08 21:12:10 +00:00
michael-bailey 1faacb9223 optimised imports 2021-02-08 21:12:10 +00:00
michael-bailey 285015f3f7 fixed warning on unused result 2021-02-08 21:12:10 +00:00
michael-bailey 0b36d04387 renamed functions 2021-02-08 21:12:10 +00:00
michael-bailey 157f76838b Update main.rs
renamed Server view controller
2021-02-08 21:12:10 +00:00
michael-bailey a7de2887d4 Update mod.rs 2021-02-08 21:12:10 +00:00
michael-bailey ef0ee61965 removed unessesery function 2021-02-08 21:12:10 +00:00
michael-bailey 864866d65b Allowing dead code 2021-02-08 21:12:10 +00:00
michael-bailey 8bd9303f5c Update main.rs
removing useless macros
2021-02-08 21:12:10 +00:00
michael-bailey abb4bfaa3e optimising imports
optimising imports
2021-02-08 21:12:10 +00:00
michael-bailey fd8346727b optimising imports 2021-02-08 21:12:10 +00:00
michael-bailey 1ac65001b4 Update main.rs
removed redundent and useless ui code
2021-02-08 21:12:10 +00:00
michael-bailey 5243a6ce8e renamed the about panel. 2021-02-08 21:12:10 +00:00
michael-bailey b3174cc488 Update server_view_controller.rs
changed the name of the ServercontrolView
2021-02-08 21:12:10 +00:00
michael-bailey 4deabcd1fc Update ServerV3.rs
removed use statement
2021-02-08 21:12:10 +00:00
michael-bailey ff97058aa9 Update mod.rs
added the new client to the module tree
2021-02-08 21:12:10 +00:00
michael-bailey b5194fce23 moving menu bar to another file 2021-02-08 21:12:10 +00:00
michael-bailey 89bf41861f added module file for ui 2021-02-08 21:12:10 +00:00
michael-bailey ab23ff5a99 updated server view controller
forgot the imports
2021-02-08 21:12:10 +00:00
michael-bailey face6f935d Create server_view_controller.rs
added a server view controller that holds the display and enables events

it uses views from other modules to create the ui tree.

the cursive user_data store is used to hiold the server
2021-02-08 21:12:10 +00:00
michael-bailey 8831413e00 Update server_profile.rs
changed the function in the update list handler
2021-02-08 21:12:10 +00:00
michael-bailey f2c87bacb3 Update server_profile.rs
removed extern crate uses
2021-02-08 21:12:10 +00:00
michael-bailey e8fc561894 Update server_profile.rs 2021-02-08 21:12:10 +00:00
michael-bailey 5473b2255d Update server_profile.rs
removed unused imports
2021-02-08 21:12:10 +00:00
michael-bailey d74ed68206 Update server_profile.rs
updated to account for the new threadpool location
2021-02-08 21:12:10 +00:00
michael-bailey 4501887b14 Update server_profile.rs
removed get client list
2021-02-08 21:12:10 +00:00
michael-bailey c233138ca6 Update server_profile.rs
added spaces and other changes
2021-02-08 21:12:10 +00:00
michael-bailey 8aa499ab03 Update server_profile.rs
removed the need for a arc for server properties
2021-02-08 21:12:10 +00:00
michael-bailey 659a26b6a8 Update mod.rs
added new modules for the new ui and server.
2021-02-08 21:12:10 +00:00
michael-bailey 143848f05c Update main.rs
extracting the ui from the main function
2021-02-08 21:12:10 +00:00
michael-bailey 800aedd9d8 Create control_panel.rs
creating a control panel for the server
2021-02-08 21:12:10 +00:00
michael-bailey e5b439bb57 Create About_Panel.rs
added an about layer function that returns a view
2021-02-08 21:12:10 +00:00
michael-bailey 49d3afd11f Update client_profile.rs
set the channels t use crossbeam_channels
2021-02-08 21:12:10 +00:00
michael-bailey 638b1ac969 created server version 3
this works on the face that a ui framework will have an event loop. so changes include:
+ start sets up the listener and allows the server to ba called
+ stop sets the server to disconnect other users and close the listener
+ tick should be called by the event loop this will allow any new connections to be handled, any pending
2021-02-08 21:12:10 +00:00
michael-bailey ad2c83c01c removed dashmap form commands 2021-02-08 21:12:10 +00:00
michael-bailey 749ddac360 Update LICENSE 2021-02-08 21:12:10 +00:00
michael-bailey b3de7670f3 got running flag to display 2021-02-08 21:12:10 +00:00
michael-bailey c4d3270026 Update client_profile.rs 2020-08-26 11:48:56 +01:00
michael-bailey 4d6a11e2be Update main.rs
experiments with openssl
2020-08-26 11:48:47 +01:00
michael-bailey 28622307af Update .gitignore 2020-08-26 11:48:35 +01:00
michael-bailey 0c28bfa7f2 Update .gitignore
+ added .cer files to ignore for obvious reasons
2020-08-24 15:27:46 +01:00
michael-bailey 48b7edd532 Update main.rs
+ added example for rsa
2020-08-23 23:57:46 +01:00
michael-bailey 7663a3f4ab Update Cargo.toml
+ openssl for crypto
- lazy-static and async-std
2020-08-23 15:36:14 +01:00
michael-bailey 52fcd65a73 removed the static crate 2020-08-23 15:35:41 +01:00
michael-bailey acddcd17a8 removed static requirement
- removed lifetimes

~ changed server properties to arc<strings>
- removed update_all_clients
~ changed read_data and transmit data to static functions
- removed all references to self.
+ added cloned arc's for each property that are moved into the thread.
2020-08-22 19:22:28 +01:00
Mitchell f3910e9515 server has been updated to v2 system 2020-08-19 22:33:03 +01:00
Mitch161 dc54a5d952
Merge pull request #4 from Mitch161/cmd--optimization
ref-method and command merge
2020-08-19 22:29:43 +01:00
Mitchell b8440e8290 all basic command functionality added
allows for all commands from server side and client side to be tested correctly
2020-08-19 22:25:27 +01:00
Mitchell 81c5949de3 new changes from ref-method branch 2020-08-19 22:24:29 +01:00
Mitchell 573a625a04 new client api from ref-method branch 2020-08-19 22:24:10 +01:00
Mitchell 135a497f18 merge with ref-method changes 2020-08-19 22:17:49 +01:00
Mitchell c49bfb281e removed dead code tags to enum types 2020-08-18 13:27:29 +01:00
Mitchell c7edea3984 new commands system with branching removed 2020-08-18 13:00:37 +01:00
Mitchell 68b3ebf74b extra read was causing data to be lost 2020-08-16 18:30:00 +01:00
Mitchell b393223bfa server info unit test fix 2020-08-16 18:29:35 +01:00
Mitchell 399b9b8c59 changed enum layout 2020-08-16 17:15:34 +01:00
Mitchell f24a4f72df functionality for individual commands 2020-08-16 17:15:23 +01:00
michael-bailey b33c250d08 Merge branch 'ref-method' of https://github.com/mitch161/rust-chat-server into ref-method 2020-08-16 17:15:18 +01:00
michael-bailey ca3e559353 Update mod.rs
removed old commands
2020-08-16 17:13:53 +01:00
michael-bailey c1236bdcd3 fixed bug with the 1 2020-08-16 17:13:41 +01:00
michael-bailey ea98cc7688 removed old commands files. 2020-08-16 17:13:28 +01:00
michael-bailey 2837ab52e1 Update Cargo.toml
- removed machine learning lib
2020-08-16 17:12:50 +01:00
Mitchell b4a49918fc duplicated folder 2020-08-16 12:38:54 +01:00
Mitchell 5e1ba2e110 removed extra brace 2020-08-15 22:08:34 +01:00
michael-bailey dbf49a65b2 Revert "Merge branch 'master' into ref-method"
This reverts commit 1a6b344b58, reversing
changes made to be167055e8.
2020-08-12 20:56:05 +01:00
michael-bailey 1a6b344b58 Merge branch 'master' into ref-method 2020-08-12 17:24:12 +01:00
michael-bailey be167055e8 connecting and disconnecting works 2020-08-12 16:44:24 +01:00
michael-bailey b1ad04ed50 Update server_profile.rs
added Dissconnect method
2020-08-11 23:36:27 +01:00
michael-bailey 593fbc96ed Update client_profile.rs
removing the transmit method
2020-08-11 23:36:05 +01:00
michael-bailey dfcc3fbedc Update main.rs
~ hindered the main thread when in non gui mode to lowwer resources
2020-08-11 23:35:47 +01:00
michael-bailey 5057cec283 modified the command to use a trait to parse &str 2020-08-11 23:35:00 +01:00
Mitchell 82498ca72d removed commands module 2020-08-10 18:56:17 +01:00
Mitchell 0b824efdb8 added Debug trait 2020-08-10 18:55:27 +01:00
Mitchell 7a25bd8b6e merged with old commands files 2020-08-10 18:55:06 +01:00
Mitchell 1f3745c0b1 commented out unit test 2020-08-10 18:54:47 +01:00
Mitchell fa57f25d8a added core server funcs to ui 2020-08-10 18:53:53 +01:00
michael-bailey 03ab7290c5 added functions 2020-08-09 21:17:17 +01:00
michael-bailey ef86b89042 added proper definition of traits for classes 2020-08-09 11:36:14 +01:00
michael-bailey a1474d01de increment version
+ clap crate
~ attempting fix on getting info
+ added clap arg parsing
+ added rudementary client, remove and update and error
2020-08-07 23:48:05 +01:00
Mitch161 6d3ffc5ae5
Merge pull request #2 from Mitch161/channel-structure
Channel structure merge
2020-08-06 12:46:26 +01:00
michael-bailey 164542a56b changed ui 2020-08-05 16:30:13 +01:00
michael-bailey 20afff2931 Update main.rs
+ added a ui using cursive.
+ moved server star and stop into the ui.
+ added specific functions for constructing the ui
2020-08-03 12:43:53 +01:00
michael-bailey 22ed3db6bb Create mod.rs
+ started wok on a client api struct.
2020-08-03 12:43:03 +01:00
michael-bailey 46a6b30681 Update client_profile.rs
+ derived debug
+ added senders for it's thread.
+ added channel sender for the server.
+ working on heartbeat.
~ changed the handle to be a single function call invoked by the server
2020-08-03 12:42:42 +01:00
michael-bailey d56a7209c2 Update server_profile.rs
+ added thread spawning.
+added channels for server communication outside of the thread.
+ added main loop process.
~ changes the tcp listener to be non blocking.
+ added loop labels to break out of.
+ impl drop for the server that signals the thread to break.
2020-08-03 12:39:50 +01:00
michael-bailey f05323361c Create mod.rs
~ coverted Commands to commands-v2
2020-08-03 12:37:30 +01:00
michael-bailey 743433ab57 Update Cargo.toml
+ added cursive, crossterm, zeroize, and rayon.
2020-08-03 12:36:55 +01:00
Mitchell 55e4935105 changed stream timeout value to 500 milli secs 2020-08-01 13:36:56 +01:00
Mitchell a63fb3019c added fix to server and unit tests 2020-08-01 12:42:54 +01:00
Mitchell d26b2676fb removed old outdated code
the code was commented out
2020-08-01 12:42:19 +01:00
Mitchell 8c2fbe17a1 added comment on stream reading issue 2020-08-01 12:41:39 +01:00
Mitchell fc6e87c8bf added bug fixes from unit tests 2020-07-31 20:22:05 +01:00
Mitchell 5ced20099a fixed thread crash due to hashmap logic
the client thread would crash if it attempted to update all clients when only it existed in the hashmap
2020-07-31 18:38:30 +01:00
Mitchell d91c9bfd3b fixed thread crash due to hashmap logic
the client thread would crash if it attempted to update all clients when only it existed in the hashmap
2020-07-31 18:36:30 +01:00
Mitchell 7edc209fc8 added unit tests for basic commands 2020-07-31 17:26:49 +01:00
Mitchell 951ddeb754 fixed bug with map size comparision
when comparing the size of the hashmap that contains the params, if the size was 1, the params would be deleted. Changed to > 0.
2020-07-31 17:26:32 +01:00
Mitchell 1e4b84787a added reading data function 2020-07-31 17:23:51 +01:00
Mitchell 3c065781ea added unit tests for connect and info cmd 2020-07-26 13:31:21 +01:00
Mitchell 72d297eca6 added Success command in 'to_string()' 2020-07-26 13:30:56 +01:00
Mitchell 28a8ec2302 changed msg processing from string to buffer 2020-07-24 12:39:30 +01:00
Mitchell 646163b3e3 added deprecated attribute to unused methods 2020-07-24 12:22:32 +01:00
Mitchell 0ab49de27f fixed minor warning issues 2020-07-24 11:41:47 +01:00
Mitchell 6f660517da created new from imple for buffer 2020-07-24 11:41:19 +01:00
Mitchell bd268d5a96 added ClientInfo, ClientUpdate functionality 2020-07-24 11:40:54 +01:00
Mitchell 4377b26d5f corrected constant format 2020-07-24 11:40:17 +01:00
michael-bailey 4cd27856ba Update main.rs 2020-07-21 16:55:06 +01:00
michael-bailey d34c9580bd Merge branch 'channel-structure' of https://github.com/mitch161/rust-chat-server into channel-structure 2020-07-21 16:55:03 +01:00
Mitch161 9dff749432 added Client command for 'to_string' conversion
no longer returns !error: back to the user when the client command needs to be returned from the server
2020-07-20 20:08:51 +01:00
Mitch161 cfe48835ac added back client thread pool 2020-07-20 20:05:22 +01:00
Mitch161 52e97bbaad added manual unlock of client hashmap 2020-07-20 19:58:41 +01:00
Mitch161 21cb46e4fb added Request command to 'to_string' conversion 2020-07-20 19:40:54 +01:00
Mitch161 ac9a337f35 commented unused and misplaced line 2020-07-20 19:40:16 +01:00
Mitch161 f399386906 added note on transmit data bug
refers to a server crash issue
2020-07-20 18:21:22 +01:00
Mitch161 2827ab5c5e constructor now returns Self
changed the return of the constructor from type Server to type Self
2020-07-19 21:07:23 +01:00
Mitch161 700c093c6b made server and details statics/consts 2020-07-19 21:05:09 +01:00
Mitch161 0e6d141704 added new crate for statics 2020-07-19 21:04:41 +01:00
Mitch161 f7b827b15d removed unused imports 2020-07-19 18:04:22 +01:00
Mitch161 240f8fa671 made self static 2020-07-19 17:55:54 +01:00
Mitch161 73b732fbe0 made Server static 2020-07-19 17:55:34 +01:00
Mitch161 7ed9e469fc changed String to 'static str
The server details shouldnt be able to be change and should exist for the whole execution of the program, this can be changed by reverting back to the old format
2020-07-19 17:55:10 +01:00
Mitch161 f4b9bdbb51 changed self pointer to static 2020-07-18 20:36:59 +01:00
Mitch161 9d234b2393 soon to be deleted code 2020-07-18 20:36:20 +01:00
Mitch161 6df560c525 added temp lifetimes 2020-07-18 20:33:09 +01:00
Mitch161 0677a95b61 fixed hashmap formatting 2020-07-18 15:04:45 +01:00
michael-bailey 0903a07cc0 Merge branch 'channel-structure' of https://github.com/mitch161/rust-chat-server into channel-structure 2020-07-17 20:53:47 +01:00
Mitch161 79e6e425dc added client command update
all clients will now be upadeted with the client command of a new connecting client
2020-07-17 20:49:25 +01:00
Mitch161 1ebde0f0ed added check for success return message 2020-07-17 20:23:38 +01:00
80 changed files with 2866 additions and 1086 deletions

7
.dockerignore Normal file
View File

@ -0,0 +1,7 @@
.gitignore
/target
/docs
/.vscode
/.idea
/.github

View File

@ -0,0 +1,50 @@
name: Publish server image
on:
push:
branches: ['master']
# Defines two custom environment variables for the workflow. These are used for the Container registry domain, and a name for the Docker image that this workflow builds.
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
# There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu.
jobs:
build-and-push-image:
runs-on: ubuntu-latest
# Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job.
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here.
- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels.
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages.
# It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository.
# It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step.
- name: Build and push Docker image
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

20
.github/workflows/master-test.yml vendored Normal file
View File

@ -0,0 +1,20 @@
name: Check and test server
on:
pull_request:
branches: [ master ]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Install protobuffer compiler
run: sudo apt-get install -y protobuf-compiler
- uses: actions/checkout@v4
- name: check
run: cargo check --verbose
- name: test
run: cargo test --verbose

44
.github/workflows/push-docker-aws.yml vendored Normal file
View File

@ -0,0 +1,44 @@
name: Push to AWS
on:
push:
branches:
- master
jobs:
Push:
name: Push
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: eu-north-1
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Get commit hash
id: get-commit-hash
run: echo "::set-output name=commit-hash::$(git rev-parse --short HEAD)"
- name: Get timestamp
id: get-timestamp
run: echo "::set-output name=timestamp::$(date +'%Y-%m-%d-%H-%M')"
- name: Build, tag, and push the image to Amazon ECR
id: build-image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: ${{ secrets.REPO_NAME }}
IMAGE_TAG: ${{ steps.get-commit-hash.outputs.commit-hash }}-${{ steps.get-timestamp.outputs.timestamp }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG

8
.gitignore vendored
View File

@ -11,4 +11,10 @@ Cargo.lock
.DS_Store
.idea
*.properties
*.properties
.vscode/launch.json
*.cer
*.pem
.vscode/settings.json
*.dylib
config_file.toml

View File

@ -1,23 +1,26 @@
[package]
name = "rust-chat-server"
version = "0.1.0"
authors = ["Mitchell <mitchellhardie1@gmail.com>"]
edition = "2018"
[workspace]
members = [
'foundation',
'server',
'protocol',
'client',
]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[workspace.dependencies]
# common data types
uuid = {version = "1.1.2", features = ["serde", "v4"]}
[dependencies]
regex = "1"
crossbeam = "0.7"
parking_lot = "0.10"
crossbeam-channel = "0.4"
crossbeam-utils = "0.7"
crossbeam-queue = "0.2"
dashmap = "3.11.4"
async-std = "1.6.2"
# maths
rand = "0.8.5"
[profile.dev]
opt-level = 0
# serialisation
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
[profile.release]
opt-level = 3
# async tokio
tokio = { version = "1.9.0", features = ["full"] }
# protobuf
bytes = "1.6.0"
prost = "0.12"
prost-build = { version = "0.12" }

21
Dockerfile Normal file
View File

@ -0,0 +1,21 @@
# First stage: build the server file.
FROM rust:alpine AS build
# Build dependencies
RUN apk add musl-dev
RUN apk add openssl-dev
RUN apk add protobuf
COPY . .
RUN cargo build --release --bin server
FROM alpine:latest AS exec
RUN apk add openssl-dev
COPY --from=build ./target/release/server /server/server
EXPOSE 5600/tcp
EXPOSE 6500/tcp
CMD ["/server/server"]

28
LICENSE
View File

@ -42,7 +42,7 @@ know their rights.
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
that there is No warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
@ -98,14 +98,14 @@ public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
a computer network, with No transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
tells the user that there is No warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
work under this License, and how to View a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
@ -202,7 +202,7 @@ non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
You may charge any price or No price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
@ -223,7 +223,7 @@ terms of section 4, provided that you also meet all of these conditions:
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
regardless of how they are packaged. This License gives No
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
@ -258,13 +258,13 @@ in one of these ways:
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
Model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
medium customarily used for software interchange, for a price No
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
Corresponding Source from a network server at No charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
@ -274,7 +274,7 @@ in one of these ways:
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
Corresponding Source in the same way through the same place at No
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
@ -287,7 +287,7 @@ in one of these ways:
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
Source of the work are being offered to the general public at No
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
@ -312,7 +312,7 @@ procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
code is in No case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
@ -337,7 +337,7 @@ protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
source code form), and must require No special password or key for
unpacking, reading or copying.
7. Additional Terms.
@ -582,7 +582,7 @@ public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
permissions. However, No additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

View File

@ -1 +1,32 @@
# rust-chat-server
# Rust-chat-server
A Chat server writen in rust to allow communication between peers.
---
## Features:
- implemented:
- json based API.
- Server introspection.
- Peer discovery.
- sending messages to connected clients.
-
- todo:
- Encryption to server.
- server to server meshing.
- asynchronous client managment instead of threaded approach.
## Goals:
- Learn the rust programming lanaguage.
- Ownership: how that affects normal programming styles.
- Borrowing and references: how this affects shared state.
- Lifetimes: how this affects data retention and sharing.
- Learn how to create networked programs.
- Application level protocol: how to get two programs to communicate via TCP sockets.
- Socket handling: Discovering ways to handle multiple socket connections without affecting performance.
- Learn common encryption protocols.
- Adding support for encrypted sockets.
- Pros and cons of symetric and asymetric encryption.
- resolving common encryption flaws
> Questions: For questions please add a issue with the question label. It will eventually be responded to

17
client/Cargo.toml Normal file
View File

@ -0,0 +1,17 @@
[package]
name = "client"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
uuid.workspace = true
tokio.workspace = true
cursive = "0.20.0"
rand.workspace = true
bytes.workspace = true
prost.workspace = true
foundation = { path = '../foundation' }

3
client/src/main.rs Normal file
View File

@ -0,0 +1,3 @@
fn main() {
println!("Please dont use this");
}

10
docs/index.md Normal file
View File

@ -0,0 +1,10 @@
# Chat Kit
This is a self hosted, distributed shat system.
It derives a lot of ideas from Discord, IRC and RCS.
This repository contains a couple of crates.
- Protocol: The protocol message structures
- Foundation: Shared structures and functions utilised in the server and client crate.
- Server: The server that accepts client connections and manages state between them.
- Client: A basic terminal client, used for testing and will be unstable.

29
foundation/Cargo.toml Normal file
View File

@ -0,0 +1,29 @@
[package]
name = "foundation"
version = "0.1.0"
authors = ["Mitchell <mitchellhardie1@gmail.com>","michael-bailey <mickyb18a@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
[dependencies]
chrono = {version = "0.4", features = ["serde", "rustc-serialize"] }
async-trait = "0.1.52"
regex = "1"
crossbeam = "0.8.0"
crossbeam-channel = "0.5.0"
crossbeam-queue = "0.3.1"
rayon = "1.2.0"
zeroize = "1.1.0"
log = "0.4"
url = "2.2.0"
futures = "0.3.16"
serde_json = "1.0"
openssl = "0.10"
uuid = {version = "1.1.2", features = ["serde", "v4"]}
tokio = { version = "1.9.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
prost.workspace = true
protocol = { path = '../protocol' }

View File

@ -0,0 +1,15 @@
use tokio::task::JoinHandle;
use crate::client::server_writer_connection::ServerWriterConnection;
pub mod network_connection;
pub mod server_reader_connection;
pub mod server_writer_connection;
pub enum NetworkState {
Disconnected,
Connection {
reader_handle: JoinHandle<()>,
writer: ServerWriterConnection,
},
}

View File

@ -0,0 +1,123 @@
use std::{io, net::SocketAddr};
use protocol::prelude::{
network_client_message,
network_server_message,
Connect,
GetInfo,
Info,
NetworkClientMessage,
NetworkServerMessage,
Request,
};
use tokio::{io::split, net::TcpStream};
use uuid::Uuid;
use crate::{
client::{
server_reader_connection::ServerReaderConnection,
server_writer_connection::ServerWriterConnection,
},
networking::protobuf::{read_message, write_message},
};
/// # NetworkConnection
/// encapsulates the state of the network connection
/// will connect to a server and ensure it is usinghte protobuf protocol
///
/// you can then either get info or connect to the server
pub struct NetworkConnection {
pub(super) stream: TcpStream,
}
impl NetworkConnection {
pub async fn connect(address: SocketAddr) -> io::Result<Self> {
let mut stream = TcpStream::connect(address).await.unwrap();
let msg =
read_message::<NetworkServerMessage, TcpStream>(&mut stream).await?;
let NetworkServerMessage {
message: Some(network_server_message::Message::Request(Request {})),
} = msg
else {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Received invalid start message from server",
));
};
Ok(Self { stream })
}
/// Will consume the connection, and fetch the servers info.
pub async fn send_get_info(mut self) -> io::Result<Info> {
_ = write_message(
&mut self.stream,
NetworkClientMessage {
message: Some(network_client_message::Message::GetInfo(GetInfo {})),
},
)
.await;
let message =
read_message::<NetworkServerMessage, TcpStream>(&mut self.stream).await?;
let NetworkServerMessage {
message: Some(network_server_message::Message::GotInfo(msg)),
} = message
else {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"sent for info got different message back",
));
};
Ok(msg)
}
/// consumes this struct and returns a tuple of the sernding and receiving ahlfs of teh connected conneciton
pub async fn send_connect(
mut self,
uuid: Uuid,
username: String,
) -> io::Result<(ServerWriterConnection, ServerReaderConnection)> {
_ = write_message(
&mut self.stream,
NetworkClientMessage {
message: Some(network_client_message::Message::Connect(Connect {
username,
uuid: uuid.to_string(),
})),
},
)
.await;
let message =
read_message::<NetworkServerMessage, TcpStream>(&mut self.stream).await?;
let NetworkServerMessage {
message: Some(network_server_message::Message::Connected(_)),
} = message
else {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"sent connect got different message back or failed to connect",
));
};
Ok(self.into())
}
}
impl From<NetworkConnection>
for (ServerWriterConnection, ServerReaderConnection)
{
fn from(value: NetworkConnection) -> Self {
let (read_half, write_half) = split(value.stream);
(
ServerWriterConnection::new(write_half),
ServerReaderConnection::new(read_half),
)
}
}

View File

@ -0,0 +1,26 @@
use std::io;
use protocol::prelude::ConnectedServerMessage;
use tokio::{io::ReadHalf, net::TcpStream};
use crate::networking::protobuf::read_message;
pub struct ServerReaderConnection {
reader: ReadHalf<TcpStream>,
}
impl ServerReaderConnection {
pub(crate) fn new(read_half: ReadHalf<TcpStream>) -> Self {
Self { reader: read_half }
}
// move to other one
pub async fn get_message(&mut self) -> io::Result<ConnectedServerMessage> {
let message = read_message::<ConnectedServerMessage, ReadHalf<TcpStream>>(
&mut self.reader,
)
.await
.unwrap();
Ok(message)
}
}

View File

@ -0,0 +1,14 @@
use tokio::{io::WriteHalf, net::TcpStream};
#[allow(dead_code)]
pub struct ServerWriterConnection {
writer: WriteHalf<TcpStream>,
}
impl ServerWriterConnection {
pub(crate) fn new(writer: WriteHalf<TcpStream>) -> Self {
Self { writer }
}
pub async fn request_clients(&mut self) {}
}

24
foundation/src/lib.rs Normal file
View File

@ -0,0 +1,24 @@
pub mod client;
pub mod messages;
pub mod models;
pub mod networking;
pub mod prelude;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
/**
* # ClientDetails.
* This defines the fileds a client would want to send when connecitng
* uuid: the unique id of the user.
* username: the users user name.
* address: the ip address of the connected user.
* public_key: the public key used when sending messages to the user.
*/
#[derive(Deserialize, Serialize, Debug, Clone, Default)]
pub struct ClientDetails {
pub uuid: Uuid,
pub username: String,
pub address: String,
pub public_key: Option<Vec<u8>>,
}

View File

@ -0,0 +1,42 @@
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::{models::message::Message, ClientDetails};
/// This enum defined the message that the server will receive from a client
/// This uses the serde library to transform to and from json.
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "type")]
pub enum ClientStreamIn {
GetClients,
GetMessages,
SendMessage { to: Uuid, content: String },
SendGlobalMessage { content: String },
Disconnect,
}
/// This enum defined the message that the server will send to a client
/// This uses the serde library to transform to and from json.
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "type")]
pub enum ClientStreamOut {
Connected,
// get reequest messages
ConnectedClients { clients: Vec<ClientDetails> },
GlobalChatMessages { messages: Vec<Message> },
// event messges
UserMessage { from: Uuid, content: String },
GlobalMessage { from: Uuid, content: String },
ClientConnected { id: Uuid, username: String },
ClientRemoved { id: Uuid },
Disconnected,
// error cases
Error { msg: String },
}

View File

@ -0,0 +1,2 @@
pub mod client;
pub mod network;

View File

@ -0,0 +1,49 @@
use serde::{Deserialize, Serialize};
use uuid::Uuid;
/// Message the server will receive from a socket
#[derive(Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum NetworkSockIn {
Info,
Connect {
uuid: Uuid,
username: String,
address: String,
},
}
/// Message the server will send through a socket
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "type")]
pub enum NetworkSockOut {
Request,
GotInfo {
server_name: String,
server_owner: String,
},
Connected,
Error,
}
impl PartialEq for NetworkSockOut {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(NetworkSockOut::Request, NetworkSockOut::Request) => true,
(
NetworkSockOut::GotInfo {
server_name,
server_owner,
},
NetworkSockOut::GotInfo {
server_owner: owner_other,
server_name: name_other,
},
) => server_name == name_other && server_owner == owner_other,
(NetworkSockOut::Connected, NetworkSockOut::Connected) => true,
_ => false,
}
}
}

View File

@ -0,0 +1,22 @@
use chrono::{DateTime, Local};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Message {
pub id: Uuid,
pub from: Uuid,
pub content: String,
pub time: DateTime<Local>,
}
impl Message {
pub fn new(from: Uuid, content: String) -> Self {
Self {
id: Uuid::new_v4(),
from,
content,
time: Local::now(),
}
}
}

View File

@ -0,0 +1 @@
pub mod message;

View File

@ -0,0 +1,51 @@
use std::io;
use serde::{de::DeserializeOwned, Serialize};
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
pub async fn write_message<S, M>(stream: &mut S, message: M)
where
S: AsyncWrite + AsyncWriteExt + Unpin,
M: Serialize,
{
let mut message = serde_json::to_string(&message).unwrap();
message.push('\n');
_ = stream.write(message.as_bytes()).await;
}
// todo: Handle error properly
pub async fn read_message<S, M>(stream: &mut S) -> io::Result<M>
where
S: AsyncRead + AsyncReadExt + Unpin,
M: DeserializeOwned,
{
let string = read_line(stream).await?;
Ok(serde_json::from_str(&string).unwrap())
}
#[allow(clippy::redundant_guards, clippy::needless_range_loop)]
async fn read_line<S>(stream: &mut S) -> Result<String, std::io::Error>
where
S: AsyncRead + AsyncReadExt + Unpin,
{
let mut buf = vec![0; 1024];
let mut newline_found = false;
let mut result = Vec::new();
loop {
let n = match stream.read(&mut buf).await {
Ok(n) if n == 0 => return Ok(String::from_utf8(result).unwrap()),
Ok(n) => n,
Err(e) => return Err(e),
};
for i in 0..n {
if buf[i] == b'\n' {
newline_found = true;
break;
}
result.push(buf[i]);
}
if newline_found {
return Ok(String::from_utf8(result).unwrap());
}
}
}

View File

@ -0,0 +1,2 @@
pub mod json;
pub mod protobuf;

View File

@ -0,0 +1,66 @@
use std::io::{self, ErrorKind};
use prost::{
bytes::{BufMut, Bytes, BytesMut},
Message,
};
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
pub async fn write_message<T, S>(stream: &mut S, message: T) -> io::Result<()>
where
T: Message + Default,
S: AsyncWrite + AsyncWriteExt + Unpin,
{
let message = encode_message::<T>(&message)?;
stream.write_all(&message).await?;
Ok(())
}
pub fn encode_message<T>(msg: &T) -> io::Result<Bytes>
where
T: Message,
{
let length = msg.encoded_len();
let mut buffer = BytesMut::with_capacity(4 + length);
buffer.put_u32(length as u32);
let encode_result = msg.encode(&mut buffer);
if let Err(err) = encode_result {
return Err(io::Error::new(
ErrorKind::InvalidInput,
format!("message encoding failed: {:?}", err),
));
}
Ok(buffer.into())
}
pub async fn read_message<T, S>(stream: &mut S) -> io::Result<T>
where
T: Message + Default,
S: AsyncRead + AsyncReadExt + Unpin,
{
let size = stream.read_u32().await?;
let mut buffer = BytesMut::with_capacity(size as usize);
unsafe { buffer.set_len(size as usize) };
stream.read_exact(&mut buffer).await?;
let message = decode_message::<T>(buffer.into())?;
Ok(message)
}
pub fn decode_message<T>(buffer: Bytes) -> io::Result<T>
where
T: Message + Default,
{
let msg_result = T::decode(buffer);
match msg_result {
Ok(msg) => Ok(msg),
Err(err) => Err(io::Error::new(
ErrorKind::InvalidInput,
format!("message decoding failed: {:?}", err),
)),
}
}

View File

@ -0,0 +1 @@
pub use protocol::prelude::*;

28
protocol/Cargo.toml Normal file
View File

@ -0,0 +1,28 @@
[package]
name = "protocol"
version = "0.1.0"
authors = ["michael-bailey <mickyb18a@gmail.com>"]
edition = "2018"
[lib]
[dependencies]
chrono = "0.4"
uuid = {version = "1.1.2", features = ["serde", "v4"]}
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
crossbeam = "0.8.0"
crossbeam-channel = "0.5.0"
zeroize = "1.1.0"
tokio = { version = "1.9.0", features = ["full"] }
futures = "0.3.16"
async-trait = "0.1.52"
toml = "0.8.8"
# prost setup
bytes.workspace = true
prost.workspace = true
[build-dependencies]
prost-build.workspace = true

10
protocol/build.rs Normal file
View File

@ -0,0 +1,10 @@
use std::io::Result;
// Use this in build.rs
fn main() -> Result<()> {
prost_build::compile_protos(
&["src/proto/network.proto", "src/proto/connected.proto"],
&["src/proto"],
)?;
Ok(())
}

5
protocol/src/lib.rs Normal file
View File

@ -0,0 +1,5 @@
mod proto;
pub mod prelude {
pub use super::proto::*;
}

View File

@ -0,0 +1,81 @@
syntax = "proto3";
package chatkit.messages;
// messages from the client when connected.
message ConnectedClientMessage {
oneof message {
GetClients get_clients = 1;
GetGlobalMessages get_global_message = 2;
SendGlobalMessage send_global_message = 3;
SendPrivateMessage send_private_message = 4;
Disconnect disconnect = 5;
}
}
message GetClients {}
message GetGlobalMessages {}
message SendGlobalMessage {
string content = 1;
}
message SendPrivateMessage {
string uuid = 1;
string to = 2;
string content = 3;
}
message Disconnect {}
// messages from the Server when connected.
message ConnectedServerMessage {
oneof message {
ConnectedClients connected_clients = 1;
GlobalMessages global_messages = 2;
PrivateMessage private_message = 3;
Disconnected disconnected = 4;
GlobalMessage global_message = 5;
ClientConnected client_connected = 6;
ClientDisconnected client_disconnected = 7;
}
}
message ConnectedClients {
repeated ClientDetails clients = 1;
}
message ClientConnected {
ClientDetails details = 1;
}
message ClientDisconnected {
string uuid = 1;
}
message ClientDetails {
string uuid = 1;
string name = 2;
string address = 3;
}
message GlobalMessages {
repeated GlobalMessage messages = 1;
}
message GlobalMessage {
string uuid = 1;
string from = 2;
string content = 3;
}
message PrivateMessage {
string uuid = 1;
string from = 2;
string content = 3;
}
message Disconnected {
string reason = 1;
}

View File

@ -0,0 +1 @@
include!(concat!(env!("OUT_DIR"), "/chatkit.messages.rs"));

View File

@ -0,0 +1,37 @@
syntax = "proto3";
package chatkit.messages;
// Network messages sent from the client.
message NetworkClientMessage {
oneof message {
GetInfo get_info = 1;
Connect connect = 2;
}
}
message GetInfo {}
message Connect {
string username = 1;
string uuid = 2;
}
// Network messages sent from the server.
message NetworkServerMessage {
oneof message {
Request request = 1;
Info got_info = 2;
Connected connected = 3;
}
}
message Request {}
message Info {
string server_name = 1;
string owner = 2;
}
message Connected {}

8
rustfmt.toml Normal file
View File

@ -0,0 +1,8 @@
max_width = 80
hard_tabs = true
tab_spaces = 2
imports_layout = "HorizontalVertical"
imports_granularity = "Crate"
merge_imports = true
reorder_imports = true
group_imports = "StdExternalCrate"

46
server/Cargo.toml Normal file
View File

@ -0,0 +1,46 @@
[package]
name = "server"
version = "0.1.0"
authors = ["michael-bailey <mickyb18a@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
# [lib]
# name = "serverlib"
# path = "src/lib.rs"
# [[bin]]
# name = "server"
# path = "src/main.rs"
[[bin]]
name = "server"
path = "src/main.rs"
[dependencies]
chrono = "0.4"
clap = {version = "4.4.8", features = ["derive"]}
uuid.workspace = true
serde.workspace = true
serde_json.workspace = true
crossbeam = "0.8.0"
crossbeam-channel = "0.5.0"
zeroize = "1.1.0"
openssl = "0.10.33"
tokio.workspace = true
futures = "0.3.16"
async-trait = "0.1.80"
actix = "0.13"
rhai = {version = "1.7.0"}
mlua = { version = "0.9.2", features=["lua54", "async", "serde", "macros", "vendored"] }
libloading = "0.8.1"
toml = "0.8.8"
tokio-stream = "0.1.9"
# protobuf
bytes.workspace = true
prost.workspace = true
foundation = {path = '../foundation'}

29
server/src/chat/mod.rs Normal file
View File

@ -0,0 +1,29 @@
use foundation::prelude::GlobalMessage;
pub struct ChatManager {
messages: Vec<GlobalMessage>,
}
impl ChatManager {
pub fn new() -> Self {
Self {
messages: Vec::new(),
}
}
pub fn add_message(&mut self, message: GlobalMessage) {
println!("[ChatManager] added new global message {:?}", message);
self.messages.push(message);
}
pub fn get_messages(&mut self) -> Vec<GlobalMessage> {
println!("[ChatManager] got all messages");
self.messages.clone()
}
}
impl Default for ChatManager {
fn default() -> Self {
Self::new()
}
}

View File

@ -0,0 +1,32 @@
use std::net::SocketAddr;
use uuid::Uuid;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ClientInfo {
uuid: Uuid,
username: String,
addr: SocketAddr,
}
impl ClientInfo {
pub fn new(uuid: Uuid, username: String, addr: SocketAddr) -> Self {
Self {
uuid,
username,
addr,
}
}
pub fn get_uuid(&self) -> Uuid {
self.uuid
}
pub fn get_username(&self) -> String {
self.username.clone()
}
pub fn get_addr(&self) -> SocketAddr {
self.addr
}
}

View File

@ -0,0 +1,87 @@
use foundation::prelude::{ClientDetails, GlobalMessage, PrivateMessage};
use tokio::{sync::mpsc::UnboundedSender, task::JoinHandle};
use uuid::Uuid;
use crate::{
connection::{
client_info::ClientInfo,
connection_manager::ConnectionManagerMessage,
},
network::{ClientWriter, NetworkConnection},
};
pub struct ClientThread {
read_task: JoinHandle<()>,
writer: Box<dyn ClientWriter>,
}
impl ClientThread {
pub async fn new_run(
uuid: Uuid,
conn: Box<dyn NetworkConnection>,
connection_manager_sender: UnboundedSender<ConnectionManagerMessage>,
) -> Self {
println!("[ClientThread] creating thread");
let (writer, reader) = conn.send_connected(uuid).await;
println!("[ClientThread] creating tasks");
ClientThread {
read_task: reader.start_run(uuid, connection_manager_sender.clone()),
writer,
}
}
pub async fn send_clients(&mut self, clients: Vec<ClientDetails>) {
self.writer.send_clients(clients).await
}
pub async fn send_client_joined(&mut self, details: ClientDetails) {
self.writer.send_client_joined(details).await;
}
pub async fn send_client_left(&mut self, uuid: Uuid) {
self.writer.send_client_left(uuid).await
}
// todo: link this in with message storage
pub(crate) async fn send_global_message(&mut self, message: GlobalMessage) {
self.writer.send_global_message(message).await;
}
pub(crate) async fn send_global_messages(
&mut self,
messages: Vec<GlobalMessage>,
) {
self.writer.send_global_messages(messages).await;
}
pub(crate) async fn send_disconnected(&mut self) {
self.writer.send_disconnect().await
}
pub(crate) async fn send_private_message(
&mut self,
from: Uuid,
uuid: Uuid,
content: String,
) {
self
.writer
.send_private_message(PrivateMessage {
uuid: uuid.to_string(),
from: from.to_string(),
content,
})
.await;
}
}
impl Drop for ClientThread {
fn drop(&mut self) {
self.read_task.abort();
}
}
pub enum ClientMessage {
SendClients(Vec<ClientInfo>),
SendGlobalMessages(Vec<GlobalMessage>),
}

View File

@ -0,0 +1,252 @@
use std::{collections::HashMap, net::SocketAddr};
use foundation::prelude::{ClientDetails, GlobalMessage};
use tokio::sync::{
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
Mutex,
};
use uuid::Uuid;
use crate::{
connection::{client_info::ClientInfo, client_thread::ClientThread},
network::NetworkConnection,
server_va::ServerMessages,
};
pub struct ConnectionManager {
receiver: Mutex<UnboundedReceiver<ConnectionManagerMessage>>,
sender: UnboundedSender<ConnectionManagerMessage>,
server_sender: UnboundedSender<ServerMessages>,
client_map: HashMap<Uuid, ClientInfo>,
client_tasks_map: HashMap<Uuid, ClientThread>,
}
impl ConnectionManager {
pub fn new(server_sender: UnboundedSender<ServerMessages>) -> Self {
let (tx, rx) = unbounded_channel();
Self {
client_map: HashMap::new(),
client_tasks_map: HashMap::new(),
server_sender,
receiver: Mutex::new(rx),
sender: tx,
}
}
pub async fn run(&mut self) {
loop {
let mut lock = self.receiver.lock().await;
let msg = lock.recv().await;
drop(lock);
match msg {
Some(ConnectionManagerMessage::AddClient {
conn,
uuid,
username,
addr,
}) => self.add_client(conn, uuid, username, addr).await,
Some(ConnectionManagerMessage::Disconnected { uuid }) => {
self.remove_client(uuid).await
}
Some(ConnectionManagerMessage::BroadcastGlobalMessage {
from,
content,
}) => {
self.broadcast_global_message(from, content).await;
}
Some(ConnectionManagerMessage::SendClientsTo { uuid }) => {
self.send_clients_to(uuid).await;
}
Some(ConnectionManagerMessage::SendGlobalMessages { uuid }) => {
self.send_global_messages(uuid).await;
}
Some(ConnectionManagerMessage::SendGlobalMessagesTo {
uuid,
messages,
}) => {
self.send_global_messages_to(uuid, messages).await;
}
Some(ConnectionManagerMessage::SendPrivateMessage {
uuid,
from,
to,
content,
}) => {
self.send_private_message(to, from, uuid, content).await;
}
Some(ConnectionManagerMessage::Disconnect { uuid }) => {
self.disconnect(uuid).await
}
None => todo!(),
}
}
}
async fn add_client(
&mut self,
conn: Box<dyn NetworkConnection>,
uuid: Uuid,
username: String,
addr: SocketAddr,
) {
println!("[ConnectionManager] adding new client");
let store = ClientInfo::new(uuid, username.clone(), addr);
self.client_map.insert(uuid, store);
println!("[ConnectionManager] added client info to map");
let thread = ClientThread::new_run(uuid, conn, self.sender.clone()).await;
self.client_tasks_map.insert(uuid, thread);
println!("[ConnectionManager] created running thread for new clinet");
for c in self.client_tasks_map.iter_mut() {
c.1
.send_client_joined(ClientDetails {
uuid: uuid.to_string(),
name: username.clone(),
address: addr.to_string(),
})
.await;
}
}
async fn remove_client(&mut self, uuid: Uuid) {
println!("[ConnectionManager] removing {}", uuid);
self.client_map.remove(&uuid);
self.client_tasks_map.remove(&uuid);
for c in self.client_tasks_map.iter_mut() {
c.1.send_client_left(uuid).await;
}
}
async fn send_clients_to(&mut self, uuid: Uuid) {
let clients = self
.client_map
.values()
.cloned()
.map(|c| foundation::prelude::ClientDetails {
uuid: c.get_uuid().to_string(),
name: c.get_username(),
address: c.get_addr().to_string(),
})
.collect();
let t = self.client_tasks_map.get_mut(&uuid);
let Some(t) = t else {
return;
};
println!("[ConnectionManager] sending client list to {:?}", clients);
t.send_clients(clients).await;
}
async fn broadcast_global_message(&mut self, from: Uuid, content: String) {
let message = GlobalMessage {
uuid: Uuid::new_v4().to_string(),
from: from.to_string(),
content,
};
_ = self
.server_sender
.send(ServerMessages::AddGlobalMessage(message.clone()));
for c in self.client_tasks_map.iter_mut() {
c.1.send_global_message(message.clone()).await;
}
}
async fn send_global_messages(&mut self, uuid: Uuid) {
_ = self
.server_sender
.send(ServerMessages::SendGlobalMessages(uuid));
}
async fn send_global_messages_to(
&mut self,
uuid: Uuid,
messages: Vec<GlobalMessage>,
) {
let t = self.client_tasks_map.get_mut(&uuid);
let Some(t) = t else {
return;
};
t.send_global_messages(messages).await;
}
async fn send_private_message(
&mut self,
to: Uuid,
from: Uuid,
uuid: Uuid,
content: String,
) {
let t = self.client_tasks_map.get_mut(&to);
let Some(t) = t else {
return;
};
t.send_private_message(from, uuid, content).await
}
async fn disconnect(&mut self, uuid: Uuid) {
let t = self.client_tasks_map.get_mut(&uuid);
let Some(t) = t else {
return;
};
t.send_disconnected().await;
}
pub fn get_sender(&self) -> UnboundedSender<ConnectionManagerMessage> {
self.sender.clone()
}
}
pub enum ConnectionManagerMessage {
// server messages
AddClient {
conn: Box<dyn NetworkConnection + 'static>,
uuid: Uuid,
username: String,
addr: SocketAddr,
},
// client thread messages
SendClientsTo {
uuid: Uuid,
},
SendGlobalMessages {
uuid: Uuid,
},
SendGlobalMessagesTo {
uuid: Uuid,
messages: Vec<GlobalMessage>,
},
BroadcastGlobalMessage {
from: Uuid,
content: String,
},
SendPrivateMessage {
uuid: Uuid,
from: Uuid,
to: Uuid,
content: String,
},
Disconnect {
uuid: Uuid,
},
Disconnected {
uuid: Uuid,
},
}

View File

@ -0,0 +1,3 @@
pub mod client_info;
pub mod client_thread;
pub mod connection_manager;

19
server/src/main.rs Normal file
View File

@ -0,0 +1,19 @@
//! This is the main module of the actix server.
//! It starts the server and sleeps for the remainder of the program
pub mod network;
pub mod chat;
pub mod connection;
pub mod os_signal_manager;
pub mod server_va;
use crate::server_va::Server;
/// The main function
#[actix::main()]
async fn main() {
// creating listeners
Server::default().run().await;
}

View File

@ -0,0 +1,98 @@
use std::{io, net::SocketAddr};
use foundation::{
messages::client::ClientStreamIn,
networking::json::read_message,
};
use tokio::{io::ReadHalf, net::TcpStream, sync::mpsc::UnboundedSender};
use uuid::Uuid;
use crate::{
connection::connection_manager::ConnectionManagerMessage,
network::ClientReader,
};
pub struct JSONClientReader {
reader: ReadHalf<TcpStream>,
addr: SocketAddr,
uuid: Uuid,
}
impl JSONClientReader {
pub fn new(
reader: ReadHalf<TcpStream>,
addr: SocketAddr,
uuid: Uuid,
) -> Self {
Self { reader, addr, uuid }
}
// move to other one
pub async fn get_message(&mut self) -> io::Result<ClientStreamIn> {
read_message::<ReadHalf<TcpStream>, ClientStreamIn>(&mut self.reader).await
}
pub fn handle_message(
&self,
msg: ClientStreamIn,
channel: &UnboundedSender<ConnectionManagerMessage>,
) {
println!("[JSONClientReader:{}] got message", self.addr);
let uuid = self.uuid;
_ = match msg {
ClientStreamIn::GetClients => {
channel.send(ConnectionManagerMessage::SendClientsTo { uuid })
}
ClientStreamIn::GetMessages => {
channel.send(ConnectionManagerMessage::SendGlobalMessages { uuid })
}
ClientStreamIn::SendMessage { to, content } => {
channel.send(ConnectionManagerMessage::SendPrivateMessage {
uuid: Uuid::new_v4(),
from: uuid,
to,
content,
})
}
ClientStreamIn::SendGlobalMessage { content } => {
channel.send(ConnectionManagerMessage::BroadcastGlobalMessage {
from: uuid,
content,
})
}
ClientStreamIn::Disconnect => {
channel.send(ConnectionManagerMessage::Disconnect { uuid })
}
};
}
}
impl ClientReader for JSONClientReader {
fn start_run(
mut self: Box<Self>,
uuid: Uuid,
channel: UnboundedSender<ConnectionManagerMessage>,
) -> tokio::task::JoinHandle<()> {
tokio::spawn(async move {
loop {
let msg = self.get_message().await;
let Ok(msg) = msg else {
let error = msg.unwrap_err();
println!(
"[JSONClientReader:{}] errored with '{}' disconnecting",
self.addr, error
);
_ = channel.send(ConnectionManagerMessage::Disconnected { uuid });
return;
};
self.handle_message(msg, &channel);
}
})
}
}

View File

@ -0,0 +1,117 @@
use std::net::SocketAddr;
use async_trait::async_trait;
use chrono::Local;
use foundation::{
messages::client::ClientStreamOut,
models::message::Message,
networking::json::write_message,
prelude::{GlobalMessage, PrivateMessage},
ClientDetails,
};
use tokio::{io::WriteHalf, net::TcpStream};
use uuid::Uuid;
use crate::network::ClientWriter;
#[allow(dead_code)]
pub struct JSONClientWriter {
writer: WriteHalf<TcpStream>,
addr: SocketAddr,
uuid: Uuid,
}
impl JSONClientWriter {
pub fn new(
writer: WriteHalf<TcpStream>,
addr: SocketAddr,
uuid: Uuid,
) -> Self {
Self { writer, addr, uuid }
}
}
#[async_trait]
impl ClientWriter for JSONClientWriter {
async fn send_clients(
&mut self,
clients: Vec<foundation::prelude::ClientDetails>,
) {
let message = ClientStreamOut::ConnectedClients {
clients: clients
.into_iter()
.map(|c| ClientDetails {
uuid: c.uuid.parse().unwrap(),
username: c.name,
address: c.address,
public_key: None,
})
.collect(),
};
println!("[JSONClientWriter:{}] sending clients", self.addr);
write_message(&mut self.writer, message).await;
}
async fn send_client_joined(
&mut self,
details: foundation::prelude::ClientDetails,
) {
let message = ClientStreamOut::ClientConnected {
id: details.uuid.parse().unwrap(),
username: details.name,
};
println!(
"[JSONClientReader:{}] sending client connected message",
self.addr
);
write_message(&mut self.writer, message).await;
}
async fn send_client_left(&mut self, uuid: Uuid) {
let message = ClientStreamOut::ClientRemoved { id: uuid };
println!(
"[JSONClientReader:{}] sending client connected message",
self.addr
);
write_message(&mut self.writer, message).await;
}
async fn send_global_messages(&mut self, messages: Vec<GlobalMessage>) {
let message = ClientStreamOut::GlobalChatMessages {
messages: messages
.into_iter()
.map(|m| Message {
id: m.uuid.parse().unwrap(),
from: m.from.parse().unwrap(),
content: m.content,
time: Local::now(),
})
.collect(),
};
println!("[JSONClientWriter:{}] sending global messages", self.addr);
write_message(&mut self.writer, message).await;
}
async fn send_private_message(&mut self, message: PrivateMessage) {
let message = ClientStreamOut::UserMessage {
from: message.from.parse().unwrap(),
content: message.content,
};
println!("[JSONClientWriter:{}] sending private message", self.addr);
write_message(&mut self.writer, message).await;
}
async fn send_global_message(&mut self, message: GlobalMessage) {
let message = ClientStreamOut::GlobalMessage {
from: message.from.parse().unwrap(),
content: message.content,
};
write_message(&mut self.writer, message).await;
}
async fn send_disconnect(&mut self) {
let message = ClientStreamOut::Disconnected;
println!("[JSONClientWriter:{}] sending disconnect", self.addr);
write_message(&mut self.writer, message).await;
}
}

View File

@ -0,0 +1,54 @@
use async_trait::async_trait;
use tokio::{net::TcpListener, sync::mpsc::UnboundedSender, task::JoinHandle};
use crate::{
network::{ConnectionType, NetworkListener},
server_va::ServerMessages,
};
/// # Listener Manager
/// This stores and awaits for connections from listeners.
/// When a connection is received, it is passed to the server
pub struct JSONListener {
listener: TcpListener,
sender: UnboundedSender<ServerMessages>,
}
#[async_trait]
impl NetworkListener for JSONListener {
/// Binds listeners and stores them in the ListenerManager
async fn new(sender: UnboundedSender<ServerMessages>) -> Self {
let address = "0.0.0.0:5600";
println!("[JSONListener] setting up listeners");
let listener = TcpListener::bind(address)
.await
.expect("[JSONListener] failed to bind to 0.0.0.0:5600");
Self { listener, sender }
}
async fn run(&self) {
loop {
println!("[JSONListener] waiting for connection");
let accept_protobuf = self.listener.accept().await;
let Ok((stream, addr)) = accept_protobuf else {
println!("[JSONListener] accept failed");
continue;
};
let msg = ServerMessages::NewConnection(ConnectionType::JsonConnection(
stream, addr,
));
println!("[JSONListener] passing message to server");
_ = self.sender.send(msg);
}
}
fn start_run(sender: UnboundedSender<ServerMessages>) -> JoinHandle<()> {
tokio::spawn(async move {
JSONListener::new(sender).await.run().await;
})
}
}

View File

@ -0,0 +1,87 @@
use std::{io, net::SocketAddr};
use foundation::{
messages::network::{NetworkSockIn, NetworkSockOut},
networking::json::{read_message, write_message},
};
use tokio::{io::split, net::TcpStream};
use uuid::Uuid;
use crate::network::{
json::{
json_client_reader::JSONClientReader,
json_client_writer::JSONClientWriter,
},
ClientReader,
ClientWriter,
NetworkConnection,
ServerRequest,
};
pub struct JSONNetworkConnection {
pub(super) stream: TcpStream,
pub(super) addr: SocketAddr,
}
impl JSONNetworkConnection {
pub fn new(stream: TcpStream, addr: SocketAddr) -> Self {
Self { stream, addr }
}
}
#[async_trait::async_trait]
impl NetworkConnection for JSONNetworkConnection {
async fn get_request(&mut self) -> io::Result<ServerRequest> {
println!("[JSONNetworkConnection] sending request");
write_message(&mut self.stream, NetworkSockOut::Request).await;
println!("[JSONNetworkConnection] waiting for response");
let request =
read_message::<TcpStream, NetworkSockIn>(&mut self.stream).await?;
println!("[JSONNetworkConnection] returning request");
match request {
NetworkSockIn::Info => Ok(ServerRequest::GetInfo),
NetworkSockIn::Connect {
uuid,
username,
address: _,
} => Ok(ServerRequest::Connect {
username,
uuid,
addr: self.addr,
}),
// _ => Ok(ServerRequest::Ignore),
}
}
async fn send_info(mut self: Box<Self>, name: String, owner: String) {
println!("[JSONNetworkConnection] Sending info to client");
write_message(
&mut self.stream,
NetworkSockOut::GotInfo {
server_name: name,
server_owner: owner,
},
)
.await;
println!("[JSONNetworkConnection] droping connection");
}
async fn send_connected(
mut self: Box<Self>,
uuid: Uuid,
) -> (Box<dyn ClientWriter>, Box<dyn ClientReader>) {
write_message(&mut self.stream, NetworkSockOut::Connected).await;
let (read, write) = split(self.stream);
let writer = Box::new(JSONClientWriter::new(write, self.addr, uuid));
let reader = Box::new(JSONClientReader::new(read, self.addr, uuid));
(writer, reader)
}
}

View File

@ -0,0 +1,4 @@
pub mod json_client_reader;
pub mod json_client_writer;
pub mod json_listener;
pub mod json_network_connection;

66
server/src/network/mod.rs Normal file
View File

@ -0,0 +1,66 @@
use std::{io, net::SocketAddr};
use async_trait::async_trait;
use foundation::prelude::{ClientDetails, GlobalMessage, PrivateMessage};
use tokio::{net::TcpStream, sync::mpsc::UnboundedSender, task::JoinHandle};
use uuid::Uuid;
use crate::{
connection::connection_manager::ConnectionManagerMessage,
server_va::ServerMessages,
};
pub mod json;
pub mod protobuf;
pub enum ConnectionType {
ProtobufConnection(TcpStream, SocketAddr),
JsonConnection(TcpStream, SocketAddr),
}
#[async_trait]
pub trait NetworkListener {
async fn new(channel: UnboundedSender<ServerMessages>) -> Self;
async fn run(&self);
fn start_run(sender: UnboundedSender<ServerMessages>) -> JoinHandle<()>;
}
#[async_trait::async_trait]
pub trait NetworkConnection: Send {
async fn get_request(&mut self) -> io::Result<ServerRequest>;
async fn send_info(self: Box<Self>, name: String, owner: String);
async fn send_connected(
self: Box<Self>,
uuid: Uuid,
) -> (Box<dyn ClientWriter>, Box<dyn ClientReader>);
}
#[async_trait::async_trait]
pub trait ClientReader: Send {
fn start_run(
self: Box<Self>,
uuid: Uuid,
channel: UnboundedSender<ConnectionManagerMessage>,
) -> JoinHandle<()>;
}
#[async_trait::async_trait]
pub trait ClientWriter: Send {
async fn send_clients(&mut self, clients: Vec<ClientDetails>);
async fn send_global_messages(&mut self, messages: Vec<GlobalMessage>);
async fn send_global_message(&mut self, message: GlobalMessage);
async fn send_private_message(&mut self, message: PrivateMessage);
async fn send_disconnect(&mut self);
async fn send_client_joined(&mut self, details: ClientDetails);
async fn send_client_left(&mut self, uuid: Uuid);
}
pub enum ServerRequest {
GetInfo,
Connect {
username: String,
uuid: uuid::Uuid,
addr: SocketAddr,
},
Ignore,
}

View File

@ -0,0 +1,4 @@
pub mod protobuf_client_reader;
pub mod protobuf_client_writer;
pub mod protobuf_listener;
pub mod protobuf_network_connection;

View File

@ -0,0 +1,118 @@
use std::{io, net::SocketAddr};
use foundation::{
networking::protobuf::read_message,
prelude::{
connected_client_message,
ConnectedClientMessage,
Disconnect,
GetClients,
GetGlobalMessages,
SendGlobalMessage,
SendPrivateMessage,
},
};
use tokio::{io::ReadHalf, net::TcpStream, sync::mpsc::UnboundedSender};
use uuid::Uuid;
use crate::{
connection::connection_manager::ConnectionManagerMessage,
network::ClientReader,
};
pub struct ProtobufClientReader {
reader: ReadHalf<TcpStream>,
addr: SocketAddr,
uuid: Uuid,
}
impl ProtobufClientReader {
pub fn new(
reader: ReadHalf<TcpStream>,
addr: SocketAddr,
uuid: Uuid,
) -> Self {
Self { reader, addr, uuid }
}
// move to other one
pub async fn get_message(&mut self) -> io::Result<ConnectedClientMessage> {
read_message::<ConnectedClientMessage, ReadHalf<TcpStream>>(
&mut self.reader,
)
.await
}
pub fn handle_message(
&self,
msg: ConnectedClientMessage,
channel: &UnboundedSender<ConnectionManagerMessage>,
) {
use connected_client_message::Message;
println!("[ProtobufClientReader:{}] got message", self.addr);
let uuid = self.uuid;
_ = match msg {
ConnectedClientMessage {
message: Some(Message::GetClients(GetClients {})),
} => channel.send(ConnectionManagerMessage::SendClientsTo { uuid }),
ConnectedClientMessage {
message: Some(Message::GetGlobalMessage(GetGlobalMessages {})),
} => channel.send(ConnectionManagerMessage::SendGlobalMessages { uuid }),
ConnectedClientMessage {
message:
Some(Message::SendPrivateMessage(SendPrivateMessage {
uuid: message_uuid,
to,
content,
})),
} => channel.send(ConnectionManagerMessage::SendPrivateMessage {
uuid: message_uuid.parse().unwrap(),
from: uuid,
to: to.parse().unwrap(),
content,
}),
ConnectedClientMessage {
message: Some(Message::SendGlobalMessage(SendGlobalMessage { content })),
} => channel.send(ConnectionManagerMessage::BroadcastGlobalMessage {
from: uuid,
content,
}),
ConnectedClientMessage {
message: Some(Message::Disconnect(Disconnect {})),
} => channel.send(ConnectionManagerMessage::Disconnect { uuid }),
ConnectedClientMessage { message: None } => unimplemented!(),
};
}
}
impl ClientReader for ProtobufClientReader {
fn start_run(
mut self: Box<Self>,
uuid: Uuid,
channel: UnboundedSender<ConnectionManagerMessage>,
) -> tokio::task::JoinHandle<()> {
tokio::spawn(async move {
loop {
let msg = self.get_message().await;
let Ok(msg) = msg else {
let error = msg.unwrap_err();
println!(
"[ProtobufClientReader:{}] errored with '{}' disconnecting",
self.addr, error
);
_ = channel.send(ConnectionManagerMessage::Disconnected { uuid });
return;
};
self.handle_message(msg, &channel);
}
})
}
}

View File

@ -0,0 +1,175 @@
use std::net::SocketAddr;
use async_trait::async_trait;
use foundation::{
networking::protobuf::write_message,
prelude::{
connected_server_message,
ClientConnected,
ClientDetails,
ClientDisconnected,
ConnectedClients,
ConnectedServerMessage,
Disconnected,
GlobalMessage,
GlobalMessages,
PrivateMessage,
},
};
use tokio::{io::WriteHalf, net::TcpStream};
use uuid::Uuid;
use crate::network::ClientWriter;
#[allow(dead_code)]
pub struct ProtobufClientWriter {
writer: WriteHalf<TcpStream>,
addr: SocketAddr,
uuid: Uuid,
}
impl ProtobufClientWriter {
pub fn new(
writer: WriteHalf<TcpStream>,
addr: SocketAddr,
uuid: Uuid,
) -> Self {
Self { writer, addr, uuid }
}
#[deprecated]
pub async fn send_clients(&mut self, clients: Vec<ClientDetails>) {
let message = ConnectedServerMessage {
message: Some(connected_server_message::Message::ConnectedClients(
ConnectedClients { clients },
)),
};
println!("[ProtobufClientWriter:{}] sending clients", self.addr);
write_message(&mut self.writer, message).await.unwrap();
}
#[deprecated]
pub async fn send_global_messages(&mut self, messages: Vec<GlobalMessage>) {
let message = ConnectedServerMessage {
message: Some(connected_server_message::Message::GlobalMessages(
GlobalMessages { messages },
)),
};
println!(
"[ProtobufClientWriter:{}] sending global messages",
self.addr
);
write_message(&mut self.writer, message).await.unwrap();
}
#[deprecated]
pub async fn send_private_message(&mut self, message: PrivateMessage) {
let message = ConnectedServerMessage {
message: Some(connected_server_message::Message::PrivateMessage(message)),
};
println!(
"[ProtobufClientWriter:{}] sending private message",
self.addr
);
write_message(&mut self.writer, message).await.unwrap();
}
#[deprecated]
pub async fn send_disconnect(&mut self) {
let message = ConnectedServerMessage {
message: Some(connected_server_message::Message::Disconnected(
Disconnected {
reason: "shutting down".into(),
},
)),
};
println!("[ProtobufClientWriter:{}] sending disconnect", self.addr);
write_message(&mut self.writer, message).await.unwrap();
}
}
#[async_trait]
impl ClientWriter for ProtobufClientWriter {
async fn send_clients(&mut self, clients: Vec<ClientDetails>) {
let message = ConnectedServerMessage {
message: Some(connected_server_message::Message::ConnectedClients(
ConnectedClients { clients },
)),
};
println!("[ProtobufClientWriter:{}] sending clients", self.addr);
write_message(&mut self.writer, message).await.unwrap();
}
async fn send_client_joined(&mut self, details: ClientDetails) {
let message = ConnectedServerMessage {
message: Some(connected_server_message::Message::ClientConnected(
ClientConnected {
details: Some(details),
},
)),
};
println!(
"[ProtobufClientWriter:{}] sending client connected message",
self.addr
);
write_message(&mut self.writer, message).await.unwrap();
}
async fn send_client_left(&mut self, uuid: Uuid) {
let message = ConnectedServerMessage {
message: Some(connected_server_message::Message::ClientDisconnected(
ClientDisconnected {
uuid: uuid.to_string(),
},
)),
};
println!(
"[ProtobufClientWriter:{}] sending client connected message",
self.addr
);
write_message(&mut self.writer, message).await.unwrap();
}
async fn send_global_messages(&mut self, messages: Vec<GlobalMessage>) {
let message = ConnectedServerMessage {
message: Some(connected_server_message::Message::GlobalMessages(
GlobalMessages { messages },
)),
};
println!(
"[ProtobufClientWriter:{}] sending global messages",
self.addr
);
write_message(&mut self.writer, message).await.unwrap();
}
async fn send_global_message(&mut self, message: GlobalMessage) {
let message = ConnectedServerMessage {
message: Some(connected_server_message::Message::GlobalMessage(message)),
};
println!("[ProtobufClientWriter:{}] sending disconnect", self.addr);
write_message(&mut self.writer, message).await.unwrap();
}
async fn send_private_message(&mut self, message: PrivateMessage) {
let message = ConnectedServerMessage {
message: Some(connected_server_message::Message::PrivateMessage(message)),
};
println!(
"[ProtobufClientWriter:{}] sending private message",
self.addr
);
write_message(&mut self.writer, message).await.unwrap();
}
async fn send_disconnect(&mut self) {
let message = ConnectedServerMessage {
message: Some(connected_server_message::Message::Disconnected(
Disconnected {
reason: "shutting down".into(),
},
)),
};
println!("[ProtobufClientWriter:{}] sending disconnect", self.addr);
write_message(&mut self.writer, message).await.unwrap();
}
}

View File

@ -0,0 +1,55 @@
use async_trait::async_trait;
use tokio::{net::TcpListener, sync::mpsc::UnboundedSender, task::JoinHandle};
use crate::{
network::{ConnectionType, NetworkListener},
server_va::ServerMessages,
};
/// # Listener Manager
/// This stores and awaits for connections from listeners.
/// When a connection is received, it is passed to the server
pub struct ProtobufListener {
protobuf_listener: TcpListener,
sender: UnboundedSender<ServerMessages>,
}
#[async_trait]
impl NetworkListener for ProtobufListener {
/// Binds listeners and stores them in the ListenerManager
async fn new(channel: UnboundedSender<ServerMessages>) -> Self {
println!("[ProtobufListener] setting up listeners");
let protobuf_listener = TcpListener::bind("0.0.0.0:6500")
.await
.expect("[ProtobufListener] failed to bind to 0.0.0.0:6500");
Self {
protobuf_listener,
sender: channel,
}
}
async fn run(&self) {
loop {
println!("[ProtobufListener] waiting for connection");
let accept_protobuf = self.protobuf_listener.accept().await;
let Ok((stream, addr)) = accept_protobuf else {
println!("[ProtobufListener] accept failed");
continue;
};
let msg = ServerMessages::NewConnection(
ConnectionType::ProtobufConnection(stream, addr),
);
println!("[ProtobufListener] passing message to server");
_ = self.sender.send(msg);
}
}
fn start_run(sender: UnboundedSender<ServerMessages>) -> JoinHandle<()> {
tokio::spawn(async move {
ProtobufListener::new(sender).await.run().await;
})
}
}

View File

@ -0,0 +1,174 @@
use std::{io, net::SocketAddr};
use async_trait::async_trait;
use foundation::{
networking::protobuf::{read_message, write_message},
prelude::{
network_client_message,
network_server_message,
Connect,
Connected,
GetInfo,
Info,
NetworkClientMessage,
NetworkServerMessage,
Request,
},
};
use tokio::{io::split, net::TcpStream};
use uuid::Uuid;
use crate::network::{
protobuf::{
protobuf_client_reader::ProtobufClientReader,
protobuf_client_writer::ProtobufClientWriter,
},
ClientReader,
ClientWriter,
NetworkConnection,
ServerRequest,
};
pub struct ProtobufNetworkConnection {
pub(super) stream: TcpStream,
pub(super) addr: SocketAddr,
}
impl ProtobufNetworkConnection {
pub fn new(stream: TcpStream, addr: SocketAddr) -> Self {
Self { stream, addr }
}
pub async fn get_request(&mut self) -> io::Result<ServerRequest> {
let message = NetworkServerMessage {
message: Some(network_server_message::Message::Request(Request {})),
};
println!("[ProtobufNetworkConnection] sending request");
write_message(&mut self.stream, message).await.unwrap();
println!("[ProtobufNetworkConnection] waiting for response");
let request =
read_message::<NetworkClientMessage, TcpStream>(&mut self.stream)
.await
.unwrap();
println!("[ProtobufNetworkConnection] returning request");
match request {
NetworkClientMessage {
message: Some(network_client_message::Message::GetInfo(GetInfo {})),
} => Ok(ServerRequest::GetInfo),
NetworkClientMessage {
message:
Some(network_client_message::Message::Connect(Connect {
username,
uuid,
})),
} => Ok(ServerRequest::Connect {
username,
uuid: uuid.parse().unwrap(),
addr: self.addr,
}),
_ => Ok(ServerRequest::Ignore),
}
}
pub async fn send_info(mut self, name: String, owner: String) {
let message = NetworkServerMessage {
message: Some(network_server_message::Message::GotInfo(Info {
server_name: name,
owner,
})),
};
println!("[ProtobufNetworkConnection] Sending info to client");
write_message(&mut self.stream, message).await.unwrap();
println!("[ProtobufNetworkConnection] droping connection");
}
pub async fn send_connected(
mut self,
uuid: Uuid,
) -> (ProtobufClientWriter, ProtobufClientReader) {
let message = NetworkServerMessage {
message: Some(network_server_message::Message::Connected(Connected {})),
};
write_message(&mut self.stream, message).await.unwrap();
self.into(uuid)
}
fn into(self, uuid: Uuid) -> (ProtobufClientWriter, ProtobufClientReader) {
let (read, write) = split(self.stream);
let writer = ProtobufClientWriter::new(write, self.addr, uuid);
let reader = ProtobufClientReader::new(read, self.addr, uuid);
(writer, reader)
}
}
#[async_trait]
impl NetworkConnection for ProtobufNetworkConnection {
async fn get_request(&mut self) -> io::Result<ServerRequest> {
let message = NetworkServerMessage {
message: Some(network_server_message::Message::Request(Request {})),
};
println!("[ProtobufNetworkConnection] sending request");
write_message(&mut self.stream, message).await.unwrap();
println!("[ProtobufNetworkConnection] waiting for response");
let request =
read_message::<NetworkClientMessage, TcpStream>(&mut self.stream)
.await
.unwrap();
println!("[ProtobufNetworkConnection] returning request");
match request {
NetworkClientMessage {
message: Some(network_client_message::Message::GetInfo(GetInfo {})),
} => Ok(ServerRequest::GetInfo),
NetworkClientMessage {
message:
Some(network_client_message::Message::Connect(Connect {
username,
uuid,
})),
} => Ok(ServerRequest::Connect {
username,
uuid: uuid.parse().unwrap(),
addr: self.addr,
}),
_ => Ok(ServerRequest::Ignore),
}
}
async fn send_info(mut self: Box<Self>, name: String, owner: String) {
let message = NetworkServerMessage {
message: Some(network_server_message::Message::GotInfo(Info {
server_name: name,
owner,
})),
};
println!("[ProtobufNetworkConnection] Sending info to client");
write_message(&mut self.stream, message).await.unwrap();
println!("[ProtobufNetworkConnection] droping connection");
}
async fn send_connected(
mut self: Box<Self>,
uuid: Uuid,
) -> (Box<dyn ClientWriter>, Box<dyn ClientReader>) {
let message = NetworkServerMessage {
message: Some(network_server_message::Message::Connected(Connected {})),
};
write_message(&mut self.stream, message).await.unwrap();
let (read, write) = split(self.stream);
let writer = Box::new(ProtobufClientWriter::new(write, self.addr, uuid));
let reader = Box::new(ProtobufClientReader::new(read, self.addr, uuid));
(writer, reader)
}
}

View File

@ -0,0 +1,27 @@
use tokio::sync::mpsc::UnboundedSender;
use crate::server_va::ServerMessages;
pub struct OSSignalManager {
server_channel: UnboundedSender<ServerMessages>,
}
impl OSSignalManager {
pub fn new(channel: UnboundedSender<ServerMessages>) -> Self {
Self {
server_channel: channel,
}
}
pub async fn run(&self) {
loop {
println!("[OSSignalManager] waiting for ctrl+c");
tokio::signal::ctrl_c().await.unwrap();
println!("[OSSignalManager] ctrl+c received, closing down server");
self
.server_channel
.send(ServerMessages::Exit)
.expect("[OSSignalManager] server channel closed");
}
}
}

View File

@ -0,0 +1,35 @@
use crate::plugin::WeakPluginInterface;
use foundation::event::Event;
use std::fmt::Debug;
use std::sync::Arc;
use crate::plugin::plugin_details::PluginDetails;
use std::sync::Arc;
/// # Plugin
/// Type alias for plugin objects.
pub type Plugin<T> = Arc<dyn IPlugin<T>>;
/// # GetPluginFn
/// This defines the type for getting the plugin struct from a
pub type GetPluginFn<T> = fn() -> Plugin<T>;
/// # Plugin
/// This trait defines an interface for plugins to implement.
///
/// ## Methods
/// - details: This returns the details about the plugin.
/// - init: Defines the initialisation routine for the plugin.
/// - run: defines a routine to be ran like a thread by the plugin manager.
/// - deinit: Defines the deinitalisation routine for the plugin
#[async_trait::async_trait]
pub trait IPlugin<T>: Send + Sync + Debug {
fn details(&self) -> PluginDetails;
fn on_event(&self, event: Event<T>);
fn set_interface(&self, interface: WeakPluginInterface<T>);
fn init(&self);
async fn run(&self);
fn deinit(&self);
}

12
server/src/plugin/mod.rs Normal file
View File

@ -0,0 +1,12 @@
mod plugin;
mod plugin_details;
mod plugin_entry;
mod plugin_interface;
mod plugin_manager;
mod plugin_permissions;
pub use plugin::{IPlugin, Plugin};
pub use plugin_details::PluginDetails;
pub(crate) use plugin_interface::PluginInterface;
pub use plugin_interface::WeakPluginInterface;
pub(crate) use plugin_manager::{PluginManager, PluginManagerMessage};

View File

@ -0,0 +1,171 @@
use crate::plugin::plugin_interface::IPluginInterface;
use crate::plugin::PluginInterface;
use foundation::event::Event;
use crate::event_type::EventType;
use foundation::event::EventResult;
use foundation::event::IResponder;
use serde::{Deserialize, Serialize};
use std::sync::Weak;
use futures::channel::oneshot::Receiver;
use crate::plugin::plugin::Plugin;
use crate::plugin::plugin_entry::PluginExecutionState::{Paused, Running, Stopped};
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Mutex;
use tokio::time::sleep;
pub(crate) type PluginEntryObj = Arc<PluginEntry>;
#[derive(Serialize, Deserialize, Debug, Ord, PartialOrd, Eq, PartialEq)]
pub enum PluginPermission {
Read,
Write,
ReadWrite,
None,
}
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
pub(crate) enum PluginExecutionState {
Running,
Paused,
Stopped,
}
/// # PluginEntry
/// a wrapper for plugins loaded into the server.
/// Used to provide an api for the plugin to use.
/// Also acts as gatekeeper to server data with permissions.
#[derive(Debug)]
pub(crate) struct PluginEntry<T>
where
T: Sync + Send,
{
server_permission: PluginPermission,
network_permission: PluginPermission,
client_manager_permission: PluginPermission,
client_permission: PluginPermission,
state: Arc<Mutex<PluginExecutionState>>,
plugin: Plugin<EventType<'static>>,
}
impl<T> PluginEntry<T>
where
T: Sync + Send,
{
pub fn new(plugin: Plugin<EventType>) -> Arc<PluginEntry<T>> {
let entry = Arc::new(PluginEntry {
server_permission: PluginPermission::None,
network_permission: PluginPermission::None,
client_manager_permission: PluginPermission::None,
client_permission: PluginPermission::None,
state: Arc::new(Mutex::new(Stopped)),
plugin: plugin.clone(),
});
let entry_ref = entry.clone() as PluginInterface<T>;
plugin.set_interface(Arc::downgrade(&entry_ref));
entry
}
pub(crate) async fn getState(&self) -> PluginExecutionState {
*self.state.lock().await
}
pub fn start(&self) {
let cont = self.plugin.clone();
let state = self.state.clone();
tokio::spawn(async move {
let local_state = state.clone();
let mut lock = local_state.lock().await;
match *lock {
Running => (),
Paused => {
*lock = Running;
}
Stopped => {
tokio::spawn(async move {
cont.init();
let mut lock = state.lock().await;
*lock = Running;
loop {
match *lock {
Running => cont.run().await,
Paused => sleep(Duration::new(1, 0)).await,
Stopped => break,
}
}
cont.deinit()
});
}
}
});
}
pub fn pause(&self) {
let state = self.state.clone();
tokio::spawn(async move {
let mut lock = state.lock().await;
match *lock {
Running => {
*lock = Paused;
}
Paused => (),
Stopped => (),
}
});
}
pub fn stop(&self) {
let state = self.state.clone();
tokio::spawn(async move {
let mut lock = state.lock().await;
match *lock {
Running => {
*lock = Stopped;
}
Paused => {
*lock = Stopped;
}
Stopped => (),
}
});
}
}
impl<T> IPluginInterface<T> for PluginEntry {
fn send_event(&self, _event: Event<EventType>) -> Receiver<EventResult> {
todo!()
}
}
impl IResponder<EventType<'_>> for PluginEntry {
fn on_event(&self, event: Event<EventType>) {
use EventType::{ClientAdded, Custom, NewConnection};
use PluginPermission::{None, Read, ReadWrite, Write};
match (
&event.r#type,
&self.network_permission,
&self.client_manager_permission,
&self.client_permission,
&self.server_permission,
) {
(NewConnection, Read | ReadWrite, _, _, _) => self.plugin.on_event(event),
(ClientAdded(id), _, Read | ReadWrite, _, _) => self.plugin.on_event(event),
(Custom("ping"), _, _, _, _) => println!("[PluginEntry:on_event] Ping!"),
_ => println!("[PluginEntry:on_event] not handled"),
};
}
fn get_next(&self) -> Option<Weak<dyn IResponder<EventType>>> {
todo!()
}
}

View File

@ -0,0 +1,25 @@
use foundation::event::Event;
use foundation::event::EventResult;
use foundation::event::IResponder;
use std::fmt::Debug;
use std::sync::Arc;
use std::sync::Weak;
use futures::channel::oneshot::Receiver;
pub type WeakPluginInterface<T>
where
T: Sync + Send,
= Weak<dyn IPluginInterface<T>>;
pub(crate) type PluginInterface<T>
where
T: Sync + Send,
= Arc<dyn IPluginInterface<T>>;
pub trait IPluginInterface<T>: IResponder<T> + Send + Sync + Debug
where
T: Sync + Send,
{
fn send_event(&self, event: Event<T>) -> Receiver<EventResult>;
}

View File

@ -0,0 +1,109 @@
use std::fs::Metadata;
use std::{io::Error, mem, sync::Arc};
use libloading::Library;
use tokio::fs::{create_dir, read_dir, DirEntry};
use tokio::sync::mpsc::Sender;
use tokio::sync::Mutex;
use futures::future::join_all;
use crate::plugin::plugin::GetPluginFn;
use crate::plugin::plugin_entry::{PluginEntry, PluginEntryObj};
pub enum PluginManagerMessage {
None,
}
/// # PluginManager
/// This struct handles the loading and unloading of plugins in the server
///
/// ## Attributes
/// - plugins: A [Vec] of all loaded plugins
/// - server_channel: A [Sender]
pub struct PluginManager<Out: 'static>
where
Out: From<PluginManagerMessage> + Send,
{
#[allow(dead_code)]
plugins: Mutex<Vec<PluginEntryObj>>,
#[allow(dead_code)]
server_channel: Mutex<Sender<Out>>,
}
impl<Out: 'static> PluginManager<Out>
where
Out: From<PluginManagerMessage> + Send,
{
/// Creates a new plugin manager with sender.
pub fn new(channel: Sender<Out>) -> Arc<Self> {
Arc::new(Self {
plugins: Mutex::new(Vec::new()),
server_channel: Mutex::new(channel),
})
}
/// Starts loading plugins from the plugins directory.
/// If this directory isn't found then create it get created.
pub async fn load(&self) -> Result<(), Error> {
println!("[PluginManager]: loading plugins");
println!(
"[PluginManager]: from: {}",
std::env::current_dir().unwrap().to_string_lossy()
);
if let Ok(mut plugins) = read_dir("./plugins").await {
// Todo: - make this concurrent
let mut plugin_vec = vec![];
while let Some(next) = plugins.next_entry().await? {
println!("{:?}", next);
plugin_vec.push(next)
}
// get all entries by extension
let entries: Vec<DirEntry> = plugin_vec
.into_iter()
.filter(|item| item.path().extension().unwrap_or_default() == "dylib")
.collect();
// get entry metadata
#[allow(clippy::needless_collect)] // This is a false positive. Collect is needed here
let metadata: Vec<Metadata> = join_all(entries.iter().map(|item| item.metadata()))
.await
.into_iter()
.filter_map(|item| item.ok())
.collect();
// convert correct ones to plugins
let plugins: Vec<PluginEntryObj> = entries
.into_iter()
.zip(metadata.into_iter())
.filter(|(_item, meta)| meta.is_file())
.map(|item| item.0)
.map(|item| unsafe {
let lib = Library::new(item.path()).unwrap();
let plugin_fn = lib.get::<GetPluginFn<()>>("get_plugin".as_ref()).unwrap();
PluginEntry::new(plugin_fn())
})
.collect();
println!("[PluginManager:load] got plugins: {:?}", plugins);
let mut self_vec = self.plugins.lock().await;
let _ = mem::replace(&mut *self_vec, plugins);
} else {
create_dir("./plugins").await?;
}
self
.plugins
.lock()
.await
.iter()
.for_each(|item| item.start());
Ok(())
}
}

183
server/src/server_va.rs Normal file
View File

@ -0,0 +1,183 @@
use foundation::prelude::GlobalMessage;
use tokio::{
sync::{
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
Mutex,
},
task::JoinHandle,
};
use uuid::Uuid;
use crate::{
chat::ChatManager,
connection::connection_manager::{
ConnectionManager,
ConnectionManagerMessage,
},
network::{
json::{
json_listener::JSONListener,
json_network_connection::JSONNetworkConnection,
},
protobuf::{
protobuf_listener::ProtobufListener,
protobuf_network_connection::ProtobufNetworkConnection,
},
ConnectionType,
NetworkConnection,
NetworkListener,
ServerRequest,
},
os_signal_manager::OSSignalManager,
};
/// # Server
/// Manages communication between components in the server
/// Main functions being the handling of new connections, and setting them up.
pub struct Server {
connection_manager_sender: UnboundedSender<ConnectionManagerMessage>,
chat_manager: ChatManager,
connection_manager_task: JoinHandle<()>,
listener_task: JoinHandle<()>,
json_listener_task: JoinHandle<()>,
os_event_manager_task: JoinHandle<()>,
receiver: Mutex<UnboundedReceiver<ServerMessages>>,
}
impl Server {
/// Loops the future, reading messages from the servers channel.
/// if exit is received, deconstructs all sub-tasks and exits the loop.
pub async fn run(&mut self) {
loop {
let mut lock = self.receiver.lock().await;
let msg = lock.recv().await;
drop(lock);
match msg {
Some(ServerMessages::Exit) | None => {
println!("[Server] Shutting down");
self.shutdown();
return;
}
Some(ServerMessages::NewConnection(
ConnectionType::ProtobufConnection(stream, addr),
)) => {
let conn = Box::new(ProtobufNetworkConnection::new(stream, addr));
println!("[Server] New protobuf connection");
self.handle_protobuf_connection(conn).await;
}
Some(ServerMessages::NewConnection(
ConnectionType::JsonConnection(stream, addr),
)) => {
let conn = Box::new(JSONNetworkConnection::new(stream, addr));
println!("[Server] New json connection");
self.handle_protobuf_connection(conn).await;
}
Some(ServerMessages::SendGlobalMessages(uuid)) => {
let messages = self.chat_manager.get_messages();
println!("[Server] Sending Global Messages");
_ = self.connection_manager_sender.send(
ConnectionManagerMessage::SendGlobalMessagesTo { uuid, messages },
);
}
Some(ServerMessages::AddGlobalMessage(message)) => {
self.chat_manager.add_message(message);
}
};
}
}
async fn handle_protobuf_connection(
&self,
mut conn: Box<dyn NetworkConnection>,
) {
println!("[Server] Getting request");
let req = conn.get_request().await;
let Ok(req) = req else {
println!("[Server] Got invalid request");
return;
};
match req {
ServerRequest::GetInfo => {
conn
.send_info("test server".into(), "mickyb18a@gmail.com".into())
.await
}
ServerRequest::Connect {
username,
uuid,
addr,
} => {
println!("[Server] sending connectionn and info to conneciton manager");
_ = self.connection_manager_sender.send(
ConnectionManagerMessage::AddClient {
conn,
uuid,
username,
addr,
},
);
}
ServerRequest::Ignore => todo!(),
}
}
fn shutdown(&self) {
self.os_event_manager_task.abort();
self.connection_manager_task.abort();
self.json_listener_task.abort();
self.listener_task.abort();
}
}
impl Default for Server {
fn default() -> Self {
let (tx, rx) = unbounded_channel();
let tx1 = tx.clone();
let tx2 = tx.clone();
let tx3 = tx.clone();
let tx4 = tx.clone();
let os_event_manager_task = tokio::spawn(async move {
OSSignalManager::new(tx1).run().await;
});
let listener_task = ProtobufListener::start_run(tx2);
let json_listener_task = JSONListener::start_run(tx3);
let mut connection_manager = ConnectionManager::new(tx4);
let connection_manager_sender = connection_manager.get_sender();
let connection_manager_task = tokio::spawn(async move {
connection_manager.run().await;
});
let chat_manager = ChatManager::new();
Self {
chat_manager,
os_event_manager_task,
connection_manager_task,
connection_manager_sender,
json_listener_task,
receiver: Mutex::new(rx),
listener_task,
}
}
}
/// # ServerMessage
/// enum describing all messages that the server can handle
pub enum ServerMessages {
Exit,
AddGlobalMessage(GlobalMessage),
SendGlobalMessages(Uuid),
NewConnection(ConnectionType),
}

View File

@ -1,102 +0,0 @@
use std::thread;
use crossbeam::{unbounded , Sender, Receiver};
use std::sync::Arc;
use std::sync::Mutex;
enum Message {
NewJob(Job),
Terminate,
}
pub struct ThreadPool{
workers: Vec<Worker>,
sender: Sender<Message>,
}
type Job = Box<dyn FnOnce() + Send + 'static>;
impl ThreadPool{
/// Create a new ThreadPool.
///
/// The size is the number of threads in the pool.
///
/// # Panics
///
/// The `new` function will panic if the size is zero.
pub fn new(size: usize) -> ThreadPool {
assert!(size > 0);
let (sender, receiver) = unbounded();
let receiver = Arc::new(Mutex::new(receiver));
let mut workers = Vec::with_capacity(size);
for id in 0..size {
// create some threads and store them in the vector
workers.push(Worker::new(id, Arc::clone(&receiver)));
}
ThreadPool {
workers,
sender,
}
}
pub fn execute<F>(&self, f: F) where F: FnOnce() + Send + 'static {
let job = Box::new(f);
self.sender.send(Message::NewJob(job)).unwrap();
}
}
struct Worker {
id: usize,
thread: Option<thread::JoinHandle<()>>,
}
impl Worker {
fn new(id: usize, receiver: Arc<Mutex<Receiver<Message>>>) -> Worker {
let thread = thread::spawn(move || {
loop{
let message = receiver.lock().unwrap().recv().unwrap();
match message {
Message::NewJob(job) => {
println!("Worker {} got a job; executing.", id);
job();
},
Message::Terminate => {
println!("Worker {} was told to terminate.", id);
break;
},
}
}
});
Worker {
id,
thread: Some(thread),
}
}
}
impl Drop for ThreadPool {
fn drop(&mut self) {
println!("Sending terminate message to all workers.");
for _ in &mut self.workers {
self.sender.send(Message::Terminate).unwrap();
}
println!("Shutting down all workers.");
for worker in &mut self.workers {
println!("Shutting down worker {}", worker.id);
if let Some(thread) = worker.thread.take() {
thread.join().unwrap();
}
}
}
}

View File

@ -1,18 +0,0 @@
mod server;
// mod server_v2;
use crate::server::client::client_profile::Client;
use crate::server::server_profile::Server;
use std::net::{TcpStream, TcpListener};
use rust_chat_server::ThreadPool;
use std::sync::{Arc, Barrier, Mutex};
use std::collections::HashMap;
fn main(){
let server_name = String::from("Server-01");
let server_address = String::from("0.0.0.0:6000");
let server_owner = String::from("noreply@email.com");
let server = Server::new(&server_name, &server_address, &server_owner);
server.start();
}

View File

@ -1,189 +0,0 @@
extern crate regex;
use crate::server::server_profile::Server;
use crate::server::commands::{Commands};
use std::net::{Shutdown, TcpStream};
use std::sync::Arc;
use parking_lot::FairMutex;
use std::collections::HashMap;
use dashmap::DashMap;
use std::io::prelude::*;
use std::time::Duration;
use regex::Regex;
use crossbeam::{channel, Sender, Receiver, TryRecvError};
use crossbeam_channel::unbounded;
#[derive(Clone)]
pub struct Client<'client_lifetime> {
connected: bool,
stream: Arc<TcpStream>,
uuid: String,
username: String,
address: String,
server: &'client_lifetime Server<'client_lifetime>,
tx_channel: Sender<Commands>,
rx_channel: Receiver<Commands>,
}
impl<'a> Client<'a> {
pub fn new(server: &'a Server<'a>, stream: Arc<TcpStream>, uuid: &String, username: &String, address: &String) -> Client<'a>{
let (tx_channel, rx_channel): (Sender<Commands>, Receiver<Commands>) = unbounded();
Client {
connected: true,
stream,
uuid: uuid.to_string(),
username: username.to_string(),
address: address.to_string(),
server,
tx_channel,
rx_channel,
}
}
fn get_stream(&self) -> &TcpStream{
&self.stream
}
pub fn get_transmitter(&self) -> &Sender<Commands>{
&self.tx_channel
}
pub fn get_uuid(&self) -> &String{
&self.uuid
}
pub fn get_username(&self) -> &String{
&self.username
}
pub fn get_address(&self) -> &String{
&self.address
}
pub fn handle_connection(&self){
self.stream.set_read_timeout(Some(Duration::from_millis(3000))).unwrap();
let mut buffer = [0; 1024];
while self.connected {
match self.rx_channel.try_recv() {
/*command is on the channel*/
Ok(command) => {
let a = command.clone();
match command {
Commands::Info(Some(params)) => {
self.get_stream().write_all(a.to_string().as_bytes());
/* why not do this?
*
* self.transmit_data(a.to_string().as_str());
*/
},
Commands::Disconnect(None) => {
},
Commands::ClientRemove(Some(params)) => {},
Commands::Client(Some(params)) => {
self.transmit_data(a.to_string().as_str());
todo!()
/*
* a success message needs to be read and confirmed
*/
},
Commands::Success(data) => {},
_ => {},
}
},
/*sender disconnected*/
Err(TryRecvError::Disconnected) => {},
/*no data available yet*/
Err(TryRecvError::Empty) => {},
}
match self.stream.peek(&mut buffer){
Ok(_) => {
self.get_stream().read(&mut buffer).unwrap();
let incoming_message = String::from(String::from_utf8_lossy(&buffer));
let command = Commands::from(incoming_message.clone());
println!("Request: {}", &incoming_message);
match command {
Commands::Connect(Some(params)) => todo!(),
_ => todo!(),
}
},
Err(_) => {
println!("no data peeked");
},
}
}
println!("---thread exit---");
}
// deprecated
/*
pub fn connect(&self, server: &Server, connected_clients: &Arc<Mutex<HashMap<String, Client>>>, data: &HashMap<String, String>){
let mut clients_hashmap = connected_clients.lock().unwrap();
let uuid = self.get_uuid().to_string();
clients_hashmap.insert(uuid, self.clone());
std::mem::drop(clients_hashmap);
let new_client = Commands::Client(data.clone());
server.update_all_clients(&new_client);
self.transmit_success(&String::from(""));
}
*/
pub fn disconnect(&mut self){
self.stream.shutdown(Shutdown::Both).expect("shutdown call failed");
self.connected = false;
}
pub fn transmit_data(&self, data: &str){
println!("Transmitting...");
println!("data: {}", data);
self.get_stream().write(data.to_string().as_bytes()).unwrap();
self.get_stream().flush().unwrap();
}
// deprecated
pub fn confirm_success(&self, buffer: &mut [u8; 1024], data: &String){
let success_regex = Regex::new(r###"!success:"###).unwrap();
let _ = match self.get_stream().read(&mut *buffer) {
Err(error) => self.transmit_error(&String::from("")),
Ok(success) => {
let incoming_message = String::from_utf8_lossy(&buffer[..]);
if success_regex.is_match(&incoming_message){
println!("success");
}else{
self.transmit_error(&String::from(""));
}
},
};
}
pub fn transmit_success(&self, data: &String){
let mut success_message = "!success:".to_string();
if !data.is_empty(){
success_message.push_str(&" ".to_string());
success_message.push_str(&data.to_string());
}
self.transmit_data(&success_message);
}
fn transmit_error(&self, data: &String){
let mut error_message = "!error:".to_string();
if !data.is_empty(){
error_message.push_str(&" ".to_string());
error_message.push_str(&data.to_string());
}
self.transmit_data(&error_message);
}
}

View File

@ -1 +0,0 @@
pub mod client_profile;

View File

@ -1,29 +0,0 @@
use crate::server::client::client_profile::Client;
use std::sync::Mutex;
use std::sync::Arc;
use std::collections::HashMap;
pub fn get_client_data(clients_ref: &Arc<Mutex<HashMap<String, Client>>>, data: &HashMap<String, String>) -> String{
let clients_hashmap = clients_ref.lock().unwrap();
let uuid = data.get("uuid").unwrap();
println!("uuid: {}", uuid);
for (key, value) in clients_hashmap.iter(){
println!("{}",key);
}
let client = clients_hashmap.get(uuid);
match client{
Some(data) => {
let mut message = String::from("!success:");
message.push_str(&" uuid:".to_string());
message.push_str(&data.get_uuid().to_string());
message.push_str(&" host:".to_string());
message.push_str(&data.get_address().to_string());
message.push_str(&" username:".to_string());
message.push_str(&data.get_username().to_string());
message
},
None => String::from("client not online"),
}
}

View File

@ -1,5 +0,0 @@
use crate::server::client::client_profile::Client;
pub fn format_client_data(uuid: &String, client: &Client) -> String{
["!client: username:",client.get_username(), " uuid:", uuid, " host:\"", client.get_address(), "\""].concat()
}

View File

@ -1,12 +0,0 @@
use crate::server::client::client_profile::Client;
use std::sync::Mutex;
use std::sync::Arc;
use std::collections::HashMap;
use dashmap::DashMap;
pub fn add_client(clients_ref: &Arc<Mutex<HashMap<String, Client>>>, client: &Client){
let mut clients_hashmap = clients_ref.lock().unwrap();
let uuid = client.get_uuid().to_string();
//clients_hashmap.insert(uuid, client.clone());
}

View File

@ -1,10 +0,0 @@
use crate::server::client::client_profile::Client;
use std::sync::Mutex;
use std::sync::Arc;
use std::collections::HashMap;
pub fn remove_client(clients_ref: &Arc<Mutex<HashMap<String, Client>>>, client: &Client){
let mut clients_hashmap = clients_ref.lock().unwrap();
clients_hashmap.remove(client.get_uuid()).unwrap();
}

View File

@ -1,239 +0,0 @@
mod request;
mod info;
mod success;
mod error;
mod connect;
mod disconnect;
mod client_update;
mod client_info;
mod client;
mod test;
mod message;
use crate::server::client::client_profile::Client;
use crate::server::server_profile::Server;
use std::string::ToString;
use parking_lot::FairMutex;
use std::sync::Mutex;
use std::sync::Arc;
use std::collections::HashMap;
use dashmap::DashMap;
use std::borrow::Borrow;
use regex::Regex;
use std::ops::Index;
/*
impl ClientCommands{
pub fn execute(&self, client: &mut Client, server: &Server, buffer: &mut [u8; 1024], connected_clients: &Arc<Mutex<HashMap<String, Client>>>){
let stream = client.get_stream();
match &*self{
ClientCommands::Info => {
let server_details = server.get_info();
client.transmit_success(&server_details);
},
ClientCommands::Connect(data) => {
connect::add_client(connected_clients, client);
let new_client = ServerCommands::Client(data.clone());
server.update_all_clients(&new_client);
client.transmit_success(&String::from(""));
},
ClientCommands::Disconnect => {
disconnect::remove_client(connected_clients, client);
let mut data: HashMap<String, String> = HashMap::new();
data.insert("uuid".to_string(), client.get_uuid().to_string());
let old_client = ServerCommands::ClientRemove(data);
server.update_all_clients(&old_client);
client.transmit_success(&String::from(""));
client.disconnect();
println!("disconnected!");
},
ClientCommands::ClientUpdate => {
let clients_hashmap = connected_clients.lock().unwrap();
for (key, value) in clients_hashmap.iter(){
let formatted_data = client_update::format_client_data(&key, &value);
client.transmit_data(&formatted_data);
client.confirm_success(buffer, &formatted_data);
}
client.transmit_success(&String::from(""));
client.confirm_success(buffer, &String::from("!success:"));
},
ClientCommands::ClientInfo(data) => {
let requested_data = client_info::get_client_data(connected_clients, &data);
client.transmit_data(&requested_data);
},
ClientCommands::Unknown => {
println!("Unknown Command");
},
}
}
}
impl ServerCommands{
pub fn execute(&self, client: &mut Client, buffer: &mut [u8; 1024]){
match &*self{
ServerCommands::Client(data) => {
let mut message = String::from("");
message.push_str(&"!client: name:");
message.push_str(&data.get("name").unwrap());
message.push_str(&" host:");
message.push_str(&data.get("host").unwrap());
message.push_str(&" uuid:");
message.push_str(&data.get("uuid").unwrap());
client.transmit_data(&message);
client.confirm_success(buffer, &message);
},
ServerCommands::ClientRemove(data) => {
let mut message = String::from("");
message.push_str(&"!client: uuid:");
message.push_str(&data.get("uuid").unwrap());
client.transmit_data(&message);
client.confirm_success(buffer, &message);
},
ServerCommands::Unknown => {
println!("Unknown Command!");
},
}
}
}
*/
// MARK: - commands_v2 electric boogaloo
#[derive(Clone)]
pub enum Commands {
Request(Option<HashMap<String, String>>),
Info(Option<HashMap<String, String>>),
Connect(Option<HashMap<String, String>>),
Disconnect(Option<HashMap<String, String>>),
ClientUpdate(Option<HashMap<String, String>>),
ClientInfo(Option<HashMap<String, String>>),
ClientRemove(Option<HashMap<String, String>>),
Client(Option<HashMap<String, String>>),
Success(Option<HashMap<String, String>>),
Error(Option<HashMap<String, String>>),
}
impl ToString for Commands {
fn to_string(&self) -> std::string::String {
let mut out_string = String::new();
let (command, parameters) = match self {
Commands::Info(arguments) => { ("!info:", arguments) },
Commands::Connect(arguments) => { ("!connect:", arguments) },
Commands::Disconnect(arguments) => { ("!disconnect:", arguments) },
Commands::ClientUpdate(arguments) => { ("!clientUpdate:", arguments) },
Commands::ClientInfo(arguments) => { ("!clientInfo:", arguments) },
Commands::Error(arguments) => { ("!error:", arguments) },
_ => { ("!error:", &None) }
};
out_string.push_str(command);
if parameters.is_some() {
let hash_map = parameters.borrow().as_ref().unwrap();
for (k, v) in hash_map.iter() {
out_string.push_str(" ");
out_string.push_str(k.as_str());
out_string.push_str(":");
out_string.push_str(v.as_str())
}
}
out_string
}
}
impl From<&str> for Commands {
fn from(data: &str) -> Self {
let regex = Regex::new(r###"(\?|!)([a-zA-z0-9]*):|([a-zA-z]*):([a-zA-Z0-9\-\+\[\]{}_=/]+|("(.*?)")+)"###).unwrap();
let mut iter = regex.find_iter(data);
let command = iter.next().unwrap().as_str();
println!("command: {:?}", command);
let mut map: HashMap<String, String> = HashMap::new();
for i in iter {
let parameter = i.as_str().to_string();
let mut parts:Vec<&str> = parameter.split(":").collect();
map.insert(parts.index(0).to_string(), parts.index(1).to_string());
}
let params = if map.capacity() > 1 {Some(map)} else { None };
match command {
"!request:" => Commands::Request(params),
"!info:" => Commands::Info(params),
"!connect:" => Commands::Connect(params),
"!disconnect:" => Commands::Disconnect(params),
"!clientUpdate:" => Commands::ClientUpdate(params),
"!clientInfo:" => Commands::ClientInfo(params),
"!client:" => Commands::Client(params),
"!clientRemove:" => Commands::ClientRemove(params),
"!success:" => Commands::Success(params),
"!error:" => Commands::Error(params),
_ => Commands::Error(params),
}
}
}
impl From<String> for Commands {
fn from(data: String) -> Self {
Commands::from(data.as_str())
}
}
impl From<&[u8]> for Commands {
fn from(data: &[u8]) -> Self {
let incoming_message = String::from(String::from_utf8_lossy(data)).as_str();
Commands::from(incoming_message)
}
}
#[cfg(test)]
mod test_commands_v2 {
use super::Commands;
use std::collections::HashMap;
#[test]
fn test_creation_from_string() {
let command_result = Commands::from("!connect: name:bop host:127.0.0.1 uuid:123456-1234-1234-123456");
()
}
#[test]
fn test_to_string() {
let mut a: HashMap<String, String> = HashMap::new();
a.insert("name".to_string(), "michael".to_string());
a.insert("host".to_string(), "127.0.0.1".to_string());
a.insert("uuid".to_string(), "123456-1234-1234-123456".to_string());
let command = Commands::Connect(Some(a));
println!("{:?}", command.to_string())
}
}

View File

@ -1,3 +0,0 @@
pub mod client;
pub mod commands;
pub mod server_profile;

View File

@ -1,165 +0,0 @@
extern crate regex;
use crate::server::client::client_profile::Client;
use crate::server::commands::{Commands};
use rust_chat_server::ThreadPool;
use std::collections::VecDeque;
use std::net::{TcpStream, TcpListener};
use std::sync::{Arc, Barrier, Mutex };
use crossbeam_channel::{unbounded, Sender, Receiver};
use parking_lot::FairMutex;
use std::collections::HashMap;
use dashmap::DashMap;
use std::io::prelude::*;
use regex::Regex;
pub struct Server<'server_lifetime> {
name: String,
address: String,
author: String,
connected_clients: Arc<Mutex<HashMap<String,&'server_lifetime Client<'server_lifetime>>>>,
thread_pool: ThreadPool,
}
// MARK: - server implemetation
impl<'server_lifetime> Server<'server_lifetime> {
pub fn new(name: &String, address: &String, author: &String) -> Server<'server_lifetime> {
Server{
name: name.to_string(),
address: address.to_string(),
author: author.to_string(),
connected_clients: Arc::new(Mutex::new(HashMap::new())),
thread_pool: ThreadPool::new(16)
}
}
pub fn get_address(&self) -> &String{
&self.address
}
pub fn start(&'server_lifetime self) {
let listener = TcpListener::bind(self.get_address()).unwrap();
let mut buffer = [0; 1024];
loop {
if let Ok((mut stream, addr)) = listener.accept() {
println!("Server: new connection, {}", addr);
let request = Commands::Request(None);
request.to_string();
self.transmit_data(&stream, &*request.to_string().as_str());
stream.read(&mut buffer).unwrap();
let incoming_message = String::from(String::from_utf8_lossy(&buffer));
let command = Commands::from(incoming_message);
match command {
Commands::Connect(Some(data)) => {
let uuid = data.get("uuid").unwrap();
let username = data.get("name").unwrap();
let address = data.get("host").unwrap();
let stream = Arc::new(stream);
let mut client = Client::new(self, stream, &uuid, &username, &address);
let mut clients_hashmap = self.connected_clients.lock().unwrap();
clients_hashmap.insert(uuid.to_string(), &client);
self.thread_pool.execute(move || {
client.handle_connection();
});
},
Commands::Info(None) => {
let mut params: HashMap<String, String> = HashMap::new();
params.insert(String::from("name"), self.name.clone());
params.insert(String::from("owner"), self.author.clone());
let command = Commands::Info(Some(params));
self.transmit_data(&stream, command.to_string().as_str());
},
_ => {
println!("Invalid command!");
self.transmit_data(&stream, Commands::Error(None).to_string().as_str());
},
}
}
}
}
pub fn get_info(&self, tx: Sender<Commands>) {
let mut params: HashMap<String, String> = HashMap::new();
params.insert(String::from("name"), self.name.clone());
params.insert(String::from("owner"), self.author.clone());
let command = Commands::Info(Some(params));
tx.send(command).unwrap();
}
pub fn update_all_clients(&self, command: Commands){
let clients = self.connected_clients.lock().unwrap();
for client in clients.values(){
let tx = client.get_transmitter();
tx.send(command.clone()).unwrap();
}
}
fn transmit_data(&self, mut stream: &TcpStream, data: &str){
println!("Transmitting...");
println!("data: {}",data);
stream.write(data.to_string().as_bytes()).unwrap();
stream.flush().unwrap();
}
//deprecated
/*
pub fn tokenize(&self, incoming_message: &str) -> Result<ClientCommands, &'static str>{
let command_regex = Regex::new(r###"(\?|!)([a-zA-z0-9]*):|([a-zA-z]*):([a-zA-Z0-9\-\+\[\]{}_=/]+|("(.*?)")+)"###).unwrap();
if command_regex.is_match(incoming_message){
let command = self.match_command(&incoming_message.to_string());
let command = match command{
ClientCommands::Connect(mut addons) => {
self.regex_data(&command_regex, &incoming_message.replace("!connect: ", ""), &mut addons);
ClientCommands::Connect(addons)
},
ClientCommands::ClientInfo(mut addons) => {
self.regex_data(&command_regex, &incoming_message.replace("!clientInfo: ", ""), &mut addons);
ClientCommands::ClientInfo(addons)
},
_ => {
println!("no addons");
command
},
};
Ok(command)
} else {
Err("data did not match regex!")
}
}
fn match_command(&self, command: &String) -> ClientCommands{
match command{
_ if command.starts_with("!info:") => ClientCommands::Info,
_ if command.starts_with("!connect:") => ClientCommands::Connect(HashMap::new()),
_ if command.starts_with("!disconnect:") => ClientCommands::Disconnect,
_ if command.starts_with("!clientUpdate:") => ClientCommands::ClientUpdate,
_ if command.starts_with("!clientInfo:") => ClientCommands::ClientInfo(HashMap::new()),
_ => ClientCommands::Unknown,
}
}
*/
fn regex_data(&self, command_regex: &Regex, data: &str, command_addons: &mut HashMap<String, String>){
for figure in command_regex.find_iter(data){
let segment = figure.as_str().to_string();
let contents: Vec<&str> = segment.split(":").collect();
println!("key: {}, value: {}", contents[0].to_string(), contents[1].to_string());
command_addons.insert(contents[0].to_string(), contents[1].to_string());
}
}
}

View File

@ -1,61 +0,0 @@
use std::string::ToString;
use std::sync::{Arc, Mutex, Weak};
use std::net::TcpStream;
use crate::server_v2::Serverv2;
use std::sync::mpsc::{Receiver, Sender, channel, TryRecvError};
use crate::server_v2::commands_v2::Commandsv2;
#[derive(Clone)]
pub struct ClientV2 {
pub uuid: String,
pub username: String,
pub address: String,
stream: Arc<Mutex<TcpStream>>,
server_reference: Weak<Serverv2>,
tx: Sender<Commandsv2>,
rx: Receiver<Commandsv2>,
}
impl ClientV2 {
pub fn new(stream: Arc<Mutex<TcpStream>>, server: Arc<Serverv2>, uuid: &String, username: &String, address: &String) -> ClientV2 {
let (tx, rx) = channel();
ClientV2 {
stream: stream,
server_reference: Arc::downgrade(&server),
tx,
rx,
uuid: uuid.to_string(),
username: username.to_string(),
address: address.to_string(),
}
}
pub fn run(&self) {
loop {
match self.rx.try_recv() {
Ok(Command) => {
}
Err(TryRecvError::Empty) => { }
Err(TryRecvError::Disconnected) => {
}
}
}
}
pub fn get_tx(&self) -> Sender<Commandsv2> {
self.tx.clone()
}
}

View File

@ -1,107 +0,0 @@
use std::collections::HashMap;
use std::borrow::Borrow;
use regex::Regex;
use std::ops::Index;
pub enum Commands {
Request(Option<HashMap<String, String>>),
Info(Option<HashMap<String, String>>),
Connect(Option<HashMap<String, String>>),
Disconnect(Option<HashMap<String, String>>),
ClientUpdate(Option<HashMap<String, String>>),
ClientInfo(Option<HashMap<String, String>>),
ClientRemove(Option<HashMap<String, String>>),
Client(Option<HashMap<String, String>>),
Success(Option<HashMap<String, String>>),
Error(Option<HashMap<String, String>>),
}
impl Commands {
pub fn to_String(&self) -> String {
let mut out_string = String::new();
let (command, parameters) = match self {
Commands::Info(arguments) => { ("!info:", arguments) },
Commands::Connect(arguments) => { ("!connect:", arguments) },
Commands::Disconnect(arguments) => { ("!disconnect:", arguments) },
Commands::ClientUpdate(arguments) => { ("!clientUpdate:", arguments) },
Commands::ClientInfo(arguments) => { ("!clientInfo:", arguments) },
Commands::Error(arguments) => { ("!error:", arguments) },
_ => { ("!error:", &None) }
};
out_string.push_str(command);
if parameters.is_some() {
let hash_map = parameters.borrow().as_ref().unwrap();
for (k, v) in hash_map.iter() {
out_string.push_str(" ");
out_string.push_str(k.as_str());
out_string.push_str(":");
out_string.push_str(v.as_str())
}
}
out_string
}
pub fn from_string(data: &str) -> Result<Commandsv2, &'static str> {
let regex = Regex::new(r###"(\?|!)([a-zA-z0-9]*):|([a-zA-z]*):([a-zA-Z0-9\-\+\[\]{}_=/]+|("(.*?)")+)"###).unwrap();
let mut iter = regex.find_iter(data);
let command = iter.next().unwrap().as_str();
println!("command: {:?}", command);
let mut map: HashMap<String, String> = HashMap::new();
for i in iter {
let parameter = i.as_str().to_string();
let mut parts:Vec<&str> = parameter.split(":").collect();
map.insert(parts.index(0).to_string(), parts.index(1).to_string());
}
let params = if map.capacity() > 1 {Some(map)} else { None };
match command {
"!info:" => Ok(Commands::Info(params)),
"!connect:" => Ok(Commands::Connect(params)),
"!clientInfo:" => Ok(Commands::ClientInfo(params)),
"!clientUpdate:" => Ok(Commands::ClientUpdate(params)),
"!disconnect:" => Ok(Commands::Disconnect(params)),
"!error:" => Ok(Commands::Error(params)),
_ => { Err("NOT IMPLEMENTED") }
}
}
}
#[cfg(test)]
mod test_commands_v2 {
use crate::server_v2::commands_v2::Commandsv2;
use std::collections::HashMap;
#[test]
fn test_creation_from_string() {
let command_result = Commandsv2::from_string("!connect: name:bop host:127.0.0.1 uuid:123456-1234-1234-123456");
assert!(command_result.is_ok(), true);
let command = command_result.unwrap_or(Commandsv2::Error(None));
()
}
#[test]
fn test_to_string() {
let mut a: HashMap<String, String> = HashMap::new();
a.insert("name".to_string(), "michael".to_string());
a.insert("host".to_string(), "127.0.0.1".to_string());
a.insert("uuid".to_string(), "123456-1234-1234-123456".to_string());
let command = Commandsv2::Connect(Some(a));
println!("{:?}", command.to_String())
}
}

View File

@ -1,110 +0,0 @@
pub mod client_v2;
pub mod commands_v2;
use client_v2::ClientV2;
use std::{
collections::{HashMap, VecDeque},
io,
thread,
sync::{
mpsc::{channel, Sender, Receiver},
Arc,
Mutex,
},
ops::Deref,
borrow::Borrow,
time::Duration,
net::TcpListener,
io::Read,
};
use crate::server_v2::commands_v2::Commandsv2;
use crate::lib::ThreadPool;
use std::sync::mpsc::TryRecvError;
use crate::server_v2::commands_v2::Commandsv2::Disconnect;
enum server_message {
start,
stop,
kick(String),
}
pub struct Serverv2 {
name: String,
host: String,
owner: String,
rx: Arc<Mutex<Receiver<server_message>>>,
tx: Arc<Mutex<Sender<server_message>>>,
connected_clients: Arc<Mutex<HashMap<String, ClientV2>>>,
thread_pool: ThreadPool,
}
impl Serverv2 {
pub fn new(name: String, host: String, owner: String) -> Serverv2 {
let (tx,rx) = channel();
Serverv2 {
name,
host,
owner,
rx: Arc::new(Mutex::new(rx)),
tx: Arc::new(Mutex::new(tx)),
connected_clients: Arc::new(Mutex::new(HashMap::new())),
thread_pool: ThreadPool::new(16)
}
}
pub fn start(&self) -> Result<(), io::Error> {
let listener = TcpListener::bind("0.0.0.0:6001")?;
// accepting clients
thread::spawn(move || {
match rx.lock().unwrap().try_recv() {
Ok(a) => {}
Err(TryRecvError::Empty) => {}
Err(TryRecvError::Disconnected) => {
self.connected_clients.lock()
.unwrap()
.iter()
.map(|(id, client)| {
let tx = client.get_tx();
tx.send(Disconnect(None));
});
}
}
});
Ok(())
}
pub fn stop(&self) {
}
pub fn add_client(&self, client: ClientV2) -> Result<(), &str> {
let mut client_map = self.connected_clients.lock().unwrap();
if client_map.contains_key(client.uuid.as_str()) {
return Err("!exists:");
}
client_map.insert(client.uuid.to_string(), client);
self.thread_pool.execute(|| {client.run()});
Ok(())
}
pub fn get_tx(&self, mesage: server_message) {
self.tx.clone();
}
fn log(mesaage: &str) {
println!("Server: {}", mesaage);
}
}