mirror of
https://github.com/savoirfairelinux/opendht.git
synced 2026-11-09 12:57:03 +00:00
Compare commits
38
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
538051b5e3 | ||
|
|
cdba5b33cc | ||
|
|
81bd05fe84 | ||
|
|
cf135d18b4 | ||
|
|
c7ba7f6703 | ||
|
|
c73c8c77f2 | ||
|
|
c3bb16e92f | ||
|
|
3f27cd7069 | ||
|
|
b222efe638 | ||
|
|
3c62026b5c | ||
|
|
7cf43f53ac | ||
|
|
df189e3e12 | ||
|
|
7e0e7d5483 | ||
|
|
1aeb596b04 | ||
|
|
950194f68e | ||
|
|
5dc3d3cfd7 | ||
|
|
5397f80b87 | ||
|
|
88d0af9e3d | ||
|
|
792b851d77 | ||
|
|
9da832c1d1 | ||
|
|
44f6d21c83 | ||
|
|
38454eaf6e | ||
|
|
76a2bd234a | ||
|
|
972d4d28a1 | ||
|
|
72e0a4fb6b | ||
|
|
3e2fd5e8fa | ||
|
|
8b3f7f2480 | ||
|
|
57c855e1cb | ||
|
|
56e7cd94a5 | ||
|
|
074e05cc32 | ||
|
|
251f66dfce | ||
|
|
227a7aa0d9 | ||
|
|
e12e344a79 | ||
|
|
d4fc4fd658 | ||
|
|
08d67cc8d8 | ||
|
|
208afd22f2 | ||
|
|
9eea01202a | ||
|
|
304a9992fe |
No files matched your search
@@ -7,7 +7,7 @@ jobs:
|
||||
name: Ubuntu/GCC Autotools build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Dependencies
|
||||
run: |
|
||||
sudo apt update && \
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
name: Ubuntu/GCC Meson build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Dependencies
|
||||
run: |
|
||||
sudo apt update && \
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
name: Ubuntu/GCC minimal build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Dependencies
|
||||
run: |
|
||||
sudo apt update && \
|
||||
@@ -87,16 +87,16 @@ jobs:
|
||||
|
||||
build-macos:
|
||||
name: macOS/Clang build
|
||||
runs-on: macos-12
|
||||
runs-on: macOS-15
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Dependencies
|
||||
run: |
|
||||
brew install msgpack-cxx asio gnutls nettle readline fmt jsoncpp argon2 openssl cppunit
|
||||
|
||||
- name: expected-lite
|
||||
run: |
|
||||
sudo mkdir /usr/local/include/nonstd && \
|
||||
sudo mkdir -p /usr/local/include/nonstd && \
|
||||
sudo wget https://raw.githubusercontent.com/martinmoene/expected-lite/master/include/nonstd/expected.hpp \
|
||||
-O /usr/local/include/nonstd/expected.hpp
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ jobs:
|
||||
name: Clang Static Analysis
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: deps
|
||||
run: |
|
||||
sudo apt update && \
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
|
||||
@@ -25,10 +25,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -36,12 +36,12 @@ jobs:
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_DEPS }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: docker/DockerfileDeps
|
||||
@@ -58,10 +58,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -69,13 +69,13 @@ jobs:
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: docker/Dockerfile
|
||||
@@ -92,10 +92,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -103,12 +103,12 @@ jobs:
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_DEPS_LLVM }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: docker/DockerfileDepsLlvm
|
||||
@@ -125,10 +125,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -136,13 +136,13 @@ jobs:
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LLVM }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: docker/DockerfileLlvm
|
||||
@@ -159,10 +159,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -170,13 +170,13 @@ jobs:
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME_DHTNODE }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: docker/DockerfileDhtnode
|
||||
@@ -193,10 +193,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -204,13 +204,13 @@ jobs:
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME_ALPINE_DEPS }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: docker/DockerfileDepsAlpine
|
||||
@@ -227,10 +227,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -238,13 +238,13 @@ jobs:
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME_ALPINE }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: docker/DockerfileAlpine
|
||||
@@ -262,7 +262,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: cmake
|
||||
run: |
|
||||
@@ -280,14 +280,14 @@ jobs:
|
||||
- name: build
|
||||
run: cd build && make dist
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: opendht-wheels-linux
|
||||
path: build/python/dist/*.whl
|
||||
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
draft: true
|
||||
files: build/python/dist/*.whl
|
||||
+17
-6
@@ -85,6 +85,9 @@ if (NOT MSVC)
|
||||
|
||||
if (OPENDHT_HTTP OR OPENDHT_PEER_DISCOVERY)
|
||||
find_path(ASIO_INCLUDE_DIR asio.hpp REQUIRED)
|
||||
message(STATUS "Found ASIO ${ASIO_INCLUDE_DIR}")
|
||||
else()
|
||||
message(STATUS "ASIO not required")
|
||||
endif ()
|
||||
|
||||
find_package(fmt)
|
||||
@@ -94,7 +97,7 @@ if (NOT MSVC)
|
||||
|
||||
# llhttp
|
||||
find_path(LLHTTP_INCLUDE_DIR llhttp.h)
|
||||
find_library(LLHTTP_LIBRARY libllhttp.a)
|
||||
find_library(LLHTTP_LIBRARY NAMES llhttp libllhttp)
|
||||
if (LLHTTP_INCLUDE_DIR AND LLHTTP_LIBRARY)
|
||||
message(STATUS "Found llhttp ${LLHTTP_INCLUDE_DIR} ${LLHTTP_LIBRARY}")
|
||||
add_library(llhttp_static STATIC IMPORTED)
|
||||
@@ -119,7 +122,7 @@ if (NOT MSVC)
|
||||
endif()
|
||||
endif()
|
||||
set(http_lib "-lllhttp")
|
||||
|
||||
|
||||
if (NOT Jsoncpp_FOUND)
|
||||
message(SEND_ERROR "Jsoncpp is required for DHT proxy support")
|
||||
endif()
|
||||
@@ -153,13 +156,16 @@ else ()
|
||||
${WIN32_DEP_DIR}/openssl/include
|
||||
${WIN32_DEP_DIR}/restinio/dev
|
||||
${WIN32_DEP_DIR}/fmt/include
|
||||
${WIN32_DEP_DIR}/http_parser
|
||||
${WIN32_DEP_DIR}/llhttp/include
|
||||
)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (OPENDHT_HTTP OR OPENDHT_PEER_DISCOVERY)
|
||||
add_definitions(-DASIO_STANDALONE)
|
||||
if (UNIX AND NOT APPLE)
|
||||
pkg_search_module(liburing IMPORTED_TARGET liburing)
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
if (NOT MSVC)
|
||||
@@ -337,12 +343,12 @@ if (MSVC)
|
||||
)
|
||||
if (OPENDHT_HTTP)
|
||||
add_obj_lib (win32_fmt ${WIN32_DEP_DIR}/fmt/msvc/Release/fmt.lib)
|
||||
add_obj_lib (win32_http_parser ${WIN32_DEP_DIR}/http_parser/x64/Release/http-parser.lib)
|
||||
add_obj_lib (win32_llhttp ${WIN32_DEP_DIR}/llhttp/build/Release/llhttp.lib)
|
||||
add_obj_lib (win32_ssl ${WIN32_DEP_DIR}/openssl/libssl_static.lib)
|
||||
add_obj_lib (win32_crypto ${WIN32_DEP_DIR}/openssl/libcrypto_static.lib)
|
||||
list (APPEND obj_libs
|
||||
$<TARGET_OBJECTS:win32_fmt>
|
||||
$<TARGET_OBJECTS:win32_http_parser>
|
||||
$<TARGET_OBJECTS:win32_llhttp>
|
||||
$<TARGET_OBJECTS:win32_ssl>
|
||||
$<TARGET_OBJECTS:win32_crypto>
|
||||
)
|
||||
@@ -355,7 +361,7 @@ if (MSVC)
|
||||
)
|
||||
list (APPEND win32_Libs
|
||||
${PROJECT_SOURCE_DIR}/../fmt/msvc/Release/fmt.lib
|
||||
${PROJECT_SOURCE_DIR}/../http_parser/x64/Release/http-parser.lib
|
||||
${PROJECT_SOURCE_DIR}/../llhttp/build/Release/llhttp.lib
|
||||
${PROJECT_SOURCE_DIR}/../openssl/libssl.lib
|
||||
${PROJECT_SOURCE_DIR}/../openssl/libcrypto.lib
|
||||
)
|
||||
@@ -401,6 +407,11 @@ else()
|
||||
if (OPENDHT_PROXY_OPENSSL)
|
||||
target_link_libraries(opendht PUBLIC PkgConfig::OPENSSL)
|
||||
endif()
|
||||
if (liburing_FOUND)
|
||||
set(iouring_lib ", liburing")
|
||||
target_link_libraries(opendht PUBLIC PkgConfig::liburing)
|
||||
target_compile_definitions(opendht PUBLIC ASIO_HAS_IO_URING ASIO_DISABLE_EPOLL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (BUILD_SHARED_LIBS)
|
||||
|
||||
@@ -344,6 +344,7 @@ int dht_runner_run_config(dht_runner* r, in_port_t port, const dht_runner_config
|
||||
config.push_token = conf->push_token ? std::string(conf->push_token) : std::string{};
|
||||
config.push_topic = conf->push_topic ? std::string(conf->push_topic) : std::string{};
|
||||
config.push_platform = conf->push_platform ? std::string(conf->push_platform) : std::string{};
|
||||
config.proxy_user_agent = conf->proxy_user_agent ? std::string(conf->proxy_user_agent) : std::string{};
|
||||
config.peer_discovery = conf->peer_discovery;
|
||||
config.peer_publish = conf->peer_publish;
|
||||
|
||||
|
||||
@@ -148,6 +148,7 @@ struct OPENDHT_PUBLIC dht_runner_config {
|
||||
const char* push_token;
|
||||
const char* push_topic;
|
||||
const char* push_platform;
|
||||
const char* proxy_user_agent;
|
||||
bool peer_discovery;
|
||||
bool peer_publish;
|
||||
dht_certificate* server_ca;
|
||||
|
||||
@@ -53,19 +53,21 @@ public:
|
||||
DhtProxyClient();
|
||||
|
||||
explicit DhtProxyClient(
|
||||
std::shared_ptr<crypto::Certificate> serverCA, crypto::Identity clientIdentity,
|
||||
std::function<void()> loopSignal, const std::string& serverHost,
|
||||
const std::string& pushClientId = "", std::shared_ptr<Logger> logger = {});
|
||||
std::shared_ptr<crypto::Certificate> serverCA,
|
||||
crypto::Identity clientIdentity,
|
||||
std::function<void()> loopSignal,
|
||||
const std::string& serverHost,
|
||||
const std::string& userAgent = "",
|
||||
const std::string& pushClientId = "",
|
||||
const std::string& pushToken = "",
|
||||
const std::string& pushTopic = "",
|
||||
const std::string& pushPlatform = "",
|
||||
std::shared_ptr<Logger> logger = {}
|
||||
);
|
||||
|
||||
void setHeaderFields(http::Request& request);
|
||||
|
||||
virtual void setPushNotificationToken(const std::string& token) override {
|
||||
#ifdef OPENDHT_PUSH_NOTIFICATIONS
|
||||
deviceKey_ = token;
|
||||
#else
|
||||
(void) token;
|
||||
#endif
|
||||
}
|
||||
virtual void setPushNotificationToken(const std::string& token) override;
|
||||
|
||||
virtual void setPushNotificationTopic(const std::string& topic) override {
|
||||
#ifdef OPENDHT_PUSH_NOTIFICATIONS
|
||||
@@ -351,7 +353,7 @@ private:
|
||||
std::string proxyUrl_;
|
||||
dht::crypto::Identity clientIdentity_;
|
||||
std::shared_ptr<dht::crypto::Certificate> serverCertificate_;
|
||||
//std::pair<std::string, std::string> serverHostService_;
|
||||
std::string userAgent_ {"OpenDHT"};
|
||||
std::string pushClientId_;
|
||||
std::string pushSessionId_;
|
||||
|
||||
|
||||
@@ -98,6 +98,32 @@ public:
|
||||
|
||||
asio::io_context& io_context() const;
|
||||
|
||||
using clock = std::chrono::steady_clock;
|
||||
using time_point = clock::time_point;
|
||||
|
||||
struct PushStats {
|
||||
uint64_t highPriorityCount {0};
|
||||
uint64_t normalPriorityCount {0};
|
||||
|
||||
void increment(bool highPriority) {
|
||||
if (highPriority)
|
||||
highPriorityCount++;
|
||||
else
|
||||
normalPriorityCount++;
|
||||
}
|
||||
|
||||
Json::Value toJson() const {
|
||||
Json::Value val;
|
||||
val["highPriorityCount"] = static_cast<Json::UInt64>(highPriorityCount);
|
||||
val["normalPriorityCount"] = static_cast<Json::UInt64>(normalPriorityCount);
|
||||
return val;
|
||||
}
|
||||
|
||||
std::string toString() const {
|
||||
return fmt::format("{} high priority, {} normal priority", highPriorityCount, normalPriorityCount);
|
||||
}
|
||||
};
|
||||
|
||||
struct ServerStats {
|
||||
/** Current number of listen operations */
|
||||
size_t listenCount {0};
|
||||
@@ -107,24 +133,39 @@ public:
|
||||
size_t totalPermanentPuts {0};
|
||||
/** Current number of push tokens with at least one listen operation */
|
||||
size_t pushListenersCount {0};
|
||||
|
||||
/** Time at which the server was started */
|
||||
time_point serverStartTime;
|
||||
/** Last time at which the stats were updated */
|
||||
time_point lastUpdated;
|
||||
/** Total number of push notification requests that the server attempted to
|
||||
* send since being started, broken down by type and priority level */
|
||||
PushStats androidPush;
|
||||
PushStats iosPush;
|
||||
PushStats unifiedPush;
|
||||
|
||||
/** Average requests per second */
|
||||
double requestRate {0};
|
||||
/** Node Info **/
|
||||
std::shared_ptr<NodeInfo> nodeInfo {};
|
||||
|
||||
std::string toString() const {
|
||||
std::ostringstream ss;
|
||||
ss << "Listens: " << listenCount << " Puts: " << putCount << " PushListeners: " << pushListenersCount << std::endl;
|
||||
ss << "Requests: " << requestRate << " per second." << std::endl;
|
||||
auto ret = fmt::format("Listens: {}, Puts: {}, PushListeners: {}\n"
|
||||
"Push requests in the last {}: [Android: {}], [iOS: {}], [Unified: {}]\n"
|
||||
"Requests: {} per second.",
|
||||
listenCount, putCount, pushListenersCount,
|
||||
print_duration(lastUpdated - serverStartTime),
|
||||
androidPush.toString(), iosPush.toString(), unifiedPush.toString(),
|
||||
requestRate);
|
||||
if (nodeInfo) {
|
||||
auto& ipv4 = nodeInfo->ipv4;
|
||||
if (ipv4.table_depth > 1)
|
||||
ss << "IPv4 Network estimation: " << ipv4.getNetworkSizeEstimation() << std::endl;;
|
||||
ret += fmt::format("IPv4 Network estimation: {}\n", ipv4.getNetworkSizeEstimation());
|
||||
auto& ipv6 = nodeInfo->ipv6;
|
||||
if (ipv6.table_depth > 1)
|
||||
ss << "IPv6 Network estimation: " << ipv6.getNetworkSizeEstimation() << std::endl;;
|
||||
ret += fmt::format("IPv6 Network estimation: {}\n", ipv6.getNetworkSizeEstimation());
|
||||
}
|
||||
return ss.str();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,6 +177,11 @@ public:
|
||||
result["putCount"] = static_cast<Json::UInt64>(putCount);
|
||||
result["totalPermanentPuts"] = static_cast<Json::UInt64>(totalPermanentPuts);
|
||||
result["pushListenersCount"] = static_cast<Json::UInt64>(pushListenersCount);
|
||||
result["serverStartTime"] = static_cast<Json::LargestInt>(to_time_t(serverStartTime));
|
||||
result["lastUpdated"] = static_cast<Json::LargestInt>(to_time_t(lastUpdated));
|
||||
result["androidPush"] = androidPush.toJson();
|
||||
result["iosPush"] = iosPush.toJson();
|
||||
result["unifiedPush"] = unifiedPush.toJson();
|
||||
result["requestRate"] = requestRate;
|
||||
if (nodeInfo)
|
||||
result["nodeInfo"] = nodeInfo->toJson();
|
||||
@@ -366,9 +412,6 @@ private:
|
||||
template <typename Is>
|
||||
void loadState(Is& is, size_t size);
|
||||
|
||||
using clock = std::chrono::steady_clock;
|
||||
using time_point = clock::time_point;
|
||||
|
||||
std::shared_ptr<asio::io_context> ioContext_;
|
||||
std::shared_ptr<DhtRunner> dht_;
|
||||
Json::StreamWriterBuilder jsonBuilder_;
|
||||
@@ -393,6 +436,11 @@ private:
|
||||
std::shared_ptr<ServerStats> stats_;
|
||||
std::shared_ptr<NodeInfo> nodeInfo_ {};
|
||||
std::unique_ptr<asio::steady_timer> printStatsTimer_;
|
||||
const time_point serverStartTime_;
|
||||
mutable std::mutex pushStatsMutex_;
|
||||
PushStats androidPush_;
|
||||
PushStats iosPush_;
|
||||
PushStats unifiedPush_;
|
||||
|
||||
// Thread-safe access to listeners map.
|
||||
std::mutex lockListener_;
|
||||
|
||||
@@ -64,6 +64,7 @@ public:
|
||||
std::string push_token {};
|
||||
std::string push_topic {};
|
||||
std::string push_platform {};
|
||||
std::string proxy_user_agent {};
|
||||
bool peer_discovery {false};
|
||||
bool peer_publish {false};
|
||||
std::shared_ptr<dht::crypto::Certificate> server_ca;
|
||||
@@ -409,7 +410,8 @@ public:
|
||||
void run(const Config& config, Context&& context);
|
||||
|
||||
void setOnStatusChanged(StatusCallback&& cb) {
|
||||
statusCb = std::move(cb);
|
||||
if (cb)
|
||||
statusCbs.emplace_back(std::move(cb));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -519,7 +521,8 @@ private:
|
||||
|
||||
NodeStatus status4 {NodeStatus::Disconnected},
|
||||
status6 {NodeStatus::Disconnected};
|
||||
StatusCallback statusCb {nullptr};
|
||||
|
||||
std::vector<StatusCallback> statusCbs {};
|
||||
|
||||
/** PeerDiscovery Parameters */
|
||||
std::shared_ptr<PeerDiscovery> peerDiscovery_;
|
||||
|
||||
@@ -72,7 +72,7 @@ class OPENDHT_PUBLIC Url
|
||||
{
|
||||
public:
|
||||
Url() = default;
|
||||
Url(const std::string& url);
|
||||
Url(std::string_view url);
|
||||
std::string url;
|
||||
std::string protocol {"http"};
|
||||
std::string host;
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
#include "sockaddr.h"
|
||||
#include "infohash.h"
|
||||
#include "logger.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <asio/steady_timer.hpp>
|
||||
#include <thread>
|
||||
|
||||
namespace asio {
|
||||
@@ -36,31 +38,34 @@ class OPENDHT_PUBLIC PeerDiscovery
|
||||
{
|
||||
public:
|
||||
static constexpr in_port_t DEFAULT_PORT = 8888;
|
||||
using ServiceDiscoveredCallback = std::function<void(msgpack::object&&, SockAddr&&)>;
|
||||
using ServiceDiscoveredCallback = std::function<void(msgpack::object &&, SockAddr &&)>;
|
||||
|
||||
PeerDiscovery(in_port_t port = DEFAULT_PORT, std::shared_ptr<asio::io_context> ioContext = {}, std::shared_ptr<Logger> logger = {});
|
||||
PeerDiscovery(in_port_t port = DEFAULT_PORT,
|
||||
std::shared_ptr<asio::io_context> ioContext = {},
|
||||
std::shared_ptr<Logger> logger = {});
|
||||
~PeerDiscovery();
|
||||
|
||||
/**
|
||||
* startDiscovery - Keep Listening data from the sender until node is joinned or stop is called
|
||||
*/
|
||||
* startDiscovery - Keep Listening data from the sender until node is joinned
|
||||
* or stop is called
|
||||
*/
|
||||
void startDiscovery(const std::string &type, ServiceDiscoveredCallback callback);
|
||||
|
||||
template<typename T>
|
||||
void startDiscovery(const std::string &type, std::function<void(T&&, SockAddr&&)> cb) {
|
||||
startDiscovery(type, [cb](msgpack::object&& ob, SockAddr&& addr) {
|
||||
template <typename T>
|
||||
void startDiscovery(const std::string &type, std::function<void(T &&, SockAddr &&)> cb) {
|
||||
startDiscovery(type, [cb](msgpack::object &&ob, SockAddr &&addr) {
|
||||
cb(ob.as<T>(), std::move(addr));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* startPublish - Keeping sending data until node is joinned or stop is called
|
||||
*/
|
||||
*/
|
||||
void startPublish(const std::string &type, const msgpack::sbuffer &pack_buf);
|
||||
void startPublish(sa_family_t domain, const std::string &type, const msgpack::sbuffer &pack_buf);
|
||||
|
||||
template<typename T>
|
||||
void startPublish(const std::string &type, const T& object) {
|
||||
template <typename T>
|
||||
void startPublish(const std::string &type, const T &object) {
|
||||
msgpack::sbuffer buf;
|
||||
msgpack::pack(buf, object);
|
||||
startPublish(type, buf);
|
||||
@@ -68,22 +73,24 @@ public:
|
||||
|
||||
/**
|
||||
* Thread Stopper
|
||||
*/
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* Remove possible callBack to discovery
|
||||
*/
|
||||
*/
|
||||
bool stopDiscovery(const std::string &type);
|
||||
|
||||
/**
|
||||
* Remove different serivce message to send
|
||||
*/
|
||||
*/
|
||||
bool stopPublish(const std::string &type);
|
||||
bool stopPublish(sa_family_t domain, const std::string &type);
|
||||
|
||||
void connectivityChanged();
|
||||
|
||||
void stopConnectivityChanged();
|
||||
|
||||
private:
|
||||
class DomainPeerDiscovery;
|
||||
std::unique_ptr<DomainPeerDiscovery> peerDiscovery4_;
|
||||
@@ -92,4 +99,4 @@ private:
|
||||
std::thread ioRunnner_;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace dht
|
||||
+9
-22
@@ -21,6 +21,7 @@
|
||||
#include "def.h"
|
||||
|
||||
#include <msgpack.hpp>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <random>
|
||||
@@ -60,7 +61,7 @@ void erase_if(std::map<Key, Item>& map, const Condition& condition)
|
||||
* Split "[host]:port" or "host:port" to pair<"host", "port">.
|
||||
*/
|
||||
OPENDHT_PUBLIC std::pair<std::string, std::string>
|
||||
splitPort(const std::string& s);
|
||||
splitPort(std::string_view s);
|
||||
|
||||
class OPENDHT_PUBLIC DhtException : public std::runtime_error {
|
||||
public:
|
||||
@@ -84,37 +85,23 @@ using duration = clock::duration;
|
||||
time_point from_time_t(std::time_t t);
|
||||
std::time_t to_time_t(time_point t);
|
||||
|
||||
inline std::string
|
||||
to_str(double d) {
|
||||
char buf[16];
|
||||
auto ret = snprintf(buf, sizeof(buf), "%.3g", d);
|
||||
return (ret < 0) ? std::to_string(d) : std::string(buf, ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts std::chrono::duration to floating-point seconds.
|
||||
*/
|
||||
template <class DT>
|
||||
static double
|
||||
print_dt(DT d) {
|
||||
return std::chrono::duration_cast<std::chrono::duration<double>>(d).count();
|
||||
}
|
||||
|
||||
template <class DT>
|
||||
static std::string
|
||||
print_duration(DT d) {
|
||||
if (d < std::chrono::seconds(0)) {
|
||||
return "-" + print_duration(-d);
|
||||
} else if (d < std::chrono::milliseconds(1)) {
|
||||
return to_str(std::chrono::duration_cast<std::chrono::duration<double, std::micro>>(d).count()) + " us";
|
||||
return fmt::format("{:.3g} us", std::chrono::duration_cast<std::chrono::duration<double, std::micro>>(d).count());
|
||||
} else if (d < std::chrono::seconds(1)) {
|
||||
return to_str(std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(d).count()) + " ms";
|
||||
return fmt::format("{:.3g} ms", std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(d).count());
|
||||
} else if (d < std::chrono::minutes(1)) {
|
||||
return to_str(std::chrono::duration_cast<std::chrono::duration<double>>(d).count()) + " s";
|
||||
return fmt::format("{:.3g} s", std::chrono::duration_cast<std::chrono::duration<double>>(d).count());
|
||||
} else if (d < std::chrono::hours(1)) {
|
||||
return to_str(std::chrono::duration_cast<std::chrono::duration<double, std::ratio<60>>>(d).count()) + " min";
|
||||
return fmt::format("{:.3g} min", std::chrono::duration_cast<std::chrono::duration<double, std::ratio<60>>>(d).count());
|
||||
} else if (d < std::chrono::hours(72)) {
|
||||
return fmt::format("{:.3g} h", std::chrono::duration_cast<std::chrono::duration<double, std::ratio<3600>>>(d).count());
|
||||
} else {
|
||||
return to_str(std::chrono::duration_cast<std::chrono::duration<double, std::ratio<3600>>>(d).count()) + " h";
|
||||
return fmt::format("{:.3g} days", std::chrono::duration_cast<std::chrono::duration<double, std::ratio<86400>>>(d).count());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
-4
@@ -13,10 +13,16 @@ openssl = dependency('openssl', required: get_option('proxy_client'))
|
||||
jsoncpp = dependency('jsoncpp', required: get_option('proxy_client'))
|
||||
fmt = dependency('fmt')
|
||||
llhttp = dependency('llhttp', 'libllhttp', required: get_option('proxy_client'))
|
||||
io_uring = dependency('liburing', required: false)
|
||||
|
||||
deps = [fmt, gnutls, nettle, msgpack, argon2, openssl, jsoncpp, llhttp]
|
||||
deps = [fmt, gnutls, nettle, msgpack, argon2, openssl, jsoncpp, llhttp, io_uring]
|
||||
conf_data = configuration_data()
|
||||
|
||||
add_project_arguments('-DMSGPACK_NO_BOOST', language : 'cpp')
|
||||
add_project_arguments('-DMSGPACK_NO_BOOST', '-DASIO_STANDALONE', language : 'cpp')
|
||||
if io_uring.found()
|
||||
add_project_arguments('-DASIO_HAS_IO_URING', '-DASIO_DISABLE_EPOLL', language : 'cpp')
|
||||
conf_data.set('iouring_lib', ', liburing')
|
||||
endif
|
||||
add_project_arguments(['-Wno-return-type','-Wno-deprecated','-Wnon-virtual-dtor','-pedantic-errors','-fvisibility=hidden'], language : 'cpp')
|
||||
|
||||
opendht_inc = include_directories('include/opendht')
|
||||
@@ -40,8 +46,6 @@ opendht_src = [
|
||||
'src/thread_pool.cpp',
|
||||
]
|
||||
|
||||
conf_data = configuration_data()
|
||||
|
||||
if get_option('indexation').enabled()
|
||||
opendht_src += 'src/indexation/pht.cpp'
|
||||
add_project_arguments('-DOPENDHT_INDEXATION', language : 'cpp')
|
||||
|
||||
+1
-1
@@ -8,5 +8,5 @@ Version: @VERSION@
|
||||
Libs: -L${libdir} -lopendht
|
||||
Libs.private: @http_lib@ -pthread
|
||||
Requires: gnutls >= 3.3@jsoncpp_lib@@openssl_lib@
|
||||
Requires.private: nettle >= 2.4@argon2_lib@
|
||||
Requires.private: nettle >= 2.4@argon2_lib@@iouring_lib@
|
||||
Cflags: -I${includedir}
|
||||
+2
-1
@@ -500,12 +500,13 @@ cdef class DhtConfig(object):
|
||||
def setRateLimit(self, ssize_t max_req_per_sec, ssize_t max_peer_req_per_sec):
|
||||
self._config.dht_config.node_config.max_req_per_sec = max_req_per_sec
|
||||
self._config.dht_config.node_config.max_peer_req_per_sec = max_peer_req_per_sec
|
||||
def setProxyInfo(self, str proxy_server, str push_node_id="", str push_token="", str push_topic="", str push_platform="", Certificate server_ca = Certificate()):
|
||||
def setProxyInfo(self, str proxy_server, str push_node_id="", str push_token="", str push_topic="", str push_platform="", str user_agent="", Certificate server_ca = Certificate()):
|
||||
self._config.proxy_server = proxy_server.encode()
|
||||
self._config.push_node_id = push_node_id.encode()
|
||||
self._config.push_token = push_token.encode()
|
||||
self._config.push_topic = push_topic.encode()
|
||||
self._config.push_platform = push_platform.encode()
|
||||
self._config.proxy_user_agent = user_agent.encode()
|
||||
self._config.server_ca = server_ca._cert
|
||||
def setPeerDiscovery(self, bool peer_discovery, bool peer_publish):
|
||||
self._config.peer_discovery = peer_discovery
|
||||
|
||||
@@ -246,6 +246,7 @@ cdef extern from "opendht/dhtrunner.h" namespace "dht":
|
||||
string push_token
|
||||
string push_topic
|
||||
string push_platform
|
||||
string proxy_user_agent
|
||||
bool peer_discovery
|
||||
bool peer_publish
|
||||
shared_ptr[Certificate] server_ca
|
||||
|
||||
@@ -49,6 +49,7 @@ impl DhtRunnerConfig {
|
||||
push_token: ptr::null(),
|
||||
push_topic: ptr::null(),
|
||||
push_platform: ptr::null(),
|
||||
proxy_user_agent: ptr::null(),
|
||||
peer_discovery: false,
|
||||
peer_publish: false,
|
||||
server_ca: ptr::null_mut(),
|
||||
@@ -84,6 +85,10 @@ impl DhtRunnerConfig {
|
||||
self.push_platform = CString::new(push_platform).unwrap().into_raw();
|
||||
}
|
||||
|
||||
pub fn set_proxy_user_agent(&mut self, proxy_user_agent: &str) {
|
||||
self.proxy_user_agent = CString::new(proxy_user_agent).unwrap().into_raw();
|
||||
}
|
||||
|
||||
pub fn set_identity(&mut self, certificate: Box<DhtCertificate>, privatekey: Box<PrivateKey>) {
|
||||
self.dht_config.id.privatekey = Box::into_raw(privatekey);
|
||||
self.dht_config.id.certificate = Box::into_raw(certificate);
|
||||
|
||||
@@ -120,6 +120,7 @@ pub struct DhtRunnerConfig
|
||||
pub push_token: *const c_char,
|
||||
pub push_topic: *const c_char,
|
||||
pub push_platform: *const c_char,
|
||||
pub proxy_user_agent: *const c_char,
|
||||
pub peer_discovery: bool,
|
||||
pub peer_publish: bool,
|
||||
pub server_ca: *mut DhtCertificate,
|
||||
|
||||
@@ -22,7 +22,7 @@ get_interface(){
|
||||
if (if1->ifa_addr->sa_family != AF_INET)
|
||||
continue;
|
||||
if((if1->ifa_flags & IFF_MULTICAST) == IFF_MULTICAST){
|
||||
char* sa_data = if1->ifa_addr->sa_data;
|
||||
auto sa_data = reinterpret_cast<uint8_t*>(if1->ifa_addr->sa_data);
|
||||
uint32_t value = sa_data[2] << 24
|
||||
| sa_data[3] << 16
|
||||
| sa_data[4] << 8
|
||||
@@ -36,4 +36,4 @@ get_interface(){
|
||||
}
|
||||
|
||||
} /* namespace workaround */
|
||||
} /* namespace dht */
|
||||
} /* namespace dht */
|
||||
+83
-22
@@ -93,11 +93,27 @@ Blob aesEncrypt(const uint8_t* data, size_t data_length, const Blob& key)
|
||||
std::random_device rdev;
|
||||
std::generate_n(ret.begin(), GCM_IV_SIZE, std::bind(rand_byte, std::ref(rdev)));
|
||||
}
|
||||
struct gcm_aes_ctx aes;
|
||||
gcm_aes_set_key(&aes, key.size(), key.data());
|
||||
gcm_aes_set_iv(&aes, GCM_IV_SIZE, ret.data());
|
||||
gcm_aes_encrypt(&aes, data_length, ret.data() + GCM_IV_SIZE, data);
|
||||
gcm_aes_digest(&aes, GCM_DIGEST_SIZE, ret.data() + GCM_IV_SIZE + data_length);
|
||||
|
||||
if (key.size() == AES_LENGTHS[0]) {
|
||||
struct gcm_aes128_ctx aes;
|
||||
gcm_aes128_set_key(&aes, key.data());
|
||||
gcm_aes128_set_iv(&aes, GCM_IV_SIZE, ret.data());
|
||||
gcm_aes128_encrypt(&aes, data_length, ret.data() + GCM_IV_SIZE, data);
|
||||
gcm_aes128_digest(&aes, GCM_DIGEST_SIZE, ret.data() + GCM_IV_SIZE + data_length);
|
||||
} else if (key.size() == AES_LENGTHS[1]) {
|
||||
struct gcm_aes192_ctx aes;
|
||||
gcm_aes192_set_key(&aes, key.data());
|
||||
gcm_aes192_set_iv(&aes, GCM_IV_SIZE, ret.data());
|
||||
gcm_aes192_encrypt(&aes, data_length, ret.data() + GCM_IV_SIZE, data);
|
||||
gcm_aes192_digest(&aes, GCM_DIGEST_SIZE, ret.data() + GCM_IV_SIZE + data_length);
|
||||
} else if (key.size() == AES_LENGTHS[2]) {
|
||||
struct gcm_aes256_ctx aes;
|
||||
gcm_aes256_set_key(&aes, key.data());
|
||||
gcm_aes256_set_iv(&aes, GCM_IV_SIZE, ret.data());
|
||||
gcm_aes256_encrypt(&aes, data_length, ret.data() + GCM_IV_SIZE, data);
|
||||
gcm_aes256_digest(&aes, GCM_DIGEST_SIZE, ret.data() + GCM_IV_SIZE + data_length);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -118,14 +134,28 @@ Blob aesDecrypt(const uint8_t* data, size_t data_length, const Blob& key)
|
||||
|
||||
std::array<uint8_t, GCM_DIGEST_SIZE> digest;
|
||||
|
||||
struct gcm_aes_ctx aes;
|
||||
gcm_aes_set_key(&aes, key.size(), key.data());
|
||||
gcm_aes_set_iv(&aes, GCM_IV_SIZE, data);
|
||||
|
||||
size_t data_sz = data_length - GCM_IV_SIZE - GCM_DIGEST_SIZE;
|
||||
Blob ret(data_sz);
|
||||
gcm_aes_decrypt(&aes, data_sz, ret.data(), data + GCM_IV_SIZE);
|
||||
gcm_aes_digest(&aes, GCM_DIGEST_SIZE, digest.data());
|
||||
|
||||
if (key.size() == AES_LENGTHS[0]) {
|
||||
struct gcm_aes128_ctx aes;
|
||||
gcm_aes128_set_key(&aes, key.data());
|
||||
gcm_aes128_set_iv(&aes, GCM_IV_SIZE, data);
|
||||
gcm_aes128_decrypt(&aes, data_sz, ret.data(), data + GCM_IV_SIZE);
|
||||
gcm_aes128_digest(&aes, GCM_DIGEST_SIZE, digest.data());
|
||||
} else if (key.size() == AES_LENGTHS[1]) {
|
||||
struct gcm_aes192_ctx aes;
|
||||
gcm_aes192_set_key(&aes, key.data());
|
||||
gcm_aes192_set_iv(&aes, GCM_IV_SIZE, data);
|
||||
gcm_aes192_decrypt(&aes, data_sz, ret.data(), data + GCM_IV_SIZE);
|
||||
gcm_aes192_digest(&aes, GCM_DIGEST_SIZE, digest.data());
|
||||
} else if (key.size() == AES_LENGTHS[2]) {
|
||||
struct gcm_aes256_ctx aes;
|
||||
gcm_aes256_set_key(&aes, key.data());
|
||||
gcm_aes256_set_iv(&aes, GCM_IV_SIZE, data);
|
||||
gcm_aes256_decrypt(&aes, data_sz, ret.data(), data + GCM_IV_SIZE);
|
||||
gcm_aes256_digest(&aes, GCM_DIGEST_SIZE, digest.data());
|
||||
}
|
||||
|
||||
if (not std::equal(digest.begin(), digest.end(), data + data_length - GCM_DIGEST_SIZE)) {
|
||||
throw DecryptError("Can't decrypt data");
|
||||
@@ -227,7 +257,7 @@ PrivateKey::PrivateKey(const uint8_t* src, size_t src_size, const char* password
|
||||
throw CryptoException("Can't initialize private key !");
|
||||
|
||||
const gnutls_datum_t dt {(uint8_t*)src, static_cast<unsigned>(src_size)};
|
||||
int flags = password_ptr ? GNUTLS_PKCS_PLAIN
|
||||
int flags = (password_ptr == NULL || strlen(password_ptr)==0) ? GNUTLS_PKCS_PLAIN
|
||||
: ( GNUTLS_PKCS_PBES2_AES_128 | GNUTLS_PKCS_PBES2_AES_192 | GNUTLS_PKCS_PBES2_AES_256
|
||||
| GNUTLS_PKCS_PKCS12_3DES | GNUTLS_PKCS_PKCS12_ARCFOUR | GNUTLS_PKCS_PKCS12_RC2_40);
|
||||
|
||||
@@ -1134,11 +1164,17 @@ saveIdentity(const Identity& id, const std::string& path, const std::string& pri
|
||||
auto ca_key_data = id.first->serialize(privkey_password);
|
||||
std::ofstream key_file(path + ".pem");
|
||||
key_file.write((char*)ca_key_data.data(), ca_key_data.size());
|
||||
// Throw error if the file is not written
|
||||
if (!key_file)
|
||||
throw CryptoException("Could not write private key file");
|
||||
}
|
||||
{
|
||||
auto ca_key_data = id.second->getPacked();
|
||||
std::ofstream crt_file(path + ".crt");
|
||||
crt_file.write((char*)ca_key_data.data(), ca_key_data.size());
|
||||
// Throw error if the file is not written
|
||||
if (!crt_file)
|
||||
throw CryptoException("Could not write certificate file");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1152,12 +1188,19 @@ loadIdentity(const std::string &path,const std::string &privkey_password)
|
||||
pkStream.close();
|
||||
// Create a certificate
|
||||
gnutls_x509_crt_t gnuCert;
|
||||
if (gnutls_x509_crt_init(&gnuCert) != GNUTLS_E_SUCCESS)
|
||||
if (gnutls_x509_crt_init(&gnuCert))
|
||||
throw std::runtime_error("Failed to initialize gnutls certificate struct");
|
||||
gnutls_datum_t crtContent {nullptr, 0};
|
||||
|
||||
// Read the certificate file
|
||||
gnutls_load_file((path + ".crt").c_str(), &crtContent);
|
||||
gnutls_x509_crt_import(gnuCert, &crtContent, GNUTLS_X509_FMT_PEM);
|
||||
int err = gnutls_load_file((path + ".crt").c_str(), &crtContent);
|
||||
if (err)
|
||||
throw CryptoException(gnutls_strerror(err));
|
||||
|
||||
err = gnutls_x509_crt_import(gnuCert, &crtContent, GNUTLS_X509_FMT_PEM);
|
||||
if (err)
|
||||
throw CryptoException(gnutls_strerror(err));
|
||||
|
||||
auto cert = std::make_shared<Certificate>(gnuCert);
|
||||
return {std::move(key), std::move(cert)};
|
||||
}
|
||||
@@ -1204,31 +1247,49 @@ Certificate::generate(const PrivateKey& key, const std::string& name, const Iden
|
||||
auto pk_id = pk.getId();
|
||||
const std::string uid_str = pk_id.toString();
|
||||
|
||||
gnutls_x509_crt_set_subject_key_id(cert, &pk_id, sizeof(pk_id));
|
||||
gnutls_x509_crt_set_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, name.data(), name.length());
|
||||
gnutls_x509_crt_set_dn_by_oid(cert, GNUTLS_OID_LDAP_UID, 0, uid_str.data(), uid_str.length());
|
||||
int err = gnutls_x509_crt_set_subject_key_id(cert, &pk_id, sizeof(pk_id));
|
||||
if(err) {
|
||||
throw CryptoException(std::string("Error when setting subject key id ") + gnutls_strerror(err));
|
||||
}
|
||||
|
||||
err = gnutls_x509_crt_set_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, name.data(), name.length());
|
||||
if(err) {
|
||||
throw CryptoException(std::string("Error when setting subject key id ") + gnutls_strerror(err));
|
||||
}
|
||||
err = gnutls_x509_crt_set_dn_by_oid(cert, GNUTLS_OID_LDAP_UID, 0, uid_str.data(), uid_str.length());
|
||||
if(err) {
|
||||
throw CryptoException(std::string("Error when setting dn by oid ") + gnutls_strerror(err));
|
||||
}
|
||||
|
||||
setRandomSerial(cert);
|
||||
|
||||
unsigned key_usage = 0;
|
||||
if (is_ca) {
|
||||
gnutls_x509_crt_set_ca_status(cert, 1);
|
||||
err = gnutls_x509_crt_set_ca_status(cert, 1);
|
||||
if(err) {
|
||||
throw CryptoException(std::string("Error when setting ca status ") + gnutls_strerror(err));
|
||||
}
|
||||
key_usage |= GNUTLS_KEY_KEY_CERT_SIGN | GNUTLS_KEY_CRL_SIGN;
|
||||
} else {
|
||||
key_usage |= GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_DATA_ENCIPHERMENT;
|
||||
}
|
||||
gnutls_x509_crt_set_key_usage(cert, key_usage);
|
||||
err = gnutls_x509_crt_set_key_usage(cert, key_usage);
|
||||
if(err) {
|
||||
throw CryptoException(std::string("Error when setting ca status ") + gnutls_strerror(err));
|
||||
}
|
||||
|
||||
if (ca.first && ca.second) {
|
||||
if (not ca.second->isCA()) {
|
||||
throw CryptoException("Signing certificate must be CA");
|
||||
}
|
||||
if (int err = gnutls_x509_crt_privkey_sign(cert, ca.second->cert, ca.first->key, pk.getPreferredDigest(), 0)) {
|
||||
err = gnutls_x509_crt_privkey_sign(cert, ca.second->cert, ca.first->key, pk.getPreferredDigest(), 0);
|
||||
if (err) {
|
||||
throw CryptoException(std::string("Error when signing certificate ") + gnutls_strerror(err));
|
||||
}
|
||||
ret.issuer = ca.second;
|
||||
} else {
|
||||
if (int err = gnutls_x509_crt_privkey_sign(cert, cert, key.key, pk.getPreferredDigest(), 0)) {
|
||||
err = gnutls_x509_crt_privkey_sign(cert, cert, key.key, pk.getPreferredDigest(), 0);
|
||||
if (err) {
|
||||
throw CryptoException(std::string("Error when signing certificate ") + gnutls_strerror(err));
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -93,7 +93,7 @@ Dht::shutdown(ShutdownCallback cb, bool stop)
|
||||
r.second.done_cb(false, {});
|
||||
sr.second->callbacks.clear();
|
||||
for (const auto& a : sr.second->announce) {
|
||||
if (a.callback) a.callback(false, {});
|
||||
for (auto& cb : a.callbacks) cb(false, {});
|
||||
}
|
||||
sr.second->announce.clear();
|
||||
sr.second->listeners.clear();
|
||||
@@ -904,7 +904,7 @@ Dht::listenTo(const InfoHash& id, sa_family_t af, ValueCallback cb, Value::Filte
|
||||
{
|
||||
if (!isRunning(af))
|
||||
return 0;
|
||||
// logger__ERR("[search %s IPv%c] search_time is now in %lfs", sr->id.toString().c_str(), (sr->af == AF_INET) ? '4' : '6', print_dt(tm-clock::now()));
|
||||
// logger__ERR("[search %s IPv%c] search_time is now in %lfs", sr->id.toString().c_str(), (sr->af == AF_INET) ? '4' : '6', print_duration(tm-clock::now()));
|
||||
|
||||
//logger__WARN("listenTo %s", id.toString().c_str());
|
||||
auto& srs = searches(af);
|
||||
|
||||
@@ -106,13 +106,26 @@ getRandomSessionId(size_t length = 8) {
|
||||
DhtProxyClient::DhtProxyClient() {}
|
||||
|
||||
DhtProxyClient::DhtProxyClient(
|
||||
std::shared_ptr<dht::crypto::Certificate> serverCA, dht::crypto::Identity clientIdentity,
|
||||
std::function<void()> signal, const std::string& serverHost,
|
||||
const std::string& pushClientId, std::shared_ptr<dht::Logger> logger)
|
||||
std::shared_ptr<dht::crypto::Certificate> serverCA,
|
||||
dht::crypto::Identity clientIdentity,
|
||||
std::function<void()> signal,
|
||||
const std::string& serverHost,
|
||||
const std::string& userAgent,
|
||||
const std::string& pushClientId,
|
||||
const std::string& pushToken,
|
||||
const std::string& pushTopic,
|
||||
const std::string& pushPlatform,
|
||||
std::shared_ptr<dht::Logger> logger
|
||||
)
|
||||
: DhtInterface(logger)
|
||||
, proxyUrl_(serverHost)
|
||||
, clientIdentity_(clientIdentity), serverCertificate_(serverCA)
|
||||
, pushClientId_(pushClientId), pushSessionId_(getRandomSessionId())
|
||||
, userAgent_(userAgent)
|
||||
, pushClientId_(pushClientId)
|
||||
, pushSessionId_(getRandomSessionId())
|
||||
, deviceKey_(pushToken)
|
||||
, notificationTopic_(pushTopic)
|
||||
, platform_(pushPlatform)
|
||||
, loopSignal_(signal)
|
||||
, jsonReader_(Json::CharReaderBuilder{}.newCharReader())
|
||||
{
|
||||
@@ -484,7 +497,7 @@ DhtProxyClient::buildRequest(const std::string& target)
|
||||
request->set_certificate_authority(serverCertificate_);
|
||||
if (clientIdentity_.first and clientIdentity_.second)
|
||||
request->set_identity(clientIdentity_);
|
||||
request->set_header_field(restinio::http_field_t::user_agent, "RESTinio client");
|
||||
request->set_header_field(restinio::http_field_t::user_agent, userAgent_);
|
||||
return request;
|
||||
}
|
||||
|
||||
@@ -800,8 +813,8 @@ SockAddr
|
||||
DhtProxyClient::parsePublicAddress(const Json::Value& val)
|
||||
{
|
||||
auto public_ip = val.asString();
|
||||
auto hostAndService = splitPort(public_ip);
|
||||
auto sa = SockAddr::resolve(hostAndService.first);
|
||||
auto [host, service] = splitPort(public_ip);
|
||||
auto sa = SockAddr::resolve(host);
|
||||
if (sa.empty()) return {};
|
||||
return sa.front().getMappedIPv4();
|
||||
}
|
||||
@@ -1381,4 +1394,24 @@ DhtProxyClient::fillBody(bool resubscribe)
|
||||
}
|
||||
#endif // OPENDHT_PUSH_NOTIFICATIONS
|
||||
|
||||
void
|
||||
DhtProxyClient::setPushNotificationToken([[maybe_unused]] const std::string& token) {
|
||||
#ifdef OPENDHT_PUSH_NOTIFICATIONS
|
||||
std::unique_lock<std::mutex> l(lockCurrentProxyInfos_);
|
||||
if (deviceKey_ != token) {
|
||||
deviceKey_ = token;
|
||||
if (statusIpv4_ == NodeStatus::Connected || statusIpv6_ == NodeStatus::Connected) {
|
||||
if (logger_)
|
||||
logger_->d("[proxy:client] [push] token changed, resubscribing");
|
||||
for (auto& search : searches_) {
|
||||
for (auto& listener : search.second.listeners) {
|
||||
resubscribe(search.first, listener.first, listener.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
} // namespace dht
|
||||
@@ -223,6 +223,7 @@ DhtProxyServer::DhtProxyServer(const std::shared_ptr<DhtRunner>& dht,
|
||||
: ioContext_(std::make_shared<asio::io_context>()),
|
||||
dht_(dht), persistPath_(config.persistStatePath), logger_(logger),
|
||||
printStatsTimer_(std::make_unique<asio::steady_timer>(*ioContext_, 3s)),
|
||||
serverStartTime_(clock::now()),
|
||||
connListener_(std::make_shared<ConnectionListener>(std::bind(&DhtProxyServer::onConnectionClosed, this, std::placeholders::_1))),
|
||||
pushServer_(config.pushServer),
|
||||
bundleId_(config.bundleId)
|
||||
@@ -542,7 +543,15 @@ DhtProxyServer::updateStats(std::shared_ptr<NodeInfo> info) const
|
||||
stats.requestRate = count / dt.count();
|
||||
#ifdef OPENDHT_PUSH_NOTIFICATIONS
|
||||
stats.pushListenersCount = pushListeners_.size();
|
||||
{
|
||||
std::lock_guard lk(pushStatsMutex_);
|
||||
stats.androidPush = androidPush_;
|
||||
stats.iosPush = iosPush_;
|
||||
stats.unifiedPush = unifiedPush_;
|
||||
}
|
||||
#endif
|
||||
stats.serverStartTime = serverStartTime_;
|
||||
stats.lastUpdated = now;
|
||||
stats.totalPermanentPuts = 0;
|
||||
std::for_each(puts_.begin(), puts_.end(), [&stats](const auto& put) {
|
||||
stats.totalPermanentPuts += put.second.puts.size();
|
||||
@@ -622,6 +631,8 @@ DhtProxyServer::createRestRouter()
|
||||
router->http_get("/key/:hash", std::bind(&DhtProxyServer::get, this, _1, _2));
|
||||
// key.post
|
||||
router->http_post("/key/:hash", std::bind(&DhtProxyServer::put, this, _1, _2));
|
||||
router->add_handler(restinio::http_method_options(),
|
||||
"/key/:hash/listen", std::bind(&DhtProxyServer::options, this, _1, _2));
|
||||
// key.listen
|
||||
router->http_get("/key/:hash/listen", std::bind(&DhtProxyServer::listen, this, _1, _2));
|
||||
#ifdef OPENDHT_PUSH_NOTIFICATIONS
|
||||
@@ -1107,10 +1118,14 @@ DhtProxyServer::sendPushNotification(const std::string& token, Json::Value&& jso
|
||||
notification["tokens"] = std::move(tokens);
|
||||
notification["platform"] = type == PushType::Android ? 2 : 1;
|
||||
notification["data"] = std::move(json);
|
||||
notification["priority"] = highPriority ? "high" : "normal";
|
||||
if (type == PushType::Android)
|
||||
notification["time_to_live"] = 3600 * 24; // 24 hours
|
||||
else {
|
||||
auto priority = highPriority ? "high" : "normal";
|
||||
if (type == PushType::Android) {
|
||||
Json::Value androidConfig(Json::objectValue);
|
||||
androidConfig["priority"] = priority;
|
||||
androidConfig["ttl"] = "86400s"; // time to live = 24 hours
|
||||
notification["android"] = std::move(androidConfig);
|
||||
} else {
|
||||
notification["priority"] = priority;
|
||||
const auto expiration = std::chrono::system_clock::now() + std::chrono::hours(24);
|
||||
uint32_t exp = std::chrono::duration_cast<std::chrono::seconds>(expiration.time_since_epoch()).count();
|
||||
notification["expiration"] = exp;
|
||||
@@ -1151,6 +1166,21 @@ DhtProxyServer::sendPushNotification(const std::string& token, Json::Value&& jso
|
||||
requests_[reqid] = request;
|
||||
}
|
||||
request->send();
|
||||
// For monitoring purposes
|
||||
std::lock_guard lk(pushStatsMutex_);
|
||||
switch (type) {
|
||||
case PushType::Android:
|
||||
androidPush_.increment(highPriority);
|
||||
break;
|
||||
case PushType::iOS:
|
||||
iosPush_.increment(highPriority);
|
||||
break;
|
||||
case PushType::UnifiedPush:
|
||||
unifiedPush_.increment(highPriority);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e){
|
||||
if (logger_)
|
||||
|
||||
+24
-12
@@ -180,9 +180,9 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
throw;
|
||||
}
|
||||
|
||||
if (context.statusChangedCallback) {
|
||||
statusCb = std::move(context.statusChangedCallback);
|
||||
}
|
||||
statusCbs.clear();
|
||||
if (context.statusChangedCallback)
|
||||
statusCbs.emplace_back(std::move(context.statusChangedCallback));
|
||||
if (context.certificateStore) {
|
||||
dht_->setLocalCertificateStore(std::move(context.certificateStore));
|
||||
}
|
||||
@@ -264,6 +264,17 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (peerDiscovery_) {
|
||||
statusCbs.emplace_back([this](NodeStatus status4, NodeStatus status6) {
|
||||
if (status4 == NodeStatus::Disconnected && status6 == NodeStatus::Disconnected) {
|
||||
peerDiscovery_->connectivityChanged();
|
||||
}
|
||||
else if (status4 != NodeStatus::Connected || status6 != NodeStatus::Connected) {
|
||||
peerDiscovery_->stopConnectivityChanged();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -690,8 +701,9 @@ DhtRunner::loop_()
|
||||
if (nstatus4 != status4 || nstatus6 != status6) {
|
||||
status4 = nstatus4;
|
||||
status6 = nstatus6;
|
||||
if (statusCb)
|
||||
statusCb(status4, status6);
|
||||
for (auto& cb : statusCbs){
|
||||
cb(status4, status6);
|
||||
}
|
||||
}
|
||||
|
||||
return wakeup;
|
||||
@@ -1108,13 +1120,13 @@ DhtRunner::enableProxy(bool proxify)
|
||||
cv.notify_all();
|
||||
}
|
||||
},
|
||||
config_.proxy_server, config_.push_node_id, logger_);
|
||||
if (not config_.push_token.empty())
|
||||
dht_via_proxy->setPushNotificationToken(config_.push_token);
|
||||
if (not config_.push_topic.empty())
|
||||
dht_via_proxy->setPushNotificationTopic(config_.push_topic);
|
||||
if (not config_.push_platform.empty())
|
||||
dht_via_proxy->setPushNotificationPlatform(config_.push_platform);
|
||||
config_.proxy_server,
|
||||
config_.proxy_user_agent,
|
||||
config_.push_node_id,
|
||||
config_.push_token,
|
||||
config_.push_topic,
|
||||
config_.push_platform,
|
||||
logger_);
|
||||
dht_ = std::make_unique<SecureDht>(std::move(dht_via_proxy), config_.dht_config, identityAnnouncedCb_, logger_);
|
||||
// and use it
|
||||
use_proxy = proxify;
|
||||
|
||||
+5
-6
@@ -54,7 +54,7 @@ constexpr const char HTTPS_PROTOCOL[] = "https://";
|
||||
constexpr const char ORIGIN_PROTOCOL[] = "//";
|
||||
constexpr unsigned MAX_REDIRECTS {5};
|
||||
|
||||
Url::Url(const std::string& url): url(url)
|
||||
Url::Url(std::string_view url): url(url)
|
||||
{
|
||||
size_t addr_begin = 0;
|
||||
// protocol
|
||||
@@ -69,10 +69,9 @@ Url::Url(const std::string& url): url(url)
|
||||
size_t addr_size = url.substr(addr_begin).find("/");
|
||||
if (addr_size == std::string::npos)
|
||||
addr_size = url.size() - addr_begin;
|
||||
auto host_service = splitPort(url.substr(addr_begin, addr_size));
|
||||
host = host_service.first;
|
||||
if (!host_service.second.empty())
|
||||
service = host_service.second;
|
||||
auto [h, s] = splitPort(url.substr(addr_begin, addr_size));
|
||||
host = std::move(h);
|
||||
service = std::move(s);
|
||||
// target, query and fragment
|
||||
size_t query_begin = url.find("?");
|
||||
auto addr_end = addr_begin + addr_size;
|
||||
@@ -461,7 +460,7 @@ Connection::set_ssl_verification(const std::string& hostname, const asio::ssl::v
|
||||
bool verified = verifier(preverified, ctx);
|
||||
auto verify_ec = X509_STORE_CTX_get_error(ctx.native_handle());
|
||||
if (verify_ec != 0 /*X509_V_OK*/ and logger)
|
||||
logger->error("[http::connection:{:d}] ssl verification error={:d} %d", id, verify_ec, verified);
|
||||
logger->error("[http::connection:{:d}] ssl verification error={:d} {}", id, verify_ec, verified);
|
||||
if (verified and checkOcsp) {
|
||||
std::unique_ptr<stack_st_X509, void(*)(stack_st_X509*)> chain(
|
||||
X509_STORE_CTX_get1_chain(ctx.native_handle()),
|
||||
|
||||
+41
-2
@@ -52,6 +52,7 @@ public:
|
||||
|
||||
void connectivityChanged();
|
||||
|
||||
void stopConnectivityChanged();
|
||||
private:
|
||||
Sp<Logger> logger_;
|
||||
//dmtx_ for callbackmap_ and drunning_ (write)
|
||||
@@ -59,6 +60,14 @@ private:
|
||||
//mtx_ for messages_ and lrunning (listen)
|
||||
std::mutex mtx_;
|
||||
std::shared_ptr<asio::io_context> ioContext_;
|
||||
|
||||
static constexpr dht::duration PeerDiscovery_PERIOD_MAX{
|
||||
std::chrono::minutes(1)};
|
||||
static constexpr std::chrono::seconds PeerDiscovery_PERIOD{10};
|
||||
asio::steady_timer peerDiscoveryTimer;
|
||||
std::chrono::steady_clock::duration peerDiscovery_period{
|
||||
PeerDiscovery_PERIOD};
|
||||
|
||||
asio::ip::udp::socket sockFd_;
|
||||
asio::ip::udp::endpoint sockAddrSend_;
|
||||
|
||||
@@ -86,6 +95,7 @@ private:
|
||||
PeerDiscovery::DomainPeerDiscovery::DomainPeerDiscovery(asio::ip::udp domain, in_port_t port, Sp<asio::io_context> ioContext, Sp<Logger> logger)
|
||||
: logger_(logger)
|
||||
, ioContext_(ioContext)
|
||||
, peerDiscoveryTimer(*ioContext_)
|
||||
, sockFd_(*ioContext_, domain)
|
||||
, sockAddrSend_(asio::ip::address::from_string(domain.family() == AF_INET ? MULTICAST_ADDRESS_IPV4
|
||||
: MULTICAST_ADDRESS_IPV6), port)
|
||||
@@ -317,8 +327,30 @@ PeerDiscovery::DomainPeerDiscovery::reDiscover()
|
||||
void
|
||||
PeerDiscovery::DomainPeerDiscovery::connectivityChanged()
|
||||
{
|
||||
reDiscover();
|
||||
publish(sockAddrSend_);
|
||||
ioContext_->post([this] () {
|
||||
reDiscover();
|
||||
publish(sockAddrSend_);
|
||||
});
|
||||
if (logger_)
|
||||
logger_->d("PeerDiscovery: connectivity changed");
|
||||
|
||||
if (peerDiscovery_period == PeerDiscovery_PERIOD_MAX ){
|
||||
peerDiscovery_period = PeerDiscovery_PERIOD;
|
||||
}
|
||||
else{
|
||||
peerDiscoveryTimer.expires_after(peerDiscovery_period);
|
||||
peerDiscoveryTimer.async_wait([this](const asio::error_code& ec) {
|
||||
if (ec == asio::error::operation_aborted)
|
||||
return;
|
||||
connectivityChanged();
|
||||
});
|
||||
peerDiscovery_period = std::min(peerDiscovery_period * 2, PeerDiscovery_PERIOD_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerDiscovery::DomainPeerDiscovery::stopConnectivityChanged() {
|
||||
peerDiscoveryTimer.cancel();
|
||||
peerDiscovery_period = PeerDiscovery_PERIOD;
|
||||
}
|
||||
|
||||
PeerDiscovery::PeerDiscovery(in_port_t port, Sp<asio::io_context> ioContext, Sp<Logger> logger)
|
||||
@@ -431,4 +463,11 @@ PeerDiscovery::connectivityChanged()
|
||||
peerDiscovery6_->connectivityChanged();
|
||||
}
|
||||
|
||||
void PeerDiscovery::stopConnectivityChanged() {
|
||||
if (peerDiscovery4_)
|
||||
peerDiscovery4_->stopConnectivityChanged();
|
||||
if (peerDiscovery6_)
|
||||
peerDiscovery6_->stopConnectivityChanged();
|
||||
}
|
||||
|
||||
} /* namespace dht */
|
||||
+26
-19
@@ -45,7 +45,7 @@ struct Dht::Announce {
|
||||
bool permanent;
|
||||
Sp<Value> value;
|
||||
time_point created;
|
||||
DoneCallback callback;
|
||||
std::vector<DoneCallback> callbacks;
|
||||
};
|
||||
|
||||
struct Dht::SearchNode {
|
||||
@@ -442,8 +442,9 @@ struct Dht::Search {
|
||||
g.second.done_cb = {};
|
||||
}
|
||||
for (auto& a : announce) {
|
||||
a.callback(false, {});
|
||||
a.callback = {};
|
||||
for (auto& cb : a.callbacks)
|
||||
cb(false, {});
|
||||
a.callbacks.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -627,7 +628,9 @@ struct Dht::Search {
|
||||
return a.value->id == value->id;
|
||||
});
|
||||
if (a_sr == announce.end()) {
|
||||
announce.emplace_back(Announce {permanent, value, created, callback});
|
||||
auto& a = announce.emplace_back(Announce {permanent, value, created, {}} );
|
||||
if (callback)
|
||||
a.callbacks.emplace_back(std::move(callback));
|
||||
for (auto& n : nodes) {
|
||||
n->probe_query.reset();
|
||||
n->acked[value->id].req.reset();
|
||||
@@ -635,24 +638,26 @@ struct Dht::Search {
|
||||
} else {
|
||||
a_sr->permanent = permanent;
|
||||
a_sr->created = created;
|
||||
if (a_sr->value != value) {
|
||||
if (a_sr->value != value && *a_sr->value != *value) {
|
||||
// Value is updated, previous ops are failed
|
||||
a_sr->value = value;
|
||||
for (auto& n : nodes) {
|
||||
n->acked[value->id].req.reset();
|
||||
n->probe_query.reset();
|
||||
}
|
||||
}
|
||||
if (isAnnounced(value->id)) {
|
||||
if (a_sr->callback)
|
||||
a_sr->callback(true, {});
|
||||
a_sr->callback = {};
|
||||
for (auto& cb: a_sr->callbacks)
|
||||
cb(false, {});
|
||||
a_sr->callbacks.clear();
|
||||
if (callback)
|
||||
a_sr->callbacks.emplace_back(std::move(callback));
|
||||
} else if (isAnnounced(value->id)) {
|
||||
// Same value, already announced
|
||||
if (callback)
|
||||
callback(true, {});
|
||||
return;
|
||||
} else {
|
||||
if (a_sr->callback)
|
||||
a_sr->callback(false, {});
|
||||
a_sr->callback = callback;
|
||||
// Same value, not announced yet
|
||||
if (callback)
|
||||
a_sr->callbacks.emplace_back(std::move(callback));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -722,8 +727,8 @@ struct Dht::Search {
|
||||
std::vector<DoneCallback> a_cbs;
|
||||
a_cbs.reserve(announce.size());
|
||||
for (auto ait = announce.begin() ; ait != announce.end(); ) {
|
||||
if (ait->callback)
|
||||
a_cbs.emplace_back(std::move(ait->callback));
|
||||
a_cbs.insert(a_cbs.end(), std::make_move_iterator(ait->callbacks.begin()), std::make_move_iterator(ait->callbacks.end()));
|
||||
ait->callbacks.clear();
|
||||
if (not ait->permanent)
|
||||
ait = announce.erase(ait);
|
||||
else
|
||||
@@ -747,9 +752,11 @@ struct Dht::Search {
|
||||
if (vid != Value::INVALID_ID and (!a.value || a.value->id != vid))
|
||||
return true;
|
||||
if (isAnnounced(a.value->id)) {
|
||||
if (a.callback) {
|
||||
a.callback(true, getNodes());
|
||||
a.callback = nullptr;
|
||||
if (!a.callbacks.empty()) {
|
||||
const auto& nodes = getNodes();
|
||||
for (auto& cb : a.callbacks)
|
||||
cb(true, nodes);
|
||||
a.callbacks.clear();
|
||||
}
|
||||
if (not a.permanent)
|
||||
return false;
|
||||
|
||||
+9
-9
@@ -44,23 +44,23 @@ const HexMap hex_map = {};
|
||||
static constexpr std::array<uint8_t, 12> MAPPED_IPV4_PREFIX {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}};
|
||||
|
||||
std::pair<std::string, std::string>
|
||||
splitPort(const std::string& s) {
|
||||
splitPort(std::string_view s) {
|
||||
if (s.empty())
|
||||
return {};
|
||||
if (s[0] == '[') {
|
||||
std::size_t closure = s.find_first_of(']');
|
||||
std::size_t found = s.find_last_of(':');
|
||||
if (closure == std::string::npos)
|
||||
return {s, ""};
|
||||
if (found == std::string::npos or found < closure)
|
||||
return {s.substr(1,closure-1), ""};
|
||||
return {s.substr(1,closure-1), s.substr(found+1)};
|
||||
if (closure == std::string_view::npos)
|
||||
return {std::string(s), ""};
|
||||
if (found == std::string_view::npos or found < closure)
|
||||
return {std::string(s.substr(1,closure-1)), ""};
|
||||
return {std::string(s.substr(1,closure-1)), std::string(s.substr(found+1))};
|
||||
}
|
||||
std::size_t found = s.find_last_of(':');
|
||||
std::size_t first = s.find_first_of(':');
|
||||
if (found == std::string::npos or found != first)
|
||||
return {s, ""};
|
||||
return {s.substr(0,found), s.substr(found+1)};
|
||||
if (found == std::string_view::npos or found != first)
|
||||
return {std::string(s), ""};
|
||||
return {std::string(s.substr(0,found)), std::string(s.substr(found+1))};
|
||||
}
|
||||
|
||||
std::vector<SockAddr>
|
||||
|
||||
@@ -271,6 +271,7 @@ Value::encrypt(const crypto::PrivateKey& from, const crypto::PublicKey& to)
|
||||
setRecipient(to.getId());
|
||||
sign(from);
|
||||
Value nv {id};
|
||||
nv.priority = priority;
|
||||
nv.setCypher(to.encrypt(getToEncrypt()));
|
||||
return nv;
|
||||
}
|
||||
|
||||
@@ -239,6 +239,27 @@ void CryptoTester::testAesEncryption() {
|
||||
CPPUNIT_ASSERT(data2 == decrypted2);
|
||||
}
|
||||
|
||||
void CryptoTester::testAesEncryptionWithMultipleKeySizes() {
|
||||
auto data = std::vector<uint8_t>(rand(), rand());
|
||||
|
||||
// Valid key sizes
|
||||
for (auto key_length : {16, 24, 32}) {
|
||||
auto key = std::vector<uint8_t>(key_length, rand());
|
||||
|
||||
auto encrypted_data = dht::crypto::aesEncrypt(data, key);
|
||||
auto decrypted_data = dht::crypto::aesDecrypt(encrypted_data, key);
|
||||
|
||||
CPPUNIT_ASSERT(data == decrypted_data);
|
||||
}
|
||||
|
||||
// Invalid key sizes
|
||||
for (auto key_length : {12, 28, 36}) {
|
||||
auto key = std::vector<uint8_t>(key_length, rand());
|
||||
|
||||
CPPUNIT_ASSERT_THROW(dht::crypto::aesEncrypt(data, key), dht::crypto::DecryptError);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CryptoTester::tearDown() {
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ class CryptoTester : public CppUnit::TestFixture {
|
||||
CPPUNIT_TEST(testCertificateSerialNumber);
|
||||
CPPUNIT_TEST(testOcsp);
|
||||
CPPUNIT_TEST(testAesEncryption);
|
||||
CPPUNIT_TEST(testAesEncryptionWithMultipleKeySizes);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
@@ -69,6 +70,7 @@ class CryptoTester : public CppUnit::TestFixture {
|
||||
* Test key streching and aes encryption/decryption
|
||||
*/
|
||||
void testAesEncryption();
|
||||
void testAesEncryptionWithMultipleKeySizes();
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
@@ -90,6 +90,59 @@ DhtRunnerTester::testGetPut() {
|
||||
CPPUNIT_ASSERT(vals.front()->data == val_data);
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunnerTester::testPutDuplicate() {
|
||||
auto key = dht::InfoHash::get("123");
|
||||
auto val = std::make_shared<dht::Value>("hey");
|
||||
val->id = 42;
|
||||
auto val_data = val->data;
|
||||
std::promise<bool> p1;
|
||||
std::promise<bool> p2;
|
||||
node2.put(key, val, [&](bool ok){
|
||||
p1.set_value(ok);
|
||||
});
|
||||
node2.put(key, val, [&](bool ok){
|
||||
p2.set_value(ok);
|
||||
});
|
||||
auto p1ret = p1.get_future().get();
|
||||
auto p2ret = p2.get_future().get();
|
||||
CPPUNIT_ASSERT(p1ret);
|
||||
CPPUNIT_ASSERT(p2ret);
|
||||
auto vals = node1.get(key).get();
|
||||
CPPUNIT_ASSERT(not vals.empty());
|
||||
CPPUNIT_ASSERT(vals.size() == 1);
|
||||
CPPUNIT_ASSERT(vals.front()->data == val_data);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DhtRunnerTester::testPutOverride() {
|
||||
auto key = dht::InfoHash::get("123");
|
||||
auto val = std::make_shared<dht::Value>("meh");
|
||||
val->id = 42;
|
||||
auto val2 = std::make_shared<dht::Value>("hey");
|
||||
val2->id = 42;
|
||||
CPPUNIT_ASSERT_EQUAL(val->id, val2->id);
|
||||
auto val_data = val2->data;
|
||||
std::promise<bool> p1;
|
||||
std::promise<bool> p2;
|
||||
node2.put(key, val, [&](bool ok){
|
||||
p1.set_value(ok);
|
||||
});
|
||||
node2.put(key, val2, [&](bool ok){
|
||||
p2.set_value(ok);
|
||||
});
|
||||
auto p1ret = p1.get_future().get();
|
||||
auto p2ret = p2.get_future().get();
|
||||
CPPUNIT_ASSERT(!p1ret);
|
||||
CPPUNIT_ASSERT(p2ret);
|
||||
auto vals = node1.get(key).get();
|
||||
CPPUNIT_ASSERT(not vals.empty());
|
||||
CPPUNIT_ASSERT(vals.size() == 1);
|
||||
CPPUNIT_ASSERT(vals.front()->data == val_data);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DhtRunnerTester::testListen() {
|
||||
std::mutex mutex;
|
||||
@@ -289,7 +342,7 @@ DhtRunnerTester::testIdOps() {
|
||||
valueCountEdit++;
|
||||
cv.notify_all();
|
||||
});
|
||||
node2.listen(key2, [&](const std::vector<std::shared_ptr<dht::Value>>& values, bool expired){
|
||||
node2.listen(key2, [&](const std::vector<std::shared_ptr<dht::Value>>& values, bool /*expired*/){
|
||||
for (const auto& v : values) {
|
||||
if (v->seq == 0)
|
||||
CPPUNIT_ASSERT_EQUAL("v1"s, dht::unpackMsg<std::string>(v->data));
|
||||
|
||||
@@ -31,6 +31,8 @@ class DhtRunnerTester : public CppUnit::TestFixture {
|
||||
CPPUNIT_TEST_SUITE(DhtRunnerTester);
|
||||
CPPUNIT_TEST(testConstructors);
|
||||
CPPUNIT_TEST(testGetPut);
|
||||
CPPUNIT_TEST(testPutDuplicate);
|
||||
CPPUNIT_TEST(testPutOverride);
|
||||
CPPUNIT_TEST(testListen);
|
||||
CPPUNIT_TEST(testListenLotOfBytes);
|
||||
CPPUNIT_TEST(testIdOps);
|
||||
@@ -55,6 +57,14 @@ class DhtRunnerTester : public CppUnit::TestFixture {
|
||||
* Test get and put methods
|
||||
*/
|
||||
void testGetPut();
|
||||
/**
|
||||
* Test get and multiple put
|
||||
*/
|
||||
void testPutDuplicate();
|
||||
/**
|
||||
* Test get and multiple put with changing value
|
||||
*/
|
||||
void testPutOverride();
|
||||
/**
|
||||
* Test listen method
|
||||
*/
|
||||
|
||||
+2
-2
@@ -107,8 +107,8 @@ main(int argc, char **argv)
|
||||
token = dht.listen<dht::ImMessage>(room, [&](dht::ImMessage&& msg) {
|
||||
if (msg.from != myid)
|
||||
std::cout << msg.from.toString() << " at " << printTime(msg.date)
|
||||
<< " (took " << print_dt(std::chrono::system_clock::now() - std::chrono::system_clock::from_time_t(msg.date))
|
||||
<< "s) " << (msg.to == myid ? "ENCRYPTED ":"") << ": " << msg.id << " - " << msg.msg << std::endl;
|
||||
<< " (took " << print_duration(std::chrono::system_clock::now() - std::chrono::system_clock::from_time_t(msg.date))
|
||||
<< ") " << (msg.to == myid ? "ENCRYPTED ":"") << ": " << msg.id << " - " << msg.msg << std::endl;
|
||||
return true;
|
||||
});
|
||||
connected = true;
|
||||
|
||||
+1
-1
@@ -265,7 +265,7 @@ int main(int argc, char **argv)
|
||||
dht_value_unref(val);
|
||||
}
|
||||
else {
|
||||
printf("Unkown command: %s\n", cmd);
|
||||
printf("Unknown command: %s\n", cmd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+236
-168
@@ -1,190 +1,78 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>OpenDHT tester</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css" />
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript">"use strict";
|
||||
let onGet;
|
||||
let onPut;
|
||||
let setServer;
|
||||
const valueGetElement = function(o) {
|
||||
const d = window.atob(o.data);
|
||||
return d;
|
||||
};
|
||||
$(function() {
|
||||
let request = undefined;
|
||||
let server;
|
||||
const getTools = $("#getTools");
|
||||
const getBtn = $("#getBtn");
|
||||
const getDropdown = $("#getDropdown");
|
||||
const listenBtn = $("#listenBtn").click(function(){onGet('LISTEN');});
|
||||
const setGetRequest = function() {
|
||||
getBtn.button('loading');
|
||||
getStopBtn.appendTo(getTools);
|
||||
getDropdown.hide();
|
||||
}
|
||||
const clearGetRequest = function() {
|
||||
if (request === undefined)
|
||||
return;
|
||||
request.abort();
|
||||
request = undefined;
|
||||
getStopBtn.detach();
|
||||
getDropdown.show();
|
||||
getBtn.button('reset');
|
||||
}
|
||||
const getStopBtn = $("#getStopBtn").detach().click(clearGetRequest);
|
||||
const putBtn = $("#putBtn");
|
||||
const result = $("#dhtResult");
|
||||
const group = $('<ul class="list-group"/>').appendTo(result);
|
||||
onGet = function (method) {
|
||||
if (request !== undefined)
|
||||
return false;
|
||||
const input = $("#getKey").val();
|
||||
group.empty();
|
||||
let lastAppended = 0;
|
||||
let start = new Date().getTime();
|
||||
request = new XMLHttpRequest();
|
||||
request.onreadystatechange = function(event) {
|
||||
if (this.readyState >= XMLHttpRequest.LOADING) {
|
||||
if (this.readyState == XMLHttpRequest.DONE) {
|
||||
clearGetRequest();
|
||||
}
|
||||
if (this.status === 200) {
|
||||
const elements = this.responseText.split("\n");
|
||||
const elementsLength = elements.length;
|
||||
const now = new Date().getTime();
|
||||
for (let i = lastAppended; i < elementsLength; i++) {
|
||||
const element = elements[i];
|
||||
if (!element || element.length == 0)
|
||||
return;
|
||||
const o = JSON.parse(element);
|
||||
if (o.expired) {
|
||||
$('#value'+o.id).slideUp(100, (e) => $(e.target).remove());
|
||||
} else {
|
||||
const d = window.atob(o.data);
|
||||
const delay = Math.max(0, start-now);
|
||||
$('<li class="list-group-item" id="value'+o.id+'"/>').append(valueGetElement(o)).appendTo(group).hide().delay(delay).slideDown(100);
|
||||
lastAppended = i+1;
|
||||
start = Math.max(start, now)+25;
|
||||
}
|
||||
}
|
||||
} else if (this.status !== 0) {
|
||||
group.empty().append($('<li class="list-group-item list-group-item-danger"/>').text("Error loading content: " + this.statusText));
|
||||
}
|
||||
}
|
||||
};
|
||||
request.onerror = function(event) {
|
||||
clearGetRequest();
|
||||
group.empty().append($('<li class="list-group-item list-group-item-danger"/>').text("Error loading content."));
|
||||
};
|
||||
request.open(method, server + input, true);
|
||||
request.send(null);
|
||||
setGetRequest();
|
||||
return false;
|
||||
};
|
||||
|
||||
onPut = function( ) {
|
||||
const key = $("#getKey").val();
|
||||
const value = $("#putValue").val();
|
||||
$.ajax({
|
||||
url: server + key,
|
||||
type: 'POST',
|
||||
data: JSON.stringify({
|
||||
data:window.btoa(value)
|
||||
}),
|
||||
contentType: 'application/json; charset=utf-8',
|
||||
dataType: 'json',
|
||||
success: function( result ) {
|
||||
putBtn.button('reset');
|
||||
//$('<li class="list-group-item list-group-item-success"/>').append(valueGetElement(result)).appendTo(group.empty());
|
||||
},
|
||||
error: function(result) {
|
||||
putBtn.button('reset');
|
||||
group.empty().append($('<li class="list-group-item list-group-item-danger"/>').text(result.statusText));
|
||||
}
|
||||
});
|
||||
putBtn.button('loading');
|
||||
return false;
|
||||
};
|
||||
|
||||
const serverValue = $("#serverValue");
|
||||
const serverStatus = $("#serverStatus");
|
||||
const serverBtn = $("#serverBtn");
|
||||
setServer = function(event) {
|
||||
server = serverValue.val() + '/';
|
||||
serverStatus.empty();
|
||||
serverBtn.button('loading');
|
||||
$.getJSON(server, function(data){
|
||||
$('<span><b>Node</b> '+data.node_id+'</span>').appendTo(serverStatus).hide().fadeIn();
|
||||
}).fail(function(error) {
|
||||
var message = " Cant' access node."
|
||||
if (serverValue.val().indexOf("https") != -1){
|
||||
message += "</br></br>Self-signed certificate must be allowed in browser."
|
||||
}
|
||||
serverStatus.html("<div class='alert alert-danger' style='margin-bottom: 0px;'>" +
|
||||
"<span class='glyphicon glyphicon-remove' aria-hidden='true'></span>" +
|
||||
message + "</div>");
|
||||
}).always(function(error) {
|
||||
serverBtn.button('reset');
|
||||
});
|
||||
return false;
|
||||
};
|
||||
setServer();
|
||||
});
|
||||
</script>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>OpenDHT web node</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.3.3/css/bootstrap.min.css" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container" style="max-width: 730px;">
|
||||
<header class="page-header">
|
||||
<div class="container" style="max-width: 880px;">
|
||||
<header class="m-3 p-3">
|
||||
<div class="row">
|
||||
<div class="col-sm-5">
|
||||
<h1>OpenDHT tester</h1>
|
||||
<div class="col-sm-5 d-flex align-items-center">
|
||||
<h2>OpenDHT web node</h2>
|
||||
</div>
|
||||
<div class="col-sm-7">
|
||||
<div class="well well-sm" style="margin-top:10px; margin-bottom:0px;">
|
||||
<form id="serverForm" class="form-inline" onsubmit="return setServer();" style="margin-bottom:4px;">
|
||||
<div class="card card-body bg-light" style="margin-top:10px; margin-bottom:0px;">
|
||||
<form id="serverForm" class="form-inline" onsubmit="return setServer();"
|
||||
style="margin-bottom:4px;">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="serverValue" placeholder="Proxy server" value="http://127.0.0.1:8080"/>
|
||||
<span class="input-group-btn">
|
||||
<button id="serverBtn" type="submit" class="btn btn-default" data-loading-text="<i class='fa fa-circle-o-notch fa-spin'></i>"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span></button>
|
||||
</span>
|
||||
<input type="text" class="form-control" id="serverValue" placeholder="Proxy server" value="http://127.0.0.1:8080" />
|
||||
<button id="serverBtn" type="submit" class="btn btn-outline-secondary">
|
||||
<div class='spinner-border spinner-border-sm' role='status'
|
||||
style="display: none;">
|
||||
<span class='visually-hidden'>Loading...</span>
|
||||
</div>
|
||||
<span class="btn-icon"><i class="bi bi-arrow-clockwise"></i></span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div id="serverStatus"><i class='fa fa-circle-o-notch fa-spin'></i></div>
|
||||
<div id="serverStatus">
|
||||
<div class='spinner-border spinner-border-sm' role='status'><span
|
||||
class='visually-hidden'>Loading...</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="panel panel-default" id="dhtResult">
|
||||
<div class="panel-heading">
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<form class="form-inline" onsubmit="return onGet('GET');">
|
||||
<div class="input-group">
|
||||
<div class="list-group" id="dhtResult">
|
||||
<div class="list-group-item list-group-item-light">
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<form class="form-inline" onsubmit="return onGet('');">
|
||||
<div class="input-group" id="getTools">
|
||||
<input type="text" class="form-control" id="getKey" placeholder="Key" aria-label="Key" />
|
||||
<span class="input-group-btn" id="getTools">
|
||||
<button id="getBtn" class="btn btn-default" data-loading-text="<i class='fa fa-circle-o-notch fa-spin'></i>" type="submit">Get</button>
|
||||
<button id="getDropdown"type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <span class="caret"></span> <span class="sr-only">Toggle Dropdown</span> </button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a id="listenBtn" href="#">Listen</a></li>
|
||||
</ul>
|
||||
<button id="getStopBtn" class="btn btn-default" type="submit"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span></button>
|
||||
</span>
|
||||
<button id="getBtn" class="btn btn-outline-secondary" type="submit">
|
||||
<div class='spinner-border spinner-border-sm' role='status' style="display: none;">
|
||||
<span class='visually-hidden'>Loading...</span>
|
||||
</div>
|
||||
<span class="btn-text">Get</span>
|
||||
</button>
|
||||
<button id="menuBtn" type="button" class="btn btn-outline-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<span class="visually-hidden">Toggle Dropdown</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li><a class="dropdown-item" id="listenBtn" href="#">Listen</a></li>
|
||||
</ul>
|
||||
<button id="getStopBtn" class="btn btn-outline-secondary">Stop</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<div class="col-sm-6">
|
||||
<form class="form-inline" onsubmit="return onPut();">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control input-group-input" id="putValue" placeholder="Value" />
|
||||
<span class="input-group-btn">
|
||||
<button id="putBtn" type="submit" class="btn btn-default" data-loading-text="<i class='fa fa-circle-o-notch fa-spin'></i> Loading">Put</button>
|
||||
</span>
|
||||
<input type="text" class="form-control input-group-input" id="putValue"
|
||||
placeholder="Value" />
|
||||
<button id="putBtn" type="submit" class="btn btn-outline-secondary">
|
||||
<div class='spinner-border spinner-border-sm' role='status' style="display: none;">
|
||||
<span class='visually-hidden'>Loading...</span>
|
||||
</div>
|
||||
<span class="btn-text">Put</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -192,5 +80,185 @@ $(function() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">"use strict";
|
||||
const valueGetElement = (o) => window.atob(o.data);
|
||||
const getTools = document.getElementById("getTools");
|
||||
const getBtn = document.getElementById("getBtn");
|
||||
const menuBtn = document.getElementById("menuBtn");
|
||||
const getStopBtn = document.getElementById("getStopBtn");
|
||||
const listenBtn = document.getElementById("listenBtn");
|
||||
const putBtn = document.getElementById("putBtn");
|
||||
const group = document.getElementById("dhtResult");
|
||||
const serverValue = document.getElementById("serverValue");
|
||||
const serverStatus = document.getElementById("serverStatus");
|
||||
const serverBtn = document.getElementById("serverBtn");
|
||||
|
||||
var request = undefined;
|
||||
const setGetRequest = () => {
|
||||
getBtn.disabled = true;
|
||||
getBtn.querySelector('.spinner-border').style.display = 'inline-block';
|
||||
getBtn.querySelector('.btn-text').style.display = 'none';
|
||||
menuBtn.style.display = 'none';
|
||||
getTools.appendChild(getStopBtn);
|
||||
};
|
||||
const clearGetRequest = () => {
|
||||
if (request === undefined)
|
||||
return;
|
||||
request.abort();
|
||||
request = undefined;
|
||||
if (getStopBtn.parentNode) {
|
||||
getStopBtn.parentNode.removeChild(getStopBtn);
|
||||
}
|
||||
menuBtn.style.display = '';
|
||||
getBtn.disabled = false;
|
||||
getBtn.querySelector('.spinner-border').style.display = 'none';
|
||||
getBtn.querySelector('.btn-text').style.display = 'inline';
|
||||
};
|
||||
|
||||
const clearResults = () => {
|
||||
while (group.children.length > 1) {
|
||||
group.removeChild(group.lastChild);
|
||||
}
|
||||
};
|
||||
|
||||
var server;
|
||||
const setServer = (event) => {
|
||||
server = serverValue.value + '/';
|
||||
serverStatus.innerHTML = '';
|
||||
serverBtn.disabled = true;
|
||||
serverBtn.querySelector('.spinner-border').style.display = 'inline-block';
|
||||
serverBtn.querySelector('.btn-icon').style.display = 'none';
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', server, true);
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||
serverBtn.disabled = false;
|
||||
serverBtn.querySelector('.spinner-border').style.display = 'none';
|
||||
serverBtn.querySelector('.btn-icon').style.display = 'inline';
|
||||
if (xhr.status === 200) {
|
||||
const data = JSON.parse(xhr.responseText);
|
||||
const span = document.createElement('span');
|
||||
span.innerHTML = '<b>Node</b> ' + data.node_id;
|
||||
serverStatus.appendChild(span);
|
||||
} else {
|
||||
const message = " Can't access node.";
|
||||
if (serverValue.value.indexOf("https") != -1) {
|
||||
message += "</br></br>Self-signed certificate must be allowed in browser.";
|
||||
}
|
||||
serverStatus.innerHTML = "<div class='alert alert-danger' style='margin-bottom: 0px;'>" +
|
||||
"<span class='glyphicon glyphicon-remove' aria-hidden='true'></span>" +
|
||||
message + "</div>";
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
return false;
|
||||
};
|
||||
setServer();
|
||||
|
||||
const onGet = (path) => {
|
||||
if (request !== undefined)
|
||||
return false;
|
||||
const input = document.getElementById("getKey").value;
|
||||
clearResults()
|
||||
let lastAppended = 0;
|
||||
let start = new Date().getTime();
|
||||
request = new XMLHttpRequest();
|
||||
request.onreadystatechange = (event) => {
|
||||
if (request.readyState >= 2) { // LOADING
|
||||
if (request.status === 200) {
|
||||
const elements = request.responseText.split("\n");
|
||||
const elementsLength = elements.length;
|
||||
const now = new Date().getTime();
|
||||
for (let i = lastAppended; i < elementsLength; i++) {
|
||||
const element = elements[i];
|
||||
if (!element || element.length == 0)
|
||||
break;
|
||||
const o = JSON.parse(element);
|
||||
if (o.expired) {
|
||||
const expiredElement = document.getElementById('value' + o.id);
|
||||
if (expiredElement) {
|
||||
expiredElement.style.display = 'none';
|
||||
if (expiredElement.parentNode) {
|
||||
expiredElement.parentNode.removeChild(expiredElement);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const d = window.atob(o.data);
|
||||
const delay = Math.max(0, start - now);
|
||||
const li = document.createElement('li');
|
||||
li.className = 'list-group-item';
|
||||
li.id = 'value' + o.id;
|
||||
li.innerHTML = valueGetElement(o);
|
||||
li.style.opacity = 0;
|
||||
li.style.transition = 'opacity 0.1s ease-in';
|
||||
group.appendChild(li);
|
||||
setTimeout(() => {
|
||||
li.style.opacity = 1;
|
||||
}, delay);
|
||||
lastAppended = i + 1;
|
||||
start = Math.max(start, now) + 25;
|
||||
}
|
||||
}
|
||||
} else if (request.status !== 0) {
|
||||
clearResults()
|
||||
const li = document.createElement('li');
|
||||
li.className = 'list-group-item list-group-item-danger';
|
||||
li.textContent = "Error loading content: " + request.statusText;
|
||||
group.appendChild(li);
|
||||
}
|
||||
}
|
||||
if (request.readyState == 4) { // DONE
|
||||
clearGetRequest();
|
||||
}
|
||||
};
|
||||
request.onerror = (event) => {
|
||||
clearGetRequest();
|
||||
clearResults()
|
||||
const li = document.createElement('li');
|
||||
li.className = 'list-group-item list-group-item-danger';
|
||||
li.textContent = "Error loading content.";
|
||||
group.appendChild(li);
|
||||
};
|
||||
request.open('GET', server + 'key/' + input + path, true);
|
||||
request.send(null);
|
||||
setGetRequest();
|
||||
return false;
|
||||
};
|
||||
const onPut = () => {
|
||||
const key = document.getElementById("getKey").value;
|
||||
const value = document.getElementById("putValue").value;
|
||||
putBtn.disabled = true;
|
||||
putBtn.querySelector('.spinner-border').style.display = 'inline-block';
|
||||
putBtn.querySelector('.btn-text').style.display = 'none';
|
||||
fetch(server + key, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json; charset=utf-8' },
|
||||
body: JSON.stringify({ data: window.btoa(value) })
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok)
|
||||
throw new Error(response.statusText);
|
||||
})
|
||||
.catch(error => {
|
||||
group.innerHTML = '';
|
||||
const li = document.createElement('li');
|
||||
li.className = 'list-group-item list-group-item-danger';
|
||||
li.textContent = error.message;
|
||||
group.appendChild(li);
|
||||
})
|
||||
.finally(() => {
|
||||
putBtn.disabled = false;
|
||||
putBtn.querySelector('.spinner-border').style.display = 'none';
|
||||
putBtn.querySelector('.btn-text').style.display = 'inline';
|
||||
});
|
||||
return false;
|
||||
};
|
||||
|
||||
listenBtn.addEventListener('click', () => { onGet('/listen'); });
|
||||
getStopBtn.parentNode.removeChild(getStopBtn);
|
||||
getStopBtn.addEventListener('click', clearGetRequest);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
Reference in new issue
Block a user