From 295b14e2e5875687fb74428e6c134d1a2ebc63e5 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sun, 18 Oct 2020 21:28:37 +0100 Subject: [PATCH] docs: readme, system design --- README.md | 92 +++++++++++++++ design/server.gv | 57 ++++++++++ design/server.svg | 283 ++++++++++++++++++++++++++++++++++++++++++++++ src/Transport.hs | 5 +- 4 files changed, 433 insertions(+), 4 deletions(-) create mode 100644 design/server.gv create mode 100644 design/server.svg diff --git a/README.md b/README.md index ec7b78b64..1cda22211 100644 --- a/README.md +++ b/README.md @@ -1 +1,93 @@ # simplex-messaging + +## SMP server demo + +This is a demo implementation of SMP (simplex messaging protocol) server. + +This is not usable for real applications, as it lacks the following protocol features: + +- cryptographic signature verification, instead it simply compares provided "signature" with stored "public key", effectively treating them as plain text passwords. +- there is no transport encryption + +These limitations make it easy to experiment with the protocol logic via telnet. + +You can either run it locally or try with the deployed demo server: + +```bash +telnet smp.simplex.im 5223 +``` + +## Run locally + +[Install stack](https://docs.haskellstack.org/en/stable/install_and_upgrade/) and `stack run`. + +## Usage example + +Lines you should send are prefixed with `>` character, you should not type them. + +Comments are prefixed with `--`, they are not part of transmission. + +`>` on its own means you need to press return - telnet should be configured to send CRLF. + +1. Create simplex message queue: + +```telnet +> +> +> CONN 1234 -- 1234 is recipient's key + + +IDS QuCLU4YxgS7wcPFA YB4CCATREHkaQcEh -- recipient and sender ID for the queue +``` + +2. Sender can send their "key" to the connection: + +```telnet +> -- no signature (just press enter) +> YB4CCATREHkaQcEh -- sender ID for the queue +> SEND :key abcd + +YB4CCATREHkaQcEh +OK +``` + +3. Secure queue with sender's "key" + +```telnet +> 1234 -- "signature" - same as "key" in the demo +> QuCLU4YxgS7wcPFA -- recipient ID +> KEY abcd -- "key" provided by sender + +QuCLU4YxgS7wcPFA +OK +``` + +4. Sender can now send messages to the queue + +```telnet +> abcd +> YB4CCATREHkaQcEh +> SEND :hello + +YB4CCATREHkaQcEh +OK +``` + +5. Recipient recieves the message and acknowledges it to receive further messages + +```telnet + +QuCLU4YxgS7wcPFA +MSG ECA3w3ID 2020-10-18T20:19:36.874Z 5 +hello +> 1234 +> QuCLU4YxgS7wcPFA +> ACK + +QuCLU4YxgS7wcPFA +OK +``` + +## Desgin + +![server design](design/server.svg) diff --git a/design/server.gv b/design/server.gv new file mode 100644 index 000000000..a0adc2e32 --- /dev/null +++ b/design/server.gv @@ -0,0 +1,57 @@ +digraph SMPServer { + graph [fontname=arial] + node [fontname=arial fontsize=11 shape=box] + edge [fontname=arial fontsize=10 arrowhead=open arrowtail=open] + + subgraph clusterPersistence { + label="persistence (STM)" + msgQueues [shape=cylinder label="Message\nqueues\npersistence"] + queueStore [shape=cylinder label="SMP connections\n(aka SMP queues)\npersistence"] + } + + subgraph clusterServer { + label="server threads" + main [shape=hexagon color=orange label="main\nthread"] + ss [label="server TCP socket" color=blue] + subgraph clusterThreads { + label="" + node [shape=hexagon color=orange] + runClient [label="runClient\nthread"] + server [label="server\nthread"] + } + subscribedQ [shape="larrow" label="subscribed\nTBQueue"] + main -> {server runClient} [style=dashed label=race color=orange fontcolor=orange] + ss -> runClient [color=blue] + subscribedQ -> server + } + + subgraph clusterConnection { + label="1 group per client connection" + cs [label="client connection TCP socket" color=blue] + + subgraph clusterThreads { + node [shape=hexagon, color=orange] + label="" + receive [label="receive\nthread"] + client [label="client\nthread"] + send [label="send\nthread"] + } + + runClient -> cs [style=dashed label="connect" color=blue fontcolor=blue] + runClient -> {client receive send} [style=dashed label=race color=orange fontcolor=orange] + server -> inq [label="END"] + + subscriber [shape=hexagon color=orange label="subscriber\nthread\n(only sends\n1 msg atm)"] + + inq [shape=rarrow label="receive\nTBQueue"] + outq [shape=larrow label="send\nTBQueue"] + cs -> receive -> inq -> client [color=blue] + msgQueues -> subscriber [label="Message" color=green fontcolor=green] + subscriber -> outq [label="MSG" color=blue fontcolor=blue constraint=false] + client -> queueStore [dir=both] + client -> subscriber [style=dashed label="1 fork\nper\nqueue" color=orange] + client -> msgQueues [dir="both" label="SEND,\nSUB,\nACK" color=green] + client -> outq -> send -> cs [color=blue] + client -> subscribedQ [label="(rId,\nClient)"] + } +} diff --git a/design/server.svg b/design/server.svg new file mode 100644 index 000000000..2e02ee72d --- /dev/null +++ b/design/server.svg @@ -0,0 +1,283 @@ + + + + + + +SMPServer + + +clusterPersistence + +persistence (STM) + + +clusterServer + +server threads + + +clusterThreads + + + +clusterConnection + +1 group per client connection + + +clusterThreads + + + + +msgQueues + + +Message +queues +persistence + + + +subscriber + +subscriber +thread +(only sends +1 msg atm) + + + +msgQueues->subscriber + + +Message + + + +queueStore + + +SMP connections +(aka SMP queues) +persistence + + + +main + +main +thread + + + +runClient + +runClient +thread + + + +main->runClient + + +race + + + +server + +server +thread + + + +main->server + + +race + + + +ss + +server TCP socket + + + +ss->runClient + + + + + +cs + +client connection TCP socket + + + +runClient->cs + + +connect + + + +receive + +receive +thread + + + +runClient->receive + + +race + + + +client + +client +thread + + + +runClient->client + + +race + + + +send + +send +thread + + + +runClient->send + + +race + + + +inq + +receive +TBQueue + + + +server->inq + + +END + + + +subscribedQ + +subscribed +TBQueue + + + +subscribedQ->server + + + + + +cs->receive + + + + + +receive->inq + + + + + +client->msgQueues + + + +SEND, +SUB, +ACK + + + +client->queueStore + + + + + + +client->subscribedQ + + +(rId, +Client) + + + +client->subscriber + + +1 fork +per +queue + + + +outq + +send +TBQueue + + + +client->outq + + + + + +send->cs + + + + + +inq->client + + + + + +subscriber->outq + + +MSG + + + +outq->send + + + + + diff --git a/src/Transport.hs b/src/Transport.hs index 3ecf1c3c3..2e9d226f9 100644 --- a/src/Transport.hs +++ b/src/Transport.hs @@ -62,14 +62,11 @@ startTCPClient host port = liftIO . withSocketsDo $ resolve >>= open runTCPClient :: MonadUnliftIO m => HostName -> ServiceName -> (Handle -> m a) -> m a runTCPClient host port = E.bracket (startTCPClient host port) IO.hClose -smpNewlineMode :: NewlineMode -smpNewlineMode = NewlineMode {inputNL = CRLF, outputNL = CRLF} - getSocketHandle :: MonadIO m => Socket -> m Handle getSocketHandle conn = liftIO $ do h <- socketToHandle conn ReadWriteMode hSetBinaryMode h True - hSetNewlineMode h smpNewlineMode + hSetNewlineMode h NewlineMode {inputNL = CRLF, outputNL = CRLF} hSetBuffering h LineBuffering return h