Skip to content

Commit 95f75ab

Browse files
authored
Efficiency improvements (#31)
* replace libwebsockets with libtws * enable app level compression
1 parent f1058a1 commit 95f75ab

17 files changed

Lines changed: 1077 additions & 622 deletions

.github/workflows/ci.yml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ env:
3636
TestUtf8
3737
TestUuid
3838
TestWorkerThread
39+
TestZstdMessageCodec
3940
4041
jobs:
4142
build-and-test:
@@ -55,12 +56,12 @@ jobs:
5556
curl \
5657
git \
5758
libcurl4-openssl-dev \
58-
libwebsockets-dev \
5959
libsqlite3-dev \
6060
uuid-dev \
6161
nlohmann-json3-dev \
6262
libssl-dev \
63-
valgrind
63+
valgrind \
64+
libzstd-dev
6465
6566
- name: Install libsodium (>=1.0.19) from source
6667
run: |
@@ -91,6 +92,15 @@ jobs:
9192
cmake --build /tmp/js-style-co-routine/build --parallel
9293
sudo cmake --install /tmp/js-style-co-routine/build
9394
sudo ldconfig
95+
96+
- name: Install tiny-websocket from source
97+
run: |
98+
set -euo pipefail
99+
git clone --depth=1 --recursive https://github.com/chemwolf6922/tiny-websocket /tmp/tiny-websocket
100+
cmake -S /tmp/tiny-websocket -B /tmp/tiny-websocket/build -DCMAKE_BUILD_TYPE="$BUILD_TYPE" -DCMAKE_POSITION_INDEPENDENT_CODE=ON
101+
cmake --build /tmp/tiny-websocket/build --parallel
102+
sudo cmake --install /tmp/tiny-websocket/build
103+
sudo ldconfig
94104
95105
- name: Configure main build
96106
run: cmake -S . -B build -DCMAKE_BUILD_TYPE="$BUILD_TYPE"

CMakeLists.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,11 @@ target_include_directories(tui-server
4545
target_link_libraries(tui-server
4646
PRIVATE
4747
curl
48-
websockets
48+
tws
4949
sqlite3
5050
uuid
51-
sodium)
51+
sodium
52+
zstd)
5253

5354
add_executable(tui-register
5455
${CMAKE_CURRENT_SOURCE_DIR}/src/tui-register.cpp

README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
## Prerequisites
22
Install the following packages from your package manager:
33
* libcurl
4-
* libwebsockets
54
* libsqlite3
65
* libuuid
76
* nlohmann-json
87
* libsodium (>=1.0.19)
98

109
For example, on debian, these can be installed:
1110
```bash
12-
sudo apt install libcurl4-openssl-dev libwebsockets-dev libsqlite3-dev uuid-dev nlohmann-json3-dev
11+
sudo apt install libcurl4-openssl-dev libsqlite3-dev uuid-dev nlohmann-json3-dev
1312
```
1413
Install the following packages from source:
1514
* [libtev-cpp](https://github.com/chemwolf6922/tiny-event-loop-cpp)
@@ -32,6 +31,16 @@ cmake ..
3231
make
3332
sudo make install
3433
```
34+
* [tiny-websocket](https://github.com/chemwolf6922/tiny-websocket)
35+
```bash
36+
git clone https://github.com/chemwolf6922/tiny-websocket
37+
cd tiny-websocket
38+
mkdir build
39+
cd build
40+
cmake ..
41+
make
42+
sudo make install
43+
```
3544
* [libsodium](https://github.com/jedisct1/libsodium)<br>
3645
Distros like debian and ubuntu only have the older version of libsodium available in their package manager. Thus, you may need to install the latest stable libsodium from source to a local location. And build and run tui-server against it.
3746
```bash

src/application/IMessageCodec.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#pragma once
2+
3+
#include <vector>
4+
#include <cstdint>
5+
6+
namespace TUI::Application
7+
{
8+
9+
class IMessageCodec
10+
{
11+
public:
12+
virtual ~IMessageCodec() = default;
13+
14+
virtual std::vector<std::uint8_t> Encode(const std::vector<std::uint8_t>& data) = 0;
15+
virtual std::vector<std::uint8_t> Decode(const std::vector<std::uint8_t>& data) = 0;
16+
};
17+
18+
}

src/application/SecureSession.cpp

Lines changed: 58 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,50 @@ using namespace TUI::Network;
1313
using namespace TUI::Application;
1414
using namespace TUI::Application::SecureSession;
1515

16+
namespace
17+
{
18+
19+
class EncryptionCodec : public IMessageCodec
20+
{
21+
public:
22+
EncryptionCodec(
23+
Cipher::ChaCha20Poly1305::Encryptor encryptor,
24+
Cipher::ChaCha20Poly1305::Decryptor decryptor)
25+
: _encryptor(std::move(encryptor)), _decryptor(std::move(decryptor))
26+
{
27+
}
28+
~EncryptionCodec() override = default;
29+
EncryptionCodec(const EncryptionCodec&) = delete;
30+
EncryptionCodec& operator=(const EncryptionCodec&) = delete;
31+
EncryptionCodec(EncryptionCodec&&) = delete;
32+
EncryptionCodec& operator=(EncryptionCodec&&) = delete;
33+
34+
std::vector<std::uint8_t> Encode(const std::vector<std::uint8_t>& data) override
35+
{
36+
return _encryptor.Encrypt(data);
37+
}
38+
39+
std::vector<std::uint8_t> Decode(const std::vector<std::uint8_t>& data) override
40+
{
41+
return _decryptor.Decrypt(data);
42+
}
43+
44+
private:
45+
Cipher::ChaCha20Poly1305::Encryptor _encryptor;
46+
Cipher::ChaCha20Poly1305::Decryptor _decryptor;
47+
};
48+
49+
}
50+
1651
/** Session */
1752

1853
Connection::Connection(
1954
std::shared_ptr<IConnection<void>> connection,
2055
const CallerId& callerId,
21-
Cipher::ChaCha20Poly1305::Encryptor encryptor,
22-
Cipher::ChaCha20Poly1305::Decryptor decryptor,
23-
bool turnOffEncryption,
24-
std::function<void(CallerId)> onClose)
25-
: _connection(std::move(connection)), _callerId(callerId), _encryptor(std::move(encryptor)),
26-
_decryptor(std::move(decryptor)), _turnOffEncryption(turnOffEncryption), _onClose(std::move(onClose))
56+
std::function<void(CallerId)> onClose,
57+
const std::vector<std::shared_ptr<IMessageCodec>>& messageCodecs)
58+
: _connection(std::move(connection)), _callerId(callerId), _onClose(std::move(onClose)),
59+
_messageCodecs(messageCodecs)
2760
{
2861
if (_connection == nullptr)
2962
{
@@ -62,9 +95,9 @@ void Connection::Send(std::vector<std::uint8_t> message)
6295
{
6396
throw std::runtime_error("Connection is closed");
6497
}
65-
if (!_turnOffEncryption)
98+
for (const auto& codec : _messageCodecs)
6699
{
67-
message = _encryptor.Encrypt(message);
100+
message = codec->Encode(message);
68101
}
69102
_connection->Send(std::move(message));
70103
}
@@ -82,9 +115,9 @@ JS::Promise<std::optional<std::vector<std::uint8_t>>> Connection::ReceiveAsync()
82115
co_return std::nullopt;
83116
}
84117
auto data = std::move(dataOpt.value());
85-
if (!_turnOffEncryption)
118+
for (auto it = _messageCodecs.rbegin(); it != _messageCodecs.rend(); ++it)
86119
{
87-
data = _decryptor.Decrypt(data);
120+
data = (*it)->Decode(data);
88121
}
89122
co_return std::move(data);
90123
}
@@ -99,16 +132,18 @@ CallerId Connection::GetId() const
99132
std::shared_ptr<IServer<CallerId>> Server::Create(
100133
Tev& tev,
101134
std::shared_ptr<IServer<void>> server,
102-
GetUserCredentialFunc getUserCredential)
135+
GetUserCredentialFunc getUserCredential,
136+
const std::vector<std::shared_ptr<IMessageCodec>>& messageCodecs)
103137
{
104-
return std::shared_ptr<Server>(new Server(tev, server, getUserCredential));
138+
return std::shared_ptr<Server>(new Server(tev, server, getUserCredential, messageCodecs));
105139
}
106140

107141
Server::Server(
108142
Tev& tev,
109143
std::shared_ptr<IServer<void>> server,
110-
GetUserCredentialFunc getUserCredential)
111-
: _tev(tev), _server(server), _getUserCredential(getUserCredential)
144+
GetUserCredentialFunc getUserCredential,
145+
const std::vector<std::shared_ptr<IMessageCodec>>& messageCodecs)
146+
: _tev(tev), _server(server), _getUserCredential(getUserCredential), _messageCodecs(messageCodecs)
112147
{
113148
if (server == nullptr || getUserCredential == nullptr)
114149
{
@@ -346,12 +381,14 @@ JS::Promise<void> Server::HandleHandshakeAsync(std::shared_ptr<IConnection<void>
346381
connection->Send(std::move(negotiationResponseCipher));
347382
/** Create the secure connection */
348383
std::weak_ptr<Server> weakThis = shared_from_this();
349-
auto secureConnection = std::make_shared<Connection>(
350-
connection,
384+
std::vector<std::shared_ptr<IMessageCodec>> codecs = _messageCodecs;
385+
if (!negotiationRequest.get_turn_off_encryption())
386+
{
387+
codecs.push_back(std::make_shared<EncryptionCodec>(std::move(encryptor), std::move(decryptor)));
388+
}
389+
auto secureConnection = std::shared_ptr<Connection>(new Connection(
390+
std::move(connection),
351391
callerId,
352-
encryptor,
353-
decryptor,
354-
negotiationRequest.get_turn_off_encryption(),
355392
[weakThis, resumptionKeyIndex](CallerId id) {
356393
auto self = weakThis.lock();
357394
if (!self)
@@ -379,7 +416,8 @@ JS::Promise<void> Server::HandleHandshakeAsync(std::shared_ptr<IConnection<void>
379416
self->_resumptionKeyTimeouts.emplace(
380417
resumptionKeyIndexStr,
381418
std::move(resumptionKeyTimeout));
382-
});
419+
},
420+
std::move(codecs)));
383421
_connections.emplace(callerId, secureConnection);
384422
/** Add the connection to the generator */
385423
_connectionGenerator.Feed(secureConnection);

src/application/SecureSession.h

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,16 @@
1616
#include "cipher/FakeCredentialGenerator.h"
1717
#include "cipher/BruteForceLimiter.h"
1818
#include "common/Cache.h"
19+
#include "IMessageCodec.h"
1920

2021
namespace TUI::Application::SecureSession
2122
{
23+
class Server;
24+
2225
class Connection : public Network::IConnection<CallerId>
2326
{
27+
friend class Server;
2428
public:
25-
/** @todo Compression? */
26-
Connection(
27-
std::shared_ptr<Network::IConnection<void>> connection,
28-
const CallerId& callerId,
29-
Cipher::ChaCha20Poly1305::Encryptor encryptor,
30-
Cipher::ChaCha20Poly1305::Decryptor decryptor,
31-
bool turnOffEncryption,
32-
std::function<void(CallerId)> onClose);
3329
~Connection() override;
3430
Connection(const Connection&) = delete;
3531
Connection& operator=(const Connection&) = delete;
@@ -43,13 +39,17 @@ namespace TUI::Application::SecureSession
4339
CallerId GetId() const override;
4440

4541
private:
42+
Connection(
43+
std::shared_ptr<Network::IConnection<void>> connection,
44+
const CallerId& callerId,
45+
std::function<void(CallerId)> onClose,
46+
const std::vector<std::shared_ptr<IMessageCodec>>& messageCodecs);
47+
4648
std::shared_ptr<Network::IConnection<void>> _connection;
4749
CallerId _callerId;
48-
Cipher::ChaCha20Poly1305::Encryptor _encryptor;
49-
Cipher::ChaCha20Poly1305::Decryptor _decryptor;
50-
bool _turnOffEncryption;
5150
std::function<void(CallerId)> _onClose;
5251
bool _closed{false};
52+
std::vector<std::shared_ptr<IMessageCodec>> _messageCodecs{};
5353
};
5454

5555
class Server : public Network::IServer<CallerId>, public std::enable_shared_from_this<Server>
@@ -71,7 +71,8 @@ namespace TUI::Application::SecureSession
7171
static std::shared_ptr<Network::IServer<CallerId>> Create(
7272
Tev& tev,
7373
std::shared_ptr<Network::IServer<void>> server,
74-
GetUserCredentialFunc getUserCredential);
74+
GetUserCredentialFunc getUserCredential,
75+
const std::vector<std::shared_ptr<IMessageCodec>>& messageCodecs = {});
7576
~Server() override;
7677
Server(const Server&) = delete;
7778
Server& operator=(const Server&) = delete;
@@ -94,11 +95,13 @@ namespace TUI::Application::SecureSession
9495
Cipher::FakeCredentialGenerator _fakeCredentialGenerator{10000};
9596
/** 5 trials per window, 5 min to 6 hours of lock out time. */
9697
Cipher::BruteForceLimiter _bruteForceLimiter{5, 5 * 60 * 1000, 6 * 60 * 60 * 1000};
98+
std::vector<std::shared_ptr<IMessageCodec>> _messageCodecs{};
9799

98100
Server(
99101
Tev& tev,
100102
std::shared_ptr<Network::IServer<void>> server,
101-
GetUserCredentialFunc getUserCredential);
103+
GetUserCredentialFunc getUserCredential,
104+
const std::vector<std::shared_ptr<IMessageCodec>>& messageCodecs);
102105

103106
JS::Promise<void> HandleRawConnections();
104107
JS::Promise<void> HandleHandshakeAsync(std::shared_ptr<Network::IConnection<void>> connection);

0 commit comments

Comments
 (0)