2.0.0-rc (#9)
Some checks failed
BlockStorage/repertory_osx/pipeline/head This commit looks good
BlockStorage/repertory_windows/pipeline/head This commit looks good
BlockStorage/repertory/pipeline/head There was a failure building this commit
BlockStorage/repertory_linux_builds/pipeline/head This commit looks good
BlockStorage/repertory_osx_builds/pipeline/head There was a failure building this commit

### Issues

* \#1 \[bug\] Unable to mount S3 due to 'item_not_found' exception
* \#2 Require bucket name for S3 mounts
* \#3 \[bug\] File size is not being updated in S3 mount
* \#4 Upgrade to libfuse-3.x.x
* \#5 Switch to renterd for Sia support
* \#6 Switch to cpp-httplib to further reduce dependencies
* \#7 Remove global_data and calculate used disk space per provider
* \#8 Switch to libcurl for S3 mount support

### Changes from v1.x.x

* Added read-only encrypt provider
  * Pass-through mount point that transparently encrypts source data using `XChaCha20-Poly1305`
* Added S3 encryption support via `XChaCha20-Poly1305`
* Added replay protection to remote mounts
* Added support base64 writes in remote FUSE
* Created static linked Linux binaries for `amd64` and `aarch64` using `musl-libc`
* Removed legacy Sia renter support
* Removed Skynet support
* Fixed multiple remote mount WinFSP API issues on \*NIX servers
* Implemented chunked read and write
  * Writes for non-cached files are performed in chunks of 8Mib
* Removed `repertory-ui` support
* Removed `FreeBSD` support
* Switched to `libsodium` over `CryptoPP`
* Switched to `XChaCha20-Poly1305` for remote mounts
* Updated `GoogleTest` to v1.14.0
* Updated `JSON for Modern C++` to v3.11.2
* Updated `OpenSSL` to v1.1.1w
* Updated `RocksDB` to v8.5.3
* Updated `WinFSP` to 2023
* Updated `boost` to v1.78.0
* Updated `cURL` to v8.3.0
* Updated `zlib` to v1.3
* Use `upload_manager` for all providers
  * Adds a delay to uploads to prevent excessive API calls
  * Supports re-upload after mount restart for incomplete uploads
  * NOTE: Uploads for all providers are full file (no resume support)
    * Multipart upload support is planned for S3

Reviewed-on: #9
This commit is contained in:
2023-10-29 06:55:59 +00:00
parent 3ff46723b8
commit f43c41f88a
839 changed files with 98214 additions and 92959 deletions

View File

@@ -1,29 +1,35 @@
/*
Copyright <2018-2022> <scott.e.graves@protonmail.com>
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "comm/packet/client_pool.hpp"
#include "events/events.hpp"
#include "events/event_system.hpp"
#include "events/events.hpp"
#include "utils/error_utils.hpp"
namespace repertory {
void client_pool::pool::execute(const std::uint64_t &thread_id, const worker_callback &worker,
const worker_complete_callback &worker_complete) {
const auto index = static_cast<std::size_t>(thread_id % pool_queues_.size());
void client_pool::pool::execute(
std::uint64_t thread_id, const worker_callback &worker,
const worker_complete_callback &worker_complete) {
const auto index = thread_id % pool_queues_.size();
auto wi = std::make_shared<work_item>(worker, worker_complete);
auto &pool_queue = pool_queues_[index];
@@ -33,14 +39,15 @@ void client_pool::pool::execute(const std::uint64_t &thread_id, const worker_cal
queue_lock.unlock();
}
client_pool::pool::pool(const std::uint8_t &pool_size) {
client_pool::pool::pool(std::uint8_t pool_size) {
event_system::instance().raise<service_started>("client_pool");
thread_index_ = 0u;
for (std::uint8_t i = 0u; i < pool_size; i++) {
pool_queues_.emplace_back(std::make_unique<work_queue>());
}
for (std::size_t i = 0u; i < pool_queues_.size(); i++) {
pool_threads_.emplace_back(std::thread([this]() {
pool_threads_.emplace_back([this]() {
const auto thread_index = thread_index_++;
auto &pool_queue = pool_queues_[thread_index];
@@ -58,18 +65,18 @@ client_pool::pool::pool(const std::uint8_t &pool_size) {
}
while (not queue.empty()) {
auto workItem = queue.front();
auto item = queue.front();
queue.pop_front();
queue_notify.notify_all();
queue_lock.unlock();
try {
const auto result = workItem->work();
workItem->work_complete(result);
const auto result = item->work();
item->work_complete(result);
} catch (const std::exception &e) {
workItem->work_complete(utils::translate_api_error(api_error::error));
event_system::instance().raise<repertory_exception>(__FUNCTION__,
e.what() ? e.what() : "unknown");
item->work_complete(utils::from_api_error(api_error::error));
utils::error::raise_error(__FUNCTION__, e,
"exception occurred in work item");
}
queue_lock.lock();
@@ -86,14 +93,14 @@ client_pool::pool::pool(const std::uint8_t &pool_size) {
queue_notify.notify_all();
queue_lock.unlock();
wi->work_complete(utils::translate_api_error(api_error::download_stopped));
wi->work_complete(utils::from_api_error(api_error::download_stopped));
queue_lock.lock();
}
queue_notify.notify_all();
queue_lock.unlock();
}));
});
}
}
@@ -113,7 +120,7 @@ void client_pool::pool::shutdown() {
pool_threads_.clear();
}
void client_pool::execute(const std::string &client_id, const std::uint64_t &thread_id,
void client_pool::execute(const std::string &client_id, std::uint64_t thread_id,
const worker_callback &worker,
const worker_complete_callback &worker_complete) {
unique_mutex_lock pool_lock(pool_mutex_);
@@ -136,15 +143,18 @@ void client_pool::remove_client(const std::string &client_id) {
}
void client_pool::shutdown() {
unique_mutex_lock pool_lock(pool_mutex_);
if (not shutdown_) {
shutdown_ = true;
event_system::instance().raise<service_shutdown>("client_pool");
for (auto &kv : pool_lookup_) {
kv.second->shutdown();
event_system::instance().raise<service_shutdown_begin>("client_pool");
unique_mutex_lock pool_lock(pool_mutex_);
if (not shutdown_) {
shutdown_ = true;
for (auto &kv : pool_lookup_) {
kv.second->shutdown();
}
pool_lookup_.clear();
}
pool_lookup_.clear();
pool_lock.unlock();
event_system::instance().raise<service_shutdown_end>("client_pool");
}
pool_lock.unlock();
}
} // namespace repertory

View File

@@ -1,27 +1,32 @@
/*
Copyright <2018-2022> <scott.e.graves@protonmail.com>
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "comm/packet/packet.hpp"
#include "events/events.hpp"
#include "events/event_system.hpp"
#include "events/events.hpp"
#include "types/remote.hpp"
#include "types/repertory.hpp"
#include "utils/encryption.hpp"
#include "utils/error_utils.hpp"
#include "utils/utils.hpp"
namespace repertory {
@@ -30,46 +35,47 @@ void packet::clear() {
decode_offset_ = 0u;
}
packet::error_type packet::decode(std::string &data) {
auto packet::decode(std::string &data) -> packet::error_type {
const auto *str = &buffer_[decode_offset_];
const auto length = strnlen(str, buffer_.size() - decode_offset_);
data = std::string(str, length);
decode_offset_ += (length + 1);
return utils::translate_api_error(api_error::success);
return utils::from_api_error(api_error::success);
}
packet::error_type packet::decode(std::wstring &data) {
auto packet::decode(std::wstring &data) -> packet::error_type {
std::string utf8_string;
const auto ret = decode(utf8_string);
if (ret == 0) {
data = utils::string::from_utf8(utf8_string);
}
return utils::translate_api_error(api_error::success);
return utils::from_api_error(api_error::success);
}
packet::error_type packet::decode(void *&ptr) {
auto packet::decode(void *&ptr) -> packet::error_type {
return decode(reinterpret_cast<std::uint64_t &>(ptr));
}
packet::error_type packet::decode(void *buffer, const size_t &size) {
auto packet::decode(void *buffer, std::size_t size) -> packet::error_type {
if (size) {
const auto read_size = utils::calculate_read_size(buffer_.size(), size, decode_offset_);
const auto read_size =
utils::calculate_read_size(buffer_.size(), size, decode_offset_);
if (read_size == size) {
memcpy(buffer, &buffer_[decode_offset_], size);
decode_offset_ += size;
return utils::translate_api_error(api_error::success);
return utils::from_api_error(api_error::success);
}
return ((decode_offset_ + size) > buffer_.size())
? utils::translate_api_error(api_error::buffer_overflow)
: utils::translate_api_error(api_error::buffer_too_small);
? utils::from_api_error(api_error::buffer_overflow)
: utils::from_api_error(api_error::buffer_too_small);
}
return utils::translate_api_error(api_error::success);
return utils::from_api_error(api_error::success);
}
packet::error_type packet::decode(std::int8_t &i) {
auto packet::decode(std::int8_t &i) -> packet::error_type {
const auto ret = decode(&i, sizeof(i));
if (ret == 0) {
boost::endian::big_to_native_inplace(i);
@@ -77,7 +83,7 @@ packet::error_type packet::decode(std::int8_t &i) {
return ret;
}
packet::error_type packet::decode(std::uint8_t &i) {
auto packet::decode(std::uint8_t &i) -> packet::error_type {
const auto ret = decode(&i, sizeof(i));
if (ret == 0) {
boost::endian::big_to_native_inplace(i);
@@ -85,7 +91,7 @@ packet::error_type packet::decode(std::uint8_t &i) {
return ret;
}
packet::error_type packet::decode(std::int16_t &i) {
auto packet::decode(std::int16_t &i) -> packet::error_type {
const auto ret = decode(&i, sizeof(i));
if (ret == 0) {
boost::endian::big_to_native_inplace(i);
@@ -93,7 +99,7 @@ packet::error_type packet::decode(std::int16_t &i) {
return ret;
}
packet::error_type packet::decode(std::uint16_t &i) {
auto packet::decode(std::uint16_t &i) -> packet::error_type {
const auto ret = decode(&i, sizeof(i));
if (ret == 0) {
boost::endian::big_to_native_inplace(i);
@@ -101,7 +107,7 @@ packet::error_type packet::decode(std::uint16_t &i) {
return ret;
}
packet::error_type packet::decode(std::int32_t &i) {
auto packet::decode(std::int32_t &i) -> packet::error_type {
const auto ret = decode(&i, sizeof(i));
if (ret == 0) {
boost::endian::big_to_native_inplace(i);
@@ -109,7 +115,7 @@ packet::error_type packet::decode(std::int32_t &i) {
return ret;
}
packet::error_type packet::decode(std::uint32_t &i) {
auto packet::decode(std::uint32_t &i) -> packet::error_type {
const auto ret = decode(&i, sizeof(i));
if (ret == 0) {
boost::endian::big_to_native_inplace(i);
@@ -117,7 +123,7 @@ packet::error_type packet::decode(std::uint32_t &i) {
return ret;
}
packet::error_type packet::decode(std::int64_t &i) {
auto packet::decode(std::int64_t &i) -> packet::error_type {
const auto ret = decode(&i, sizeof(i));
if (ret == 0) {
boost::endian::big_to_native_inplace(i);
@@ -125,7 +131,7 @@ packet::error_type packet::decode(std::int64_t &i) {
return ret;
}
packet::error_type packet::decode(std::uint64_t &i) {
auto packet::decode(std::uint64_t &i) -> packet::error_type {
const auto ret = decode(&i, sizeof(i));
if (ret == 0) {
boost::endian::big_to_native_inplace(i);
@@ -133,7 +139,7 @@ packet::error_type packet::decode(std::uint64_t &i) {
return ret;
}
packet::error_type packet::decode(remote::setattr_x &i) {
auto packet::decode(remote::setattr_x &i) -> packet::error_type {
const auto ret = decode(&i, sizeof(i));
if (ret == 0) {
boost::endian::big_to_native_inplace(i.acctime);
@@ -152,7 +158,7 @@ packet::error_type packet::decode(remote::setattr_x &i) {
return ret;
}
packet::error_type packet::decode(remote::stat &i) {
auto packet::decode(remote::stat &i) -> packet::error_type {
const auto ret = decode(&i, sizeof(i));
if (ret == 0) {
boost::endian::big_to_native_inplace(i.st_mode);
@@ -172,7 +178,7 @@ packet::error_type packet::decode(remote::stat &i) {
return ret;
}
packet::error_type packet::decode(remote::statfs &i) {
auto packet::decode(remote::statfs &i) -> packet::error_type {
const auto ret = decode(&i, sizeof(i));
if (ret == 0) {
boost::endian::big_to_native_inplace(i.f_bavail);
@@ -185,7 +191,7 @@ packet::error_type packet::decode(remote::statfs &i) {
return ret;
}
packet::error_type packet::decode(remote::statfs_x &i) {
auto packet::decode(remote::statfs_x &i) -> packet::error_type {
auto ret = decode(*dynamic_cast<remote::statfs *>(&i));
if (ret == 0) {
ret = decode(&i.f_mntfromname[0], 1024);
@@ -193,7 +199,7 @@ packet::error_type packet::decode(remote::statfs_x &i) {
return ret;
}
packet::error_type packet::decode(remote::file_info &i) {
auto packet::decode(remote::file_info &i) -> packet::error_type {
const auto ret = decode(&i, sizeof(i));
if (ret == 0) {
boost::endian::big_to_native_inplace(i.AllocationSize);
@@ -212,15 +218,14 @@ packet::error_type packet::decode(remote::file_info &i) {
return ret;
}
int packet::decode_json(packet &response, json &json_data) {
auto packet::decode_json(packet &response, json &json_data) -> int {
int ret = 0;
std::string data;
if ((ret = response.decode(data)) == 0) {
try {
json_data = json::parse(data);
} catch (const std::exception &e) {
event_system::instance().raise<repertory_exception>(
__FUNCTION__, e.what() ? e.what() : "Failed to parse JSON string");
utils::error::raise_error(__FUNCTION__, e, "failed to parse json string");
ret = -EIO;
}
}
@@ -228,25 +233,26 @@ int packet::decode_json(packet &response, json &json_data) {
return ret;
}
packet::error_type packet::decrypt(const std::string &token) {
auto ret = utils::translate_api_error(api_error::success);
auto packet::decrypt(const std::string &token) -> packet::error_type {
auto ret = utils::from_api_error(api_error::success);
try {
std::vector<char> result;
data_buffer result;
if (not utils::encryption::decrypt_data(token, &buffer_[decode_offset_],
buffer_.size() - decode_offset_, result)) {
throw std::runtime_error("Decryption failed");
buffer_.size() - decode_offset_,
result)) {
throw std::runtime_error("decryption failed");
}
buffer_ = std::move(result);
decode_offset_ = 0;
} catch (const std::exception &e) {
event_system::instance().raise<repertory_exception>(__FUNCTION__, e.what());
ret = utils::translate_api_error(api_error::error);
utils::error::raise_error(__FUNCTION__, e, "exception occurred");
ret = utils::from_api_error(api_error::error);
}
return ret;
}
void packet::encode(const void *buffer, const std::size_t &size, bool should_reserve) {
void packet::encode(const void *buffer, std::size_t size, bool should_reserve) {
if (size) {
if (should_reserve) {
buffer_.reserve(buffer_.size() + size);
@@ -263,11 +269,17 @@ void packet::encode(const std::string &str) {
buffer_.emplace_back(0);
}
void packet::encode(wchar_t *str) { encode(utils::string::to_utf8(str ? str : L"")); }
void packet::encode(wchar_t *str) {
encode(utils::string::to_utf8(str ? str : L""));
}
void packet::encode(const wchar_t *str) { encode(utils::string::to_utf8(str ? str : L"")); }
void packet::encode(const wchar_t *str) {
encode(utils::string::to_utf8(str ? str : L""));
}
void packet::encode(const std::wstring &str) { encode(utils::string::to_utf8(str)); }
void packet::encode(const std::wstring &str) {
encode(utils::string::to_utf8(str));
}
void packet::encode(std::int8_t i) {
boost::endian::native_to_big_inplace(i);
@@ -375,7 +387,8 @@ void packet::encode(remote::file_info i) {
encode(&i, sizeof(i), true);
}
void packet::encode_top(const void *buffer, const std::size_t &size, bool should_reserve) {
void packet::encode_top(const void *buffer, std::size_t size,
bool should_reserve) {
if (size) {
if (should_reserve) {
buffer_.reserve(buffer_.size() + size);
@@ -386,13 +399,15 @@ void packet::encode_top(const void *buffer, const std::size_t &size, bool should
}
void packet::encode_top(const std::string &str) {
const auto len = strnlen(&str[0], str.size());
buffer_.reserve(len + 1 + buffer_.size());
const auto len = strnlen(str.c_str(), str.size());
buffer_.reserve(len + 1U + buffer_.size());
encode_top(&str[0], len, false);
buffer_.insert(buffer_.begin() + len, 0);
buffer_.insert(buffer_.begin() + static_cast<std::int32_t>(len), 0);
}
void packet::encode_top(const std::wstring &str) { encode_top(utils::string::to_utf8(str)); }
void packet::encode_top(const std::wstring &str) {
encode_top(utils::string::to_utf8(str));
}
void packet::encode_top(std::int8_t i) {
boost::endian::native_to_big_inplace(i);
@@ -502,22 +517,22 @@ void packet::encode_top(remote::file_info i) {
void packet::encrypt(const std::string &token) {
try {
std::vector<char> result;
data_buffer result;
utils::encryption::encrypt_data(token, buffer_, result);
buffer_ = std::move(result);
encode_top(static_cast<std::uint32_t>(buffer_.size()));
} catch (const std::exception &e) {
event_system::instance().raise<repertory_exception>(__FUNCTION__, e.what());
utils::error::raise_error(__FUNCTION__, e, "exception occurred");
}
}
void packet::transfer_into(std::vector<char> &buffer) {
void packet::transfer_into(data_buffer &buffer) {
buffer = std::move(buffer_);
buffer_ = std::vector<char>();
buffer_ = data_buffer();
decode_offset_ = 0;
}
packet &packet::operator=(const std::vector<char> &buffer) noexcept {
auto packet::operator=(const data_buffer &buffer) noexcept -> packet & {
if (&buffer_ != &buffer) {
buffer_ = buffer;
decode_offset_ = 0;
@@ -526,7 +541,7 @@ packet &packet::operator=(const std::vector<char> &buffer) noexcept {
return *this;
}
packet &packet::operator=(std::vector<char> &&buffer) noexcept {
auto packet::operator=(data_buffer &&buffer) noexcept -> packet & {
if (&buffer_ != &buffer) {
buffer_ = std::move(buffer);
decode_offset_ = 0;
@@ -535,7 +550,7 @@ packet &packet::operator=(std::vector<char> &&buffer) noexcept {
return *this;
}
packet &packet::operator=(const packet &p) noexcept {
auto packet::operator=(const packet &p) noexcept -> packet & {
if (this != &p) {
buffer_ = p.buffer_;
decode_offset_ = p.decode_offset_;
@@ -544,7 +559,7 @@ packet &packet::operator=(const packet &p) noexcept {
return *this;
}
packet &packet::operator=(packet &&p) noexcept {
auto packet::operator=(packet &&p) noexcept -> packet & {
if (this != &p) {
buffer_ = std::move(p.buffer_);
decode_offset_ = p.decode_offset_;

View File

@@ -1,37 +1,44 @@
/*
Copyright <2018-2022> <scott.e.graves@protonmail.com>
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "comm/packet/packet_client.hpp"
#include "events/events.hpp"
#include "types/repertory.hpp"
#include "utils/error_utils.hpp"
#include "utils/timeout.hpp"
namespace repertory {
// clang-format off
E_SIMPLE2(packet_client_timeout, error, true,
std::string, event_name, en, E_STRING,
E_SIMPLE2(packet_client_timeout, error, true,
std::string, event_name, en, E_STRING,
std::string, message, msg, E_STRING
);
// clang-format on
packet_client::packet_client(std::string host_name_or_ip, const std::uint8_t &max_connections,
const std::uint16_t &port, const std::uint16_t &receive_timeout,
const std::uint16_t &send_timeout, std::string encryption_token)
packet_client::packet_client(std::string host_name_or_ip,
std::uint8_t max_connections, std::uint16_t port,
std::uint16_t receive_timeout,
std::uint16_t send_timeout,
std::string encryption_token)
: io_context_(),
host_name_or_ip_(std::move(host_name_or_ip)),
max_connections_(max_connections ? max_connections : 20u),
@@ -47,10 +54,10 @@ packet_client::~packet_client() {
io_context_.stop();
}
void packet_client::close(client &client) const {
void packet_client::close(client &cli) const {
try {
boost::system::error_code ec;
client.socket.close(ec);
cli.socket.close(ec);
} catch (...) {
}
}
@@ -65,8 +72,7 @@ void packet_client::close_all() {
unique_id_ = utils::create_uuid_string();
}
bool packet_client::connect(client &c) {
auto ret = false;
void packet_client::connect(client &c) {
try {
resolve();
boost::asio::connect(c.socket, resolve_results_);
@@ -74,16 +80,16 @@ bool packet_client::connect(client &c) {
c.socket.set_option(boost::asio::socket_base::linger(false, 0));
packet response;
read_packet(c, response);
ret = true;
const auto res = read_packet(c, response);
if (res != 0) {
throw std::runtime_error(std::to_string(res));
}
} catch (const std::exception &e) {
event_system::instance().raise<repertory_exception>(__FUNCTION__, e.what());
utils::error::raise_error(__FUNCTION__, e, "connection handshake failed");
}
return ret;
}
std::shared_ptr<packet_client::client> packet_client::get_client() {
auto packet_client::get_client() -> std::shared_ptr<packet_client::client> {
std::shared_ptr<client> ret;
unique_mutex_lock clients_lock(clients_mutex_);
@@ -109,22 +115,25 @@ void packet_client::put_client(std::shared_ptr<client> &c) {
}
}
packet::error_type packet_client::read_packet(client &c, packet &response) {
std::vector<char> buffer(sizeof(std::uint32_t));
auto packet_client::read_packet(client &c, packet &response)
-> packet::error_type {
data_buffer buffer(sizeof(std::uint32_t));
const auto read_buffer = [&]() {
std::uint32_t offset = 0u;
while (offset < buffer.size()) {
const auto bytes_read =
boost::asio::read(c.socket, boost::asio::buffer(&buffer[offset], buffer.size() - offset));
const auto bytes_read = boost::asio::read(
c.socket,
boost::asio::buffer(&buffer[offset], buffer.size() - offset));
if (bytes_read <= 0) {
throw std::runtime_error("Read failed: " + std::to_string(bytes_read));
throw std::runtime_error("read failed|" + std::to_string(bytes_read));
}
offset += static_cast<std::uint32_t>(bytes_read);
}
};
read_buffer();
const auto size = boost::endian::big_to_native(*reinterpret_cast<std::uint32_t *>(&buffer[0u]));
const auto size = boost::endian::big_to_native(
*reinterpret_cast<std::uint32_t *>(&buffer[0u]));
buffer.resize(size);
read_buffer();
@@ -140,34 +149,37 @@ packet::error_type packet_client::read_packet(client &c, packet &response) {
void packet_client::resolve() {
if (resolve_results_.empty()) {
resolve_results_ =
tcp::resolver(io_context_).resolve({host_name_or_ip_, std::to_string(port_)});
resolve_results_ = tcp::resolver(io_context_)
.resolve({host_name_or_ip_, std::to_string(port_)});
}
}
packet::error_type packet_client::send(const std::string &method, std::uint32_t &service_flags) {
auto packet_client::send(const std::string &method,
std::uint32_t &service_flags) -> packet::error_type {
packet request;
return send(method, request, service_flags);
}
packet::error_type packet_client::send(const std::string &method, packet &request,
std::uint32_t &service_flags) {
auto packet_client::send(const std::string &method, packet &request,
std::uint32_t &service_flags) -> packet::error_type {
packet response;
return send(method, request, response, service_flags);
}
packet::error_type packet_client::send(const std::string &method, packet &request, packet &response,
std::uint32_t &service_flags) {
auto packet_client::send(const std::string &method, packet &request,
packet &response, std::uint32_t &service_flags)
-> packet::error_type {
auto success = false;
packet::error_type ret = utils::translate_api_error(api_error::error);
packet::error_type ret = utils::from_api_error(api_error::error);
request.encode_top(method);
request.encode_top(utils::get_thread_id());
request.encode_top(unique_id_);
request.encode_top(PACKET_SERVICE_FLAGS);
request.encode_top(get_repertory_version());
static const auto max_attempts = 2;
for (auto i = 1; allow_connections_ && not success && (i <= max_attempts); i++) {
static const std::uint8_t max_attempts = 5u;
for (std::uint8_t i = 1u;
allow_connections_ && not success && (i <= max_attempts); i++) {
auto c = get_client();
if (c) {
try {
@@ -176,7 +188,8 @@ packet::error_type packet_client::send(const std::string &method, packet &reques
timeout request_timeout(
[this, method, c]() {
event_system::instance().raise<packet_client_timeout>("Request", method);
event_system::instance().raise<packet_client_timeout>("request",
method);
close(*c.get());
},
std::chrono::seconds(send_timeout_));
@@ -184,9 +197,11 @@ packet::error_type packet_client::send(const std::string &method, packet &reques
std::uint32_t offset = 0u;
while (offset < request.get_size()) {
const auto bytes_written = boost::asio::write(
c->socket, boost::asio::buffer(&request[offset], request.get_size() - offset));
c->socket, boost::asio::buffer(&request[offset],
request.get_size() - offset));
if (bytes_written <= 0) {
throw std::runtime_error("Write failed: " + std::to_string(bytes_written));
throw std::runtime_error("write failed|" +
std::to_string(bytes_written));
}
offset += static_cast<std::uint32_t>(bytes_written);
}
@@ -194,7 +209,8 @@ packet::error_type packet_client::send(const std::string &method, packet &reques
timeout response_timeout(
[this, method, c]() {
event_system::instance().raise<packet_client_timeout>("Response", method);
event_system::instance().raise<packet_client_timeout>("response",
method);
close(*c.get());
},
std::chrono::seconds(receive_timeout_));
@@ -202,13 +218,17 @@ packet::error_type packet_client::send(const std::string &method, packet &reques
ret = read_packet(*c, response);
response_timeout.disable();
if (ret == 0) {
response.decode(service_flags);
response.decode(ret);
success = true;
put_client(c);
if ((ret = response.decode(service_flags)) == 0) {
packet::error_type res{};
if ((ret = response.decode(res)) == 0) {
ret = res;
success = true;
put_client(c);
}
}
}
} catch (const std::exception &e) {
event_system::instance().raise<repertory_exception>(__FUNCTION__, e.what());
utils::error::raise_error(__FUNCTION__, e, "send failed");
close_all();
if (allow_connections_ && (i < max_attempts)) {
std::this_thread::sleep_for(1s);
@@ -217,7 +237,7 @@ packet::error_type packet_client::send(const std::string &method, packet &reques
}
if (not allow_connections_) {
ret = utils::translate_api_error(api_error::error);
ret = utils::from_api_error(api_error::error);
success = true;
}
}

View File

@@ -1,37 +1,49 @@
/*
Copyright <2018-2022> <scott.e.graves@protonmail.com>
Copyright <2018-2023> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "comm/packet/packet_server.hpp"
#include "events/events.hpp"
#include "events/event_system.hpp"
#include "comm/packet/packet.hpp"
#include "events/event_system.hpp"
#include "events/events.hpp"
#include "types/repertory.hpp"
#include "utils/error_utils.hpp"
#include "utils/string_utils.hpp"
#include "utils/utils.hpp"
namespace repertory {
packet_server::packet_server(const std::uint16_t &port, std::string token, std::uint8_t pool_size,
closed_callback closed, message_handler_callback message_handler)
: encryption_token_(std::move(token)), closed_(closed), message_handler_(message_handler) {
using std::thread;
packet_server::packet_server(std::uint16_t port, std::string token,
std::uint8_t pool_size, closed_callback closed,
message_handler_callback message_handler)
: encryption_token_(std::move(token)),
closed_(std::move(closed)),
message_handler_(std::move(message_handler)) {
initialize(port, pool_size);
event_system::instance().raise<service_started>("packet_server");
}
packet_server::~packet_server() {
event_system::instance().raise<service_shutdown>("packet_server");
event_system::instance().raise<service_shutdown_begin>("packet_server");
std::thread([this]() {
for (std::size_t i = 0u; i < service_threads_.size(); i++) {
io_context_.stop();
@@ -40,6 +52,7 @@ packet_server::~packet_server() {
server_thread_->join();
server_thread_.reset();
event_system::instance().raise<service_shutdown_end>("packet_server");
}
void packet_server::add_client(connection &c, const std::string &client_id) {
@@ -64,12 +77,13 @@ void packet_server::initialize(const uint16_t &port, uint8_t pool_size) {
acceptor.bind(endpoint);
acceptor.listen();
} catch (const std::exception &e) {
event_system::instance().raise<repertory_exception>(__FUNCTION__, e.what());
repertory::utils::error::raise_error(__FUNCTION__, e,
"exception occurred");
}
listen_for_connection(acceptor);
for (std::uint8_t i = 0u; i < pool_size; i++) {
service_threads_.emplace_back(std::thread([this]() { io_context_.run(); }));
service_threads_.emplace_back([this]() { io_context_.run(); });
}
for (auto &th : service_threads_) {
@@ -80,14 +94,16 @@ void packet_server::initialize(const uint16_t &port, uint8_t pool_size) {
void packet_server::listen_for_connection(tcp::acceptor &acceptor) {
auto c = std::make_shared<packet_server::connection>(io_context_, acceptor);
acceptor.async_accept(
c->socket, boost::bind(&packet_server::on_accept, this, c, boost::asio::placeholders::error));
acceptor.async_accept(c->socket,
boost::bind(&packet_server::on_accept, this, c,
boost::asio::placeholders::error));
}
void packet_server::on_accept(std::shared_ptr<connection> c, boost::system::error_code ec) {
void packet_server::on_accept(std::shared_ptr<connection> c,
boost::system::error_code ec) {
listen_for_connection(c->acceptor);
if (ec) {
event_system::instance().raise<repertory_exception>(__FUNCTION__, ec.message());
utils::error::raise_error(__FUNCTION__, ec.message());
std::this_thread::sleep_for(1s);
} else {
c->socket.set_option(boost::asio::ip::tcp::no_delay(true));
@@ -101,30 +117,34 @@ void packet_server::on_accept(std::shared_ptr<connection> c, boost::system::erro
}
void packet_server::read_header(std::shared_ptr<connection> c) {
static const std::string function_name = __FUNCTION__;
c->buffer.resize(sizeof(std::uint32_t));
boost::asio::async_read(c->socket, boost::asio::buffer(&c->buffer[0u], c->buffer.size()),
[this, c](boost::system::error_code ec, std::size_t) {
if (ec) {
remove_client(*c);
event_system::instance().raise<repertory_exception>(__FUNCTION__,
ec.message());
} else {
auto to_read = *reinterpret_cast<std::uint32_t *>(&c->buffer[0u]);
boost::endian::big_to_native_inplace(to_read);
read_packet(c, to_read);
}
});
boost::asio::async_read(
c->socket, boost::asio::buffer(&c->buffer[0u], c->buffer.size()),
[this, c](boost::system::error_code ec, std::size_t) {
if (ec) {
remove_client(*c);
repertory::utils::error::raise_error(function_name, ec.message());
} else {
auto to_read = *reinterpret_cast<std::uint32_t *>(&c->buffer[0u]);
boost::endian::big_to_native_inplace(to_read);
read_packet(c, to_read);
}
});
}
void packet_server::read_packet(std::shared_ptr<connection> c, const std::uint32_t &data_size) {
void packet_server::read_packet(std::shared_ptr<connection> c,
std::uint32_t data_size) {
try {
const auto read_buffer = [&]() {
std::uint32_t offset = 0u;
while (offset < c->buffer.size()) {
const auto bytes_read = boost::asio::read(
c->socket, boost::asio::buffer(&c->buffer[offset], c->buffer.size() - offset));
c->socket,
boost::asio::buffer(&c->buffer[offset], c->buffer.size() - offset));
if (bytes_read <= 0) {
throw std::runtime_error("read failed: " + std::to_string(bytes_read));
throw std::runtime_error("read failed|" + std::to_string(bytes_read));
}
offset += static_cast<std::uint32_t>(bytes_read);
}
@@ -147,7 +167,8 @@ void packet_server::read_packet(std::shared_ptr<connection> c, const std::uint32
std::string version;
if ((ret = request->decode(version)) == 0) {
if (utils::compare_version_strings(version, MIN_REMOTE_VERSION) >= 0) {
if (utils::compare_version_strings(
version, REPERTORY_MIN_REMOTE_VERSION) >= 0) {
std::uint32_t service_flags = 0u;
DECODE_OR_IGNORE(request, service_flags);
@@ -166,17 +187,18 @@ void packet_server::read_packet(std::shared_ptr<connection> c, const std::uint32
}
should_send_response = false;
message_handler_(service_flags, client_id, thread_id, method, request.get(),
*response,
[this, c, request, response](const packet::error_type &result) {
message_handler_(service_flags, client_id, thread_id, method,
request.get(), *response,
[this, c, request,
response](const packet::error_type &result) {
this->send_response(c, result, *response);
});
}
} else {
ret = utils::translate_api_error(api_error::incompatible_version);
ret = utils::from_api_error(api_error::incompatible_version);
}
} else {
ret = utils::translate_api_error(api_error::invalid_version);
ret = utils::from_api_error(api_error::invalid_version);
}
} else {
throw std::runtime_error("invalid nonce");
@@ -189,7 +211,7 @@ void packet_server::read_packet(std::shared_ptr<connection> c, const std::uint32
}
} catch (const std::exception &e) {
remove_client(*c);
event_system::instance().raise<repertory_exception>(__FUNCTION__, e.what());
utils::error::raise_error(__FUNCTION__, e, "exception occurred");
}
}
@@ -203,23 +225,26 @@ void packet_server::remove_client(connection &c) {
}
}
void packet_server::send_response(std::shared_ptr<connection> c, const packet::error_type &result,
void packet_server::send_response(std::shared_ptr<connection> c,
const packet::error_type &result,
packet &response) {
static const std::string function_name = __FUNCTION__;
response.encode_top(result);
response.encode_top(PACKET_SERVICE_FLAGS);
response.encode_top(c->nonce);
response.encrypt(encryption_token_);
response.transfer_into(c->buffer);
boost::asio::async_write(c->socket, boost::asio::buffer(c->buffer),
[this, c](boost::system::error_code ec, std::size_t /*length*/) {
if (ec) {
remove_client(*c);
event_system::instance().raise<repertory_exception>(__FUNCTION__,
ec.message());
} else {
read_header(c);
}
});
boost::asio::async_write(
c->socket, boost::asio::buffer(c->buffer),
[this, c](boost::system::error_code ec, std::size_t /*length*/) {
if (ec) {
remove_client(*c);
utils::error::raise_error(function_name, ec.message());
} else {
read_header(c);
}
});
}
} // namespace repertory