mirror of
https://github.com/savoirfairelinux/opendht.git
synced 2026-11-11 22:07:10 +00:00
Compare commits
58
Commits
2.0.0beta2
...
branch
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c63030b591 | ||
|
|
b3ae25602f | ||
|
|
16f64684ed | ||
|
|
82938289e2 | ||
|
|
a671b57273 | ||
|
|
17fb92654e | ||
|
|
294034f00f | ||
|
|
36857c1f3d | ||
|
|
7e9f5303bd | ||
|
|
c2780b1ca9 | ||
|
|
16b798324d | ||
|
|
bc850bf00c | ||
|
|
0d8d302128 | ||
|
|
97013badfa | ||
|
|
31185c24f0 | ||
|
|
2f6f8e2ac1 | ||
|
|
8f141951ce | ||
|
|
43d111b7dc | ||
|
|
620ba2a89b | ||
|
|
d1598380db | ||
|
|
69fe399db0 | ||
|
|
a60dde2e13 | ||
|
|
201982b1a9 | ||
|
|
83f2b272a7 | ||
|
|
7e47d0458a | ||
|
|
f6d26757fa | ||
|
|
476d0bca4f | ||
|
|
259986e786 | ||
|
|
8ff0c979d4 | ||
|
|
913cb543c8 | ||
|
|
b380c9ffba | ||
|
|
8c717085b4 | ||
|
|
48742a1058 | ||
|
|
fe11285a03 | ||
|
|
fb34770a54 | ||
|
|
4a909419e9 | ||
|
|
dab7099369 | ||
|
|
9c84e59bbf | ||
|
|
eae383b3ed | ||
|
|
28bf78408d | ||
|
|
7ed6f74e6b | ||
|
|
cf6d9fd8c2 | ||
|
|
5df34bb752 | ||
|
|
bebb66490d | ||
|
|
5f361748eb | ||
|
|
984745624c | ||
|
|
f9e860fdc8 | ||
|
|
06ea3d23e5 | ||
|
|
f0857a5925 | ||
|
|
5a993fb902 | ||
|
|
8a18c591e9 | ||
|
|
9daa7ec283 | ||
|
|
01f2ad9ea4 | ||
|
|
2dd4cde035 | ||
|
|
cb0005d0eb | ||
|
|
eca340b785 | ||
|
|
cdd035b801 | ||
|
|
cb7d7f6614 |
No files matched your search
@@ -0,0 +1,27 @@
|
||||
name: C/C++ CI
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: deps
|
||||
run: |
|
||||
sudo apt install libncurses5-dev libreadline-dev nettle-dev \
|
||||
libgnutls28-dev libuv1-dev cython3 python3-dev python3-setuptools libcppunit-dev libjsoncpp-dev \
|
||||
autotools-dev autoconf libasio-dev libfmt-dev libhttp-parser-dev libmsgpack-dev
|
||||
- name: autogen
|
||||
run: ./autogen.sh
|
||||
- name: argon2
|
||||
run: |
|
||||
cd argon2 && make && sudo make install && cd ..
|
||||
- name: configure
|
||||
run: ./configure
|
||||
- name: make
|
||||
run: make
|
||||
- name: make check
|
||||
run: make check
|
||||
+25
-24
@@ -95,6 +95,7 @@ if (OPENDHT_SANITIZE)
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fstack-protector-strong")
|
||||
endif ()
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMSGPACK_DISABLE_LEGACY_NIL -DMSGPACK_DISABLE_LEGACY_CONVERT")
|
||||
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
endif ()
|
||||
@@ -362,39 +363,39 @@ install (FILES ${CMAKE_CURRENT_BINARY_DIR}/opendhtConfigVersion.cmake DESTINATIO
|
||||
|
||||
# Unit tests
|
||||
if (OPENDHT_TESTS)
|
||||
FIND_PACKAGE(Cppunit REQUIRED)
|
||||
# unit testing
|
||||
FIND_PACKAGE(Cppunit REQUIRED)
|
||||
# unit testing
|
||||
list (APPEND test_FILES
|
||||
tests/infohashtester.h
|
||||
tests/infohashtester.cpp
|
||||
tests/valuetester.h
|
||||
tests/valuetester.cpp
|
||||
tests/cryptotester.h
|
||||
tests/cryptotester.cpp
|
||||
tests/dhtrunnertester.h
|
||||
tests/dhtrunnertester.cpp
|
||||
tests/peerdiscoverytester.h
|
||||
tests/peerdiscoverytester.cpp
|
||||
tests/threadpooltester.h
|
||||
tests/threadpooltester.cpp
|
||||
tests/infohashtester.h
|
||||
tests/infohashtester.cpp
|
||||
tests/valuetester.h
|
||||
tests/valuetester.cpp
|
||||
tests/cryptotester.h
|
||||
tests/cryptotester.cpp
|
||||
tests/dhtrunnertester.h
|
||||
tests/dhtrunnertester.cpp
|
||||
tests/peerdiscoverytester.h
|
||||
tests/peerdiscoverytester.cpp
|
||||
tests/threadpooltester.h
|
||||
tests/threadpooltester.cpp
|
||||
)
|
||||
if (OPENDHT_PROXY_SERVER AND OPENDHT_PROXY_CLIENT)
|
||||
list (APPEND test_FILES
|
||||
tests/httptester.h
|
||||
tests/httptester.cpp
|
||||
tests/dhtproxytester.h
|
||||
tests/dhtproxytester.cpp
|
||||
)
|
||||
list (APPEND test_FILES
|
||||
tests/httptester.h
|
||||
tests/httptester.cpp
|
||||
tests/dhtproxytester.h
|
||||
tests/dhtproxytester.cpp
|
||||
)
|
||||
endif()
|
||||
add_executable(opendht_unit_tests
|
||||
tests/tests_runner.cpp
|
||||
${test_FILES}
|
||||
tests/tests_runner.cpp
|
||||
${test_FILES}
|
||||
)
|
||||
target_include_directories(opendht_unit_tests SYSTEM PRIVATE ${CPPUNIT_INCLUDE_DIRS})
|
||||
if (OPENDHT_SHARED)
|
||||
target_link_libraries(opendht_unit_tests opendht)
|
||||
target_link_libraries(opendht_unit_tests opendht)
|
||||
else ()
|
||||
target_link_libraries(opendht_unit_tests opendht-static)
|
||||
target_link_libraries(opendht_unit_tests opendht-static)
|
||||
endif ()
|
||||
target_link_libraries(opendht_unit_tests
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
|
||||
@@ -16,9 +16,7 @@ if ENABLE_TESTS
|
||||
SUBDIRS += tests
|
||||
endif
|
||||
|
||||
if HAVE_DOXYGEN
|
||||
SUBDIRS += doc
|
||||
endif
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
|
||||
+1
-1
Submodule argon2 updated: 670229c849...62358ba212.
+9
-12
@@ -98,7 +98,7 @@ AS_IF([test "x$SYS" = "xandroid"],
|
||||
LT_INIT()
|
||||
LT_LANG(C++)
|
||||
|
||||
AX_CXX_COMPILE_STDCXX(11,[noext],[mandatory])
|
||||
AX_CXX_COMPILE_STDCXX(14,[noext],[mandatory])
|
||||
|
||||
PKG_PROG_PKG_CONFIG()
|
||||
|
||||
@@ -210,22 +210,19 @@ AM_COND_IF(ENABLE_PROXY_CLIENT, [CPPFLAGS+=" -DOPENDHT_PROXY_CLIENT"], [])
|
||||
AM_COND_IF(ENABLE_PUSH_NOTIFICATIONS, [CPPFLAGS+=" -DOPENDHT_PUSH_NOTIFICATIONS"], [])
|
||||
AM_COND_IF(ENABLE_PROXY_SERVER_IDENTITY, [CPPFLAGS+=" -DOPENDHT_PROXY_SERVER_IDENTITY"], [])
|
||||
|
||||
AM_COND_IF([HAVE_DOXYGEN], [
|
||||
AC_CONFIG_FILES([doc/Doxyfile doc/Makefile])
|
||||
])
|
||||
|
||||
dnl Configure setup.py if we build the python module
|
||||
AM_COND_IF([USE_CYTHON], [
|
||||
AC_SUBST(CURRENT_SOURCE_DIR, ".")
|
||||
AC_SUBST(CURRENT_BINARY_DIR, ".")
|
||||
AC_SUBST(PROJECT_SOURCE_DIR, "..")
|
||||
AC_SUBST(PROJECT_BINARY_DIR, "../src/.libs")
|
||||
AC_CONFIG_FILES([python/Makefile python/setup.py])
|
||||
])
|
||||
AC_SUBST(CURRENT_SOURCE_DIR, ".")
|
||||
AC_SUBST(CURRENT_BINARY_DIR, ".")
|
||||
AC_SUBST(PROJECT_SOURCE_DIR, "..")
|
||||
AC_SUBST(PROJECT_BINARY_DIR, "../src/.libs")
|
||||
|
||||
AC_CONFIG_FILES([Makefile
|
||||
src/Makefile
|
||||
tools/Makefile
|
||||
python/Makefile
|
||||
python/setup.py
|
||||
tests/Makefile
|
||||
doc/Makefile
|
||||
doc/Doxyfile
|
||||
opendht.pc])
|
||||
AC_OUTPUT
|
||||
+4
-1
@@ -1,7 +1,10 @@
|
||||
dist_man1_MANS = dhtnode.1
|
||||
|
||||
noinst_HEADERS = \
|
||||
Doxyfile
|
||||
|
||||
if HAVE_DOXYGEN
|
||||
doxyfile.stamp:
|
||||
doxyfile.stamp: Doxyfile
|
||||
$(DOXYGEN) Doxyfile
|
||||
echo stamp > doxyfile.stamp
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
FROM docker.pkg.github.com/savoirfairelinux/opendht/opendht-deps-bionic:2.0.0
|
||||
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
|
||||
RUN git clone https://github.com/savoirfairelinux/opendht.git \
|
||||
&& cd opendht && mkdir build && cd build \
|
||||
&& cmake .. -DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DOPENDHT_PROXY_CLIENT=On \
|
||||
-DOPENDHT_PROXY_SERVER=On \
|
||||
-DOPENDHT_C=On \
|
||||
-DOPENDHT_PYTHON=On \
|
||||
-DOPENDHT_LTO=On \
|
||||
&& make -j8 && make install \
|
||||
&& cd ../.. && rm -rf opendht
|
||||
@@ -0,0 +1,28 @@
|
||||
FROM ubuntu:18.04
|
||||
MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
|
||||
RUN echo "APT::Acquire::Retries \"3\";" > /etc/apt/apt.conf.d/80-retries
|
||||
RUN apt-get update && apt-get install -y \
|
||||
apt-transport-https build-essential cmake git wget libncurses5-dev libreadline-dev nettle-dev \
|
||||
libgnutls28-dev libuv1-dev cython3 python3-dev python3-setuptools libcppunit-dev libjsoncpp-dev \
|
||||
autotools-dev autoconf libasio-dev libfmt-dev libhttp-parser-dev libmsgpack-dev \
|
||||
&& apt-get clean
|
||||
|
||||
# libasio-dev (1.10) is too old
|
||||
RUN echo "** Building a recent version of asio ***" \
|
||||
&& wget https://github.com/chriskohlhoff/asio/archive/asio-1-12-2.tar.gz \
|
||||
&& tar -xvf asio-1-12-2.tar.gz && cd asio-asio-1-12-2/asio \
|
||||
&& ./autogen.sh && ./configure --prefix=/usr --without-boost \
|
||||
&& make install \
|
||||
&& cd ../../ && rm -rf asio*
|
||||
|
||||
RUN echo "*** Downloading RESTinio ***" \
|
||||
&& mkdir restinio && cd restinio \
|
||||
&& wget https://github.com/aberaud/restinio/archive/8d5d3e8237e0947adb9ba1ffc8281f4ad7cb2a59.tar.gz \
|
||||
&& ls -l && tar -xzf 8d5d3e8237e0947adb9ba1ffc8281f4ad7cb2a59.tar.gz \
|
||||
&& cd restinio-8d5d3e8237e0947adb9ba1ffc8281f4ad7cb2a59/dev \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DRESTINIO_TEST=OFF -DRESTINIO_SAMPLE=OFF \
|
||||
-DRESTINIO_INSTALL_SAMPLES=OFF -DRESTINIO_BENCH=OFF -DRESTINIO_INSTALL_BENCHES=OFF \
|
||||
-DRESTINIO_FIND_DEPS=ON -DRESTINIO_ALLOW_SOBJECTIZER=Off -DRESTINIO_USE_BOOST_ASIO=none . \
|
||||
&& make -j8 && make install \
|
||||
&& cd ../../ && rm -rf restinio*
|
||||
@@ -44,6 +44,13 @@ enum class NodeStatus {
|
||||
Connected // 1+ good nodes
|
||||
};
|
||||
|
||||
inline constexpr const char*
|
||||
statusToStr(NodeStatus status) {
|
||||
return status == NodeStatus::Connected ? "connected" : (
|
||||
status == NodeStatus::Connecting ? "connecting" :
|
||||
"disconnected");
|
||||
}
|
||||
|
||||
struct OPENDHT_PUBLIC NodeStats {
|
||||
unsigned good_nodes {0},
|
||||
dubious_nodes {0},
|
||||
@@ -71,6 +78,7 @@ struct OPENDHT_PUBLIC NodeInfo {
|
||||
InfoHash node_id;
|
||||
NodeStats ipv4;
|
||||
NodeStats ipv6;
|
||||
size_t ongoing_ops;
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
/**
|
||||
@@ -106,6 +114,12 @@ struct OPENDHT_PUBLIC Config {
|
||||
|
||||
/** If set, the dht will load its state from this file on start and save its state in this file on shutdown */
|
||||
std::string persist_path {};
|
||||
|
||||
/** If non-0, overrides the default global rate-limit.-1 means no limit. */
|
||||
ssize_t max_req_per_sec {0};
|
||||
|
||||
/** If non-0, overrides the default per-IP address rate-limit. -1 means no limit. */
|
||||
ssize_t max_peer_req_per_sec {0};
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,16 +43,22 @@ namespace dht {
|
||||
namespace crypto {
|
||||
|
||||
class OPENDHT_PUBLIC CryptoException : public std::runtime_error {
|
||||
public:
|
||||
CryptoException(const std::string& str) : std::runtime_error(str) {};
|
||||
public:
|
||||
explicit CryptoException(const std::string& str) : std::runtime_error(str) {};
|
||||
explicit CryptoException(const char* str) : std::runtime_error(str) {};
|
||||
CryptoException(const CryptoException& e) noexcept = default;
|
||||
CryptoException& operator=(const CryptoException&) noexcept = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* Exception thrown when a decryption error happened.
|
||||
*/
|
||||
class OPENDHT_PUBLIC DecryptError : public CryptoException {
|
||||
public:
|
||||
DecryptError(const std::string& str = "") : CryptoException(str) {};
|
||||
public:
|
||||
explicit DecryptError(const std::string& str) : CryptoException(str) {};
|
||||
explicit DecryptError(const char* str) : CryptoException(str) {};
|
||||
DecryptError(const DecryptError& e) noexcept = default;
|
||||
DecryptError& operator=(const DecryptError&) noexcept = default;
|
||||
};
|
||||
|
||||
struct PrivateKey;
|
||||
|
||||
@@ -319,10 +319,8 @@ private:
|
||||
* cancel all Listeners
|
||||
*/
|
||||
void cancelAllListeners();
|
||||
/**
|
||||
* cancel all Operations
|
||||
*/
|
||||
void cancelAllOperations();
|
||||
|
||||
std::atomic_bool isDestroying_ {false};
|
||||
|
||||
std::string proxyUrl_;
|
||||
dht::crypto::Identity clientIdentity_;
|
||||
@@ -349,7 +347,9 @@ private:
|
||||
*/
|
||||
asio::io_context httpContext_;
|
||||
std::shared_ptr<http::Resolver> resolver_;
|
||||
std::map<unsigned int /*id*/, std::shared_ptr<http::Request>> requests_;
|
||||
|
||||
mutable std::mutex requestLock_;
|
||||
std::map<unsigned, std::shared_ptr<http::Request>> requests_;
|
||||
/*
|
||||
* Thread for executing the http io_context.run() blocking call
|
||||
*/
|
||||
@@ -403,8 +403,6 @@ private:
|
||||
void getPushRequest(Json::Value&) const;
|
||||
#endif // OPENDHT_PUSH_NOTIFICATIONS
|
||||
|
||||
std::atomic_bool isDestroying_ {false};
|
||||
|
||||
Json::StreamWriterBuilder jsonBuilder_;
|
||||
Json::CharReaderBuilder jsonReaderBuilder_;
|
||||
std::shared_ptr<dht::Logger> logger_;
|
||||
|
||||
@@ -328,6 +328,8 @@ private:
|
||||
|
||||
// http client
|
||||
std::pair<std::string, std::string> pushHostPort_;
|
||||
|
||||
mutable std::mutex requestLock_;
|
||||
std::map<unsigned int /*id*/, std::shared_ptr<http::Request>> requests_;
|
||||
|
||||
std::shared_ptr<dht::Logger> logger_;
|
||||
|
||||
@@ -317,9 +317,7 @@ public:
|
||||
* Returns the currently bound port, in host byte order.
|
||||
* @param f: address family of the bound port to retreive.
|
||||
*/
|
||||
in_port_t getBoundPort(sa_family_t f = AF_INET) const {
|
||||
return getBound(f).getPort();
|
||||
}
|
||||
in_port_t getBoundPort(sa_family_t f = AF_INET) const;
|
||||
|
||||
std::pair<size_t, size_t> getStoreSize() const;
|
||||
|
||||
@@ -342,7 +340,7 @@ public:
|
||||
void importValues(const std::vector<ValuesExport>& values);
|
||||
|
||||
bool isRunning() const {
|
||||
return running;
|
||||
return running != State::Idle;
|
||||
}
|
||||
|
||||
NodeStats getNodesStats(sa_family_t af) const;
|
||||
@@ -360,7 +358,7 @@ public:
|
||||
|
||||
// securedht methods
|
||||
|
||||
void findCertificate(InfoHash hash, std::function<void(const std::shared_ptr<crypto::Certificate>)>);
|
||||
void findCertificate(InfoHash hash, std::function<void(const std::shared_ptr<crypto::Certificate>&)>);
|
||||
void registerCertificate(std::shared_ptr<crypto::Certificate> cert);
|
||||
void setLocalCertificateStore(CertificateStoreQuery&& query_method);
|
||||
|
||||
@@ -413,7 +411,7 @@ public:
|
||||
/**
|
||||
* Gracefuly disconnect from network.
|
||||
*/
|
||||
void shutdown(ShutdownCallback cb);
|
||||
void shutdown(ShutdownCallback cb = {});
|
||||
|
||||
/**
|
||||
* Quit and wait for all threads to terminate.
|
||||
@@ -451,6 +449,12 @@ public:
|
||||
private:
|
||||
static constexpr std::chrono::seconds BOOTSTRAP_PERIOD {10};
|
||||
|
||||
enum class State {
|
||||
Idle,
|
||||
Running,
|
||||
Stopping
|
||||
};
|
||||
|
||||
/**
|
||||
* Will try to resolve the list of hostnames `bootstrap_nodes` on seperate
|
||||
* thread and then queue ping requests. This list should contain reliable
|
||||
@@ -465,6 +469,11 @@ private:
|
||||
return std::max(status4, status6);
|
||||
}
|
||||
|
||||
bool checkShutdown();
|
||||
void opEnded();
|
||||
DoneCallback bindOpDoneCallback(DoneCallback&& cb);
|
||||
DoneCallbackSimple bindOpDoneCallback(DoneCallbackSimple&& cb);
|
||||
|
||||
/** Local DHT instance */
|
||||
std::unique_ptr<SecureDht> dht_;
|
||||
|
||||
@@ -512,7 +521,9 @@ private:
|
||||
std::queue<std::function<void(SecureDht&)>> pending_ops {};
|
||||
std::mutex storage_mtx {};
|
||||
|
||||
std::atomic_bool running {false};
|
||||
std::atomic<State> running {State::Idle};
|
||||
std::atomic_size_t ongoing_ops {0};
|
||||
std::vector<ShutdownCallback> shutdownCallbacks_;
|
||||
|
||||
NodeStatus status4 {NodeStatus::Disconnected},
|
||||
status6 {NodeStatus::Disconnected};
|
||||
|
||||
+24
-17
@@ -61,7 +61,7 @@ struct Certificate;
|
||||
namespace http {
|
||||
|
||||
using HandlerCb = std::function<void(const asio::error_code& ec)>;
|
||||
using BytesHandlerCb = std::function<void(const asio::error_code& ec, const size_t bytes)>;
|
||||
using BytesHandlerCb = std::function<void(const asio::error_code& ec, size_t bytes)>;
|
||||
using ConnectHandlerCb = std::function<void(const asio::error_code& ec,
|
||||
const asio::ip::tcp::endpoint& endpoint)>;
|
||||
|
||||
@@ -82,7 +82,7 @@ public:
|
||||
std::string fragment;
|
||||
};
|
||||
|
||||
class OPENDHT_PUBLIC Connection
|
||||
class OPENDHT_PUBLIC Connection : public std::enable_shared_from_this<Connection>
|
||||
{
|
||||
public:
|
||||
Connection(asio::io_context& ctx, const bool ssl = true, std::shared_ptr<dht::Logger> l = {});
|
||||
@@ -90,30 +90,31 @@ public:
|
||||
const dht::crypto::Identity& identity, std::shared_ptr<dht::Logger> l = {});
|
||||
~Connection();
|
||||
|
||||
unsigned int id();
|
||||
bool is_open();
|
||||
bool is_ssl();
|
||||
inline unsigned int id() const { return id_; };
|
||||
bool is_open() const;
|
||||
bool is_ssl() const;
|
||||
|
||||
void set_ssl_verification(const asio::ip::tcp::endpoint& endpoint, const asio::ssl::verify_mode verify_mode);
|
||||
|
||||
asio::streambuf& input();
|
||||
asio::streambuf& data();
|
||||
std::istream& data() { return istream_; }
|
||||
|
||||
std::string read_bytes(const size_t bytes);
|
||||
std::string read_bytes(size_t bytes);
|
||||
std::string read_until(const char delim);
|
||||
|
||||
void async_connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, ConnectHandlerCb);
|
||||
void async_handshake(HandlerCb cb);
|
||||
void async_write(BytesHandlerCb cb);
|
||||
void async_read_until(const char* delim, BytesHandlerCb cb);
|
||||
void async_read(const size_t bytes, BytesHandlerCb cb);
|
||||
void async_read_until(char delim, BytesHandlerCb cb);
|
||||
void async_read(size_t bytes, BytesHandlerCb cb);
|
||||
|
||||
void timeout(const std::chrono::seconds timeout, HandlerCb cb = {});
|
||||
void close();
|
||||
|
||||
private:
|
||||
unsigned int id_;
|
||||
static unsigned int ids_;
|
||||
static std::atomic_uint ids_;
|
||||
|
||||
asio::io_context& ctx_;
|
||||
std::unique_ptr<socket_t> socket_;
|
||||
@@ -124,6 +125,7 @@ private:
|
||||
|
||||
asio::streambuf write_buf_;
|
||||
asio::streambuf read_buf_;
|
||||
std::istream istream_;
|
||||
|
||||
std::unique_ptr<asio::steady_timer> timeout_timer_;
|
||||
std::shared_ptr<dht::Logger> logger_;
|
||||
@@ -190,12 +192,13 @@ private:
|
||||
|
||||
struct Response
|
||||
{
|
||||
unsigned int status_code;
|
||||
unsigned int status_code {0};
|
||||
std::map<std::string, std::string> headers;
|
||||
std::string body;
|
||||
bool aborted {false};
|
||||
};
|
||||
|
||||
class OPENDHT_PUBLIC Request
|
||||
class OPENDHT_PUBLIC Request : public std::enable_shared_from_this<Request>
|
||||
{
|
||||
public:
|
||||
enum class State {
|
||||
@@ -209,6 +212,7 @@ public:
|
||||
using OnDataCb = std::function<void(const char* at, size_t length)>;
|
||||
using OnStateChangeCb = std::function<void(State state, const Response& response)>;
|
||||
using OnJsonCb = std::function<void(Json::Value value, unsigned int status_code)>;
|
||||
using OnDoneCb = std::function<void(const Response& response)>;
|
||||
|
||||
// resolves implicitly
|
||||
Request(asio::io_context& ctx, const std::string& url, const Json::Value& json, OnJsonCb jsoncb,
|
||||
@@ -216,6 +220,7 @@ public:
|
||||
Request(asio::io_context& ctx, const std::string& url, std::shared_ptr<dht::Logger> logger = {});
|
||||
Request(asio::io_context& ctx, const std::string& host, const std::string& service,
|
||||
const bool ssl = false, std::shared_ptr<dht::Logger> logger = {});
|
||||
Request(asio::io_context& ctx, const std::string& url, OnDoneCb onDone, std::shared_ptr<dht::Logger> logger = {});
|
||||
|
||||
// user defined resolver
|
||||
Request(asio::io_context& ctx, std::shared_ptr<Resolver> resolver, sa_family_t family = AF_UNSPEC);
|
||||
@@ -297,9 +302,11 @@ private:
|
||||
|
||||
void handle_request(const asio::error_code& ec);
|
||||
|
||||
void handle_response_header(const asio::error_code& ec);
|
||||
void handle_response_header(const asio::error_code& ec, size_t bytes);
|
||||
|
||||
void handle_response_body(const asio::error_code& ec, const size_t bytes);
|
||||
void handle_response_body(const asio::error_code& ec, size_t bytes);
|
||||
|
||||
void onHeadersComplete();
|
||||
|
||||
/**
|
||||
* Parse the request with http_parser.
|
||||
@@ -308,9 +315,11 @@ private:
|
||||
*/
|
||||
size_t parse_request(const std::string& request);
|
||||
|
||||
std::shared_ptr<dht::Logger> logger_;
|
||||
|
||||
restinio::http_request_header_t header_;
|
||||
std::map<restinio::http_field_t, std::string> headers_;
|
||||
restinio::http_connection_header_t connection_type_;
|
||||
restinio::http_connection_header_t connection_type_ {restinio::http_connection_header_t::close};
|
||||
std::string body_;
|
||||
|
||||
std::mutex cbs_mutex_;
|
||||
@@ -323,9 +332,9 @@ private:
|
||||
std::string host_;
|
||||
|
||||
unsigned int id_;
|
||||
static std::atomic_uint ids_;
|
||||
asio::io_context& ctx_;
|
||||
sa_family_t family_ = AF_UNSPEC;
|
||||
static unsigned int ids_;
|
||||
std::shared_ptr<Connection> conn_;
|
||||
std::shared_ptr<Resolver> resolver_;
|
||||
|
||||
@@ -335,8 +344,6 @@ private:
|
||||
std::atomic<bool> finishing_ {false};
|
||||
std::unique_ptr<http_parser> parser_;
|
||||
std::unique_ptr<http_parser_settings> parser_s_;
|
||||
|
||||
std::shared_ptr<dht::Logger> logger_;
|
||||
};
|
||||
|
||||
} // namespace http
|
||||
|
||||
@@ -48,6 +48,12 @@ struct TransId;
|
||||
#define MSG_CONFIRM 0
|
||||
#endif
|
||||
|
||||
struct NetworkConfig {
|
||||
NetId network {0};
|
||||
ssize_t max_req_per_sec {0};
|
||||
ssize_t max_peer_req_per_sec {0};
|
||||
};
|
||||
|
||||
class DhtProtocolException : public DhtException {
|
||||
public:
|
||||
// sent to another peer (http-like).
|
||||
@@ -207,7 +213,12 @@ public:
|
||||
using RequestExpiredCb = std::function<void(const Request&, bool)>;
|
||||
|
||||
NetworkEngine(Logger& log, Scheduler& scheduler, std::unique_ptr<DatagramSocket>&& sock);
|
||||
NetworkEngine(InfoHash& myid, NetId net, std::unique_ptr<DatagramSocket>&& sock, Logger& log, Scheduler& scheduler,
|
||||
NetworkEngine(
|
||||
InfoHash& myid,
|
||||
NetworkConfig config,
|
||||
std::unique_ptr<DatagramSocket>&& sock,
|
||||
Logger& log,
|
||||
Scheduler& scheduler,
|
||||
decltype(NetworkEngine::onError)&& onError,
|
||||
decltype(NetworkEngine::onNewNode)&& onNewNode,
|
||||
decltype(NetworkEngine::onReportedAddr)&& onReportedAddr,
|
||||
@@ -425,7 +436,6 @@ private:
|
||||
/***************
|
||||
* Constants *
|
||||
***************/
|
||||
static constexpr size_t MAX_REQUESTS_PER_SEC {1600};
|
||||
/* the length of a node info buffer in ipv4 format */
|
||||
static const constexpr size_t NODE4_INFO_BUF_LEN {HASH_LEN + sizeof(in_addr) + sizeof(in_port_t)};
|
||||
/* the length of a node info buffer in ipv6 format */
|
||||
@@ -513,18 +523,18 @@ private:
|
||||
|
||||
/* DHT info */
|
||||
const InfoHash& myid;
|
||||
const NetId network {0};
|
||||
const NetworkConfig config {};
|
||||
const std::unique_ptr<DatagramSocket> dht_socket;
|
||||
const Logger& DHT_LOG;
|
||||
|
||||
NodeCache cache {};
|
||||
|
||||
// global limiting should be triggered by at least 8 different IPs
|
||||
using IpLimiter = RateLimiter<MAX_REQUESTS_PER_SEC/8>;
|
||||
using IpLimiter = RateLimiter;
|
||||
using IpLimiterMap = std::map<SockAddr, IpLimiter, SockAddr::ipCmp>;
|
||||
IpLimiterMap address_rate_limiter {};
|
||||
RateLimiter<MAX_REQUESTS_PER_SEC> rate_limiter {};
|
||||
size_t limiter_maintenance {0};
|
||||
IpLimiterMap address_rate_limiter;
|
||||
RateLimiter rate_limiter;
|
||||
ssize_t limiter_maintenance {0};
|
||||
|
||||
// requests handling
|
||||
std::map<Tid, Sp<Request>> requests {};
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
|
||||
namespace dht {
|
||||
namespace net {
|
||||
@@ -63,24 +63,34 @@ public:
|
||||
virtual int sendTo(const SockAddr& dest, const uint8_t* data, size_t size, bool replied) = 0;
|
||||
|
||||
inline void setOnReceive(OnReceive&& cb) {
|
||||
std::lock_guard<std::mutex> lk(lock);
|
||||
rx_callback = std::move(cb);
|
||||
}
|
||||
|
||||
virtual const SockAddr& getBound(sa_family_t family = AF_UNSPEC) const = 0;
|
||||
virtual bool hasIPv4() const = 0;
|
||||
virtual bool hasIPv6() const = 0;
|
||||
|
||||
in_port_t getPort(sa_family_t family = AF_UNSPEC) const {
|
||||
return getBound(family).getPort();
|
||||
SockAddr getBound(sa_family_t family = AF_UNSPEC) const {
|
||||
std::lock_guard<std::mutex> lk(lock);
|
||||
return getBoundRef(family);
|
||||
}
|
||||
in_port_t getPort(sa_family_t family = AF_UNSPEC) const {
|
||||
std::lock_guard<std::mutex> lk(lock);
|
||||
return getBoundRef(family).getPort();
|
||||
}
|
||||
|
||||
virtual const SockAddr& getBoundRef(sa_family_t family = AF_UNSPEC) const = 0;
|
||||
|
||||
virtual void stop() = 0;
|
||||
protected:
|
||||
|
||||
inline void onReceived(std::unique_ptr<ReceivedPacket>&& packet) {
|
||||
std::lock_guard<std::mutex> lk(lock);
|
||||
if (rx_callback)
|
||||
rx_callback(std::move(packet));
|
||||
}
|
||||
protected:
|
||||
mutable std::mutex lock;
|
||||
private:
|
||||
OnReceive rx_callback;
|
||||
};
|
||||
@@ -93,12 +103,18 @@ public:
|
||||
|
||||
int sendTo(const SockAddr& dest, const uint8_t* data, size_t size, bool replied) override;
|
||||
|
||||
const SockAddr& getBound(sa_family_t family = AF_UNSPEC) const override {
|
||||
const SockAddr& getBoundRef(sa_family_t family = AF_UNSPEC) const override {
|
||||
return (family == AF_INET6) ? bound6 : bound4;
|
||||
}
|
||||
|
||||
bool hasIPv4() const override { return s4 != -1; }
|
||||
bool hasIPv6() const override { return s6 != -1; }
|
||||
bool hasIPv4() const override {
|
||||
std::lock_guard<std::mutex> lk(lock);
|
||||
return s4 != -1;
|
||||
}
|
||||
bool hasIPv6() const override {
|
||||
std::lock_guard<std::mutex> lk(lock);
|
||||
return s6 != -1;
|
||||
}
|
||||
|
||||
void stop() override;
|
||||
private:
|
||||
|
||||
@@ -23,19 +23,23 @@
|
||||
|
||||
namespace dht {
|
||||
|
||||
template<size_t Quota, unsigned long Period=1>
|
||||
class RateLimiter {
|
||||
public:
|
||||
RateLimiter(size_t quota, const duration& period = std::chrono::seconds(1))
|
||||
: quota_(quota), period_(period) {}
|
||||
|
||||
/** Clear outdated records and return current quota usage */
|
||||
size_t maintain(const time_point& now) {
|
||||
auto limit = now - std::chrono::seconds(Period);
|
||||
auto limit = now - period_;
|
||||
while (not records.empty() and records.front() < limit)
|
||||
records.pop();
|
||||
return records.size();
|
||||
}
|
||||
/** Return false if quota is reached, insert record and return true otherwise. */
|
||||
bool limit(const time_point& now) {
|
||||
if (maintain(now) >= Quota)
|
||||
if (quota_ == std::numeric_limits<size_t>::max())
|
||||
return true;
|
||||
if (maintain(now) >= quota_)
|
||||
return false;
|
||||
records.emplace(now);
|
||||
return true;
|
||||
@@ -44,6 +48,8 @@ public:
|
||||
return records.empty();
|
||||
}
|
||||
private:
|
||||
const size_t quota_;
|
||||
const duration period_;
|
||||
std::queue<time_point> records {};
|
||||
};
|
||||
|
||||
|
||||
+419
-30
@@ -1,5 +1,5 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
@@ -33,21 +33,23 @@
|
||||
# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
|
||||
# Copyright (c) 2015 Paul Norman <penorman@mac.com>
|
||||
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
|
||||
# Copyright (c) 2016, 2018 Krzesimir Nowak <qdlacz@gmail.com>
|
||||
# Copyright (c) 2019 Enji Cooper <yaneurabeya@gmail.com>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 4
|
||||
#serial 11
|
||||
|
||||
dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
|
||||
dnl (serial version number 13).
|
||||
|
||||
AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
|
||||
m4_if([$1], [11], [],
|
||||
[$1], [14], [],
|
||||
[$1], [17], [m4_fatal([support for C++17 not yet implemented in AX_CXX_COMPILE_STDCXX])],
|
||||
m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
|
||||
[$1], [14], [ax_cxx_compile_alternatives="14 1y"],
|
||||
[$1], [17], [ax_cxx_compile_alternatives="17 1z"],
|
||||
[m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
|
||||
m4_if([$2], [], [],
|
||||
[$2], [ext], [],
|
||||
@@ -59,18 +61,11 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
|
||||
[m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
|
||||
AC_LANG_PUSH([C++])dnl
|
||||
ac_success=no
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
|
||||
ax_cv_cxx_compile_cxx$1,
|
||||
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[ax_cv_cxx_compile_cxx$1=yes],
|
||||
[ax_cv_cxx_compile_cxx$1=no])])
|
||||
if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
|
||||
ac_success=yes
|
||||
fi
|
||||
|
||||
m4_if([$2], [noext], [], [dnl
|
||||
if test x$ac_success = xno; then
|
||||
for switch in -std=gnu++$1 -std=gnu++0x; do
|
||||
for alternative in ${ax_cxx_compile_alternatives}; do
|
||||
switch="-std=gnu++${alternative}"
|
||||
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
|
||||
$cachevar,
|
||||
@@ -96,22 +91,27 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
|
||||
dnl HP's aCC needs +std=c++11 according to:
|
||||
dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
|
||||
dnl Cray's crayCC needs "-h std=c++11"
|
||||
for switch in -std=c++$1 -std=c++0x +std=c++$1 "-h std=c++$1"; do
|
||||
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
|
||||
$cachevar,
|
||||
[ac_save_CXX="$CXX"
|
||||
CXX="$CXX $switch"
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[eval $cachevar=yes],
|
||||
[eval $cachevar=no])
|
||||
CXX="$ac_save_CXX"])
|
||||
if eval test x\$$cachevar = xyes; then
|
||||
CXX="$CXX $switch"
|
||||
if test -n "$CXXCPP" ; then
|
||||
CXXCPP="$CXXCPP $switch"
|
||||
for alternative in ${ax_cxx_compile_alternatives}; do
|
||||
for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
|
||||
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
|
||||
$cachevar,
|
||||
[ac_save_CXX="$CXX"
|
||||
CXX="$CXX $switch"
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[eval $cachevar=yes],
|
||||
[eval $cachevar=no])
|
||||
CXX="$ac_save_CXX"])
|
||||
if eval test x\$$cachevar = xyes; then
|
||||
CXX="$CXX $switch"
|
||||
if test -n "$CXXCPP" ; then
|
||||
CXXCPP="$CXXCPP $switch"
|
||||
fi
|
||||
ac_success=yes
|
||||
break
|
||||
fi
|
||||
ac_success=yes
|
||||
done
|
||||
if test x$ac_success = xyes; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
@@ -148,6 +148,11 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
|
||||
)
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_17
|
||||
)
|
||||
|
||||
dnl Tests for new features in C++11
|
||||
|
||||
@@ -185,11 +190,13 @@ namespace cxx11
|
||||
|
||||
struct Base
|
||||
{
|
||||
virtual ~Base() {}
|
||||
virtual void f() {}
|
||||
};
|
||||
|
||||
struct Derived : public Base
|
||||
{
|
||||
virtual ~Derived() override {}
|
||||
virtual void f() override {}
|
||||
};
|
||||
|
||||
@@ -518,7 +525,7 @@ namespace cxx14
|
||||
|
||||
}
|
||||
|
||||
namespace test_digit_seperators
|
||||
namespace test_digit_separators
|
||||
{
|
||||
|
||||
constexpr auto ten_million = 100'000'000;
|
||||
@@ -560,3 +567,385 @@ namespace cxx14
|
||||
#endif // __cplusplus >= 201402L
|
||||
|
||||
]])
|
||||
|
||||
|
||||
dnl Tests for new features in C++17
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
|
||||
|
||||
// If the compiler admits that it is not ready for C++17, why torture it?
|
||||
// Hopefully, this will speed up the test.
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#error "This is not a C++ compiler"
|
||||
|
||||
#elif __cplusplus < 201703L
|
||||
|
||||
#error "This is not a C++17 compiler"
|
||||
|
||||
#else
|
||||
|
||||
#include <initializer_list>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
namespace cxx17
|
||||
{
|
||||
|
||||
namespace test_constexpr_lambdas
|
||||
{
|
||||
|
||||
constexpr int foo = [](){return 42;}();
|
||||
|
||||
}
|
||||
|
||||
namespace test::nested_namespace::definitions
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
namespace test_fold_expression
|
||||
{
|
||||
|
||||
template<typename... Args>
|
||||
int multiply(Args... args)
|
||||
{
|
||||
return (args * ... * 1);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
bool all(Args... args)
|
||||
{
|
||||
return (args && ...);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_extended_static_assert
|
||||
{
|
||||
|
||||
static_assert (true);
|
||||
|
||||
}
|
||||
|
||||
namespace test_auto_brace_init_list
|
||||
{
|
||||
|
||||
auto foo = {5};
|
||||
auto bar {5};
|
||||
|
||||
static_assert(std::is_same<std::initializer_list<int>, decltype(foo)>::value);
|
||||
static_assert(std::is_same<int, decltype(bar)>::value);
|
||||
}
|
||||
|
||||
namespace test_typename_in_template_template_parameter
|
||||
{
|
||||
|
||||
template<template<typename> typename X> struct D;
|
||||
|
||||
}
|
||||
|
||||
namespace test_fallthrough_nodiscard_maybe_unused_attributes
|
||||
{
|
||||
|
||||
int f1()
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
|
||||
[[nodiscard]] int f2()
|
||||
{
|
||||
[[maybe_unused]] auto unused = f1();
|
||||
|
||||
switch (f1())
|
||||
{
|
||||
case 17:
|
||||
f1();
|
||||
[[fallthrough]];
|
||||
case 42:
|
||||
f1();
|
||||
}
|
||||
return f1();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_extended_aggregate_initialization
|
||||
{
|
||||
|
||||
struct base1
|
||||
{
|
||||
int b1, b2 = 42;
|
||||
};
|
||||
|
||||
struct base2
|
||||
{
|
||||
base2() {
|
||||
b3 = 42;
|
||||
}
|
||||
int b3;
|
||||
};
|
||||
|
||||
struct derived : base1, base2
|
||||
{
|
||||
int d;
|
||||
};
|
||||
|
||||
derived d1 {{1, 2}, {}, 4}; // full initialization
|
||||
derived d2 {{}, {}, 4}; // value-initialized bases
|
||||
|
||||
}
|
||||
|
||||
namespace test_general_range_based_for_loop
|
||||
{
|
||||
|
||||
struct iter
|
||||
{
|
||||
int i;
|
||||
|
||||
int& operator* ()
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
const int& operator* () const
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
iter& operator++()
|
||||
{
|
||||
++i;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
struct sentinel
|
||||
{
|
||||
int i;
|
||||
};
|
||||
|
||||
bool operator== (const iter& i, const sentinel& s)
|
||||
{
|
||||
return i.i == s.i;
|
||||
}
|
||||
|
||||
bool operator!= (const iter& i, const sentinel& s)
|
||||
{
|
||||
return !(i == s);
|
||||
}
|
||||
|
||||
struct range
|
||||
{
|
||||
iter begin() const
|
||||
{
|
||||
return {0};
|
||||
}
|
||||
|
||||
sentinel end() const
|
||||
{
|
||||
return {5};
|
||||
}
|
||||
};
|
||||
|
||||
void f()
|
||||
{
|
||||
range r {};
|
||||
|
||||
for (auto i : r)
|
||||
{
|
||||
[[maybe_unused]] auto v = i;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_lambda_capture_asterisk_this_by_value
|
||||
{
|
||||
|
||||
struct t
|
||||
{
|
||||
int i;
|
||||
int foo()
|
||||
{
|
||||
return [*this]()
|
||||
{
|
||||
return i;
|
||||
}();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace test_enum_class_construction
|
||||
{
|
||||
|
||||
enum class byte : unsigned char
|
||||
{};
|
||||
|
||||
byte foo {42};
|
||||
|
||||
}
|
||||
|
||||
namespace test_constexpr_if
|
||||
{
|
||||
|
||||
template <bool cond>
|
||||
int f ()
|
||||
{
|
||||
if constexpr(cond)
|
||||
{
|
||||
return 13;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_selection_statement_with_initializer
|
||||
{
|
||||
|
||||
int f()
|
||||
{
|
||||
return 13;
|
||||
}
|
||||
|
||||
int f2()
|
||||
{
|
||||
if (auto i = f(); i > 0)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
switch (auto i = f(); i + 4)
|
||||
{
|
||||
case 17:
|
||||
return 2;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_template_argument_deduction_for_class_templates
|
||||
{
|
||||
|
||||
template <typename T1, typename T2>
|
||||
struct pair
|
||||
{
|
||||
pair (T1 p1, T2 p2)
|
||||
: m1 {p1},
|
||||
m2 {p2}
|
||||
{}
|
||||
|
||||
T1 m1;
|
||||
T2 m2;
|
||||
};
|
||||
|
||||
void f()
|
||||
{
|
||||
[[maybe_unused]] auto p = pair{13, 42u};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_non_type_auto_template_parameters
|
||||
{
|
||||
|
||||
template <auto n>
|
||||
struct B
|
||||
{};
|
||||
|
||||
B<5> b1;
|
||||
B<'a'> b2;
|
||||
|
||||
}
|
||||
|
||||
namespace test_structured_bindings
|
||||
{
|
||||
|
||||
int arr[2] = { 1, 2 };
|
||||
std::pair<int, int> pr = { 1, 2 };
|
||||
|
||||
auto f1() -> int(&)[2]
|
||||
{
|
||||
return arr;
|
||||
}
|
||||
|
||||
auto f2() -> std::pair<int, int>&
|
||||
{
|
||||
return pr;
|
||||
}
|
||||
|
||||
struct S
|
||||
{
|
||||
int x1 : 2;
|
||||
volatile double y1;
|
||||
};
|
||||
|
||||
S f3()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
auto [ x1, y1 ] = f1();
|
||||
auto& [ xr1, yr1 ] = f1();
|
||||
auto [ x2, y2 ] = f2();
|
||||
auto& [ xr2, yr2 ] = f2();
|
||||
const auto [ x3, y3 ] = f3();
|
||||
|
||||
}
|
||||
|
||||
namespace test_exception_spec_type_system
|
||||
{
|
||||
|
||||
struct Good {};
|
||||
struct Bad {};
|
||||
|
||||
void g1() noexcept;
|
||||
void g2();
|
||||
|
||||
template<typename T>
|
||||
Bad
|
||||
f(T*, T*);
|
||||
|
||||
template<typename T1, typename T2>
|
||||
Good
|
||||
f(T1*, T2*);
|
||||
|
||||
static_assert (std::is_same_v<Good, decltype(f(g1, g2))>);
|
||||
|
||||
}
|
||||
|
||||
namespace test_inline_variables
|
||||
{
|
||||
|
||||
template<class T> void f(T)
|
||||
{}
|
||||
|
||||
template<class T> inline T g(T)
|
||||
{
|
||||
return T{};
|
||||
}
|
||||
|
||||
template<> inline void f<>(int)
|
||||
{}
|
||||
|
||||
template<> int g<>(int)
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace cxx17
|
||||
|
||||
#endif // __cplusplus < 201703L
|
||||
|
||||
]])
|
||||
@@ -458,6 +458,9 @@ cdef class DhtConfig(object):
|
||||
self._config.dht_config.node_config.network = netid
|
||||
def setMaintainStorage(self, bool maintain_storage):
|
||||
self._config.dht_config.node_config.maintain_storage = maintain_storage
|
||||
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
|
||||
|
||||
cdef class DhtRunner(_WithID):
|
||||
cdef cpp.shared_ptr[cpp.DhtRunner] thisptr
|
||||
|
||||
@@ -211,6 +211,9 @@ cdef extern from "opendht/callbacks.h" namespace "dht":
|
||||
uint32_t network
|
||||
bool is_bootstrap
|
||||
bool maintain_storage
|
||||
string persist_path
|
||||
size_t max_req_per_sec
|
||||
size_t max_peer_req_per_sec
|
||||
cppclass SecureDhtConfig:
|
||||
Config node_config
|
||||
Identity id
|
||||
|
||||
@@ -19,13 +19,16 @@ import opendht as dht
|
||||
import time
|
||||
import asyncio
|
||||
|
||||
config = dht.DhtConfig()
|
||||
config.setRateLimit(-1, -1)
|
||||
|
||||
ping_node = dht.DhtRunner()
|
||||
ping_node.run()
|
||||
ping_node.run(config=config)
|
||||
#ping_node.enableLogging()
|
||||
#ping_node.bootstrap("bootstrap.ring.cx", "4222")
|
||||
|
||||
pong_node = dht.DhtRunner()
|
||||
pong_node.run()
|
||||
pong_node.run(config=config)
|
||||
#pong_node.enableLogging()
|
||||
pong_node.ping(ping_node.getBound())
|
||||
|
||||
@@ -42,7 +45,6 @@ def done(h, ok):
|
||||
|
||||
def ping(node, h):
|
||||
global i
|
||||
time.sleep(0.0075)
|
||||
i += 1
|
||||
if i < MAX:
|
||||
node.put(h, dht.Value(b"hey"), lambda ok, nodes: done(node.getNodeId().decode(), ok))
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ lib_LTLIBRARIES = libopendht.la
|
||||
|
||||
libopendht_la_CPPFLAGS = @CPPFLAGS@ -I$(top_srcdir)/include/opendht @Argon2_CFLAGS@ @JsonCpp_CFLAGS@ @MsgPack_CFLAGS@ @OpenSSL_CFLAGS@ @Fmt_CFLAGS@
|
||||
libopendht_la_LIBADD = @Argon2_LIBS@ @JsonCpp_LIBS@ @GnuTLS_LIBS@ @Nettle_LIBS@ @OpenSSL_LIBS@ @Fmt_LIBS@
|
||||
libopendht_la_LDFLAGS = @LDFLAGS@ @Argon2_LDFLAGS@ @OpenSSL_LDFLAGS@ @Fmt_LDFLAGS@ -version-number @OPENDHT_MAJOR_VERSION@:@OPENDHT_MINOR_VERSION@:@OPENDHT_PATCH_VERSION@
|
||||
libopendht_la_LDFLAGS = @LDFLAGS@ @Argon2_LDFLAGS@ -version-number @OPENDHT_MAJOR_VERSION@:@OPENDHT_MINOR_VERSION@:@OPENDHT_PATCH_VERSION@
|
||||
libopendht_la_SOURCES = \
|
||||
dht.cpp \
|
||||
storage.h \
|
||||
|
||||
@@ -121,6 +121,7 @@ NodeInfo::toJson() const
|
||||
val["node_id"] = node_id.toString();
|
||||
val["ipv4"] = ipv4.toJson();
|
||||
val["ipv6"] = ipv6.toJson();
|
||||
val["ops"] = Json::Value::LargestUInt(ongoing_ops);
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -131,6 +132,7 @@ NodeInfo::NodeInfo(const Json::Value& v)
|
||||
node_id = InfoHash(v["node_id"].asString());
|
||||
ipv4 = NodeStats(v["ipv4"]);
|
||||
ipv6 = NodeStats(v["ipv6"]);
|
||||
ongoing_ops = v["ops"].asLargestUInt();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+16
-4
@@ -39,6 +39,7 @@ constexpr std::chrono::minutes Dht::MAX_STORAGE_MAINTENANCE_EXPIRE_TIME;
|
||||
constexpr std::chrono::minutes Dht::SEARCH_EXPIRE_TIME;
|
||||
constexpr std::chrono::seconds Dht::LISTEN_EXPIRE_TIME;
|
||||
constexpr std::chrono::seconds Dht::REANNOUNCE_MARGIN;
|
||||
static constexpr size_t MAX_REQUESTS_PER_SEC {1600};
|
||||
|
||||
NodeStatus
|
||||
Dht::getStatus(sa_family_t af) const
|
||||
@@ -77,8 +78,7 @@ Dht::shutdown(ShutdownCallback cb)
|
||||
|
||||
if (!*remaining) {
|
||||
DHT_LOG.w("shuting down node: %u ops remaining", *remaining);
|
||||
if (cb)
|
||||
cb();
|
||||
if (cb) cb();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1688,11 +1688,23 @@ Dht::~Dht()
|
||||
s.second->clear();
|
||||
}
|
||||
|
||||
net::NetworkConfig
|
||||
fromDhtConfig(const Config& config)
|
||||
{
|
||||
net::NetworkConfig netConf;
|
||||
netConf.network = config.network;
|
||||
netConf.max_req_per_sec = config.max_req_per_sec ? config.max_req_per_sec : MAX_REQUESTS_PER_SEC;
|
||||
netConf.max_peer_req_per_sec = config.max_peer_req_per_sec
|
||||
? config.max_peer_req_per_sec
|
||||
: netConf.max_req_per_sec/8;
|
||||
return netConf;
|
||||
}
|
||||
|
||||
Dht::Dht() : store(), network_engine(DHT_LOG, scheduler, {}) {}
|
||||
|
||||
Dht::Dht(std::unique_ptr<net::DatagramSocket>&& sock, const Config& config, const Logger& l)
|
||||
: DhtInterface(l), myid(config.node_id ? config.node_id : InfoHash::getRandom()), store(), store_quota(),
|
||||
network_engine(myid, config.network, std::move(sock), DHT_LOG, scheduler,
|
||||
network_engine(myid, fromDhtConfig(config), std::move(sock), DHT_LOG, scheduler,
|
||||
std::bind(&Dht::onError, this, _1, _2),
|
||||
std::bind(&Dht::onNewNode, this, _1, _2),
|
||||
std::bind(&Dht::onReportedAddr, this, _1, _2),
|
||||
@@ -1907,7 +1919,7 @@ Dht::periodic(const uint8_t *buf, size_t buflen, SockAddr from)
|
||||
try {
|
||||
network_engine.processMessage(buf, buflen, std::move(from));
|
||||
} catch (const std::exception& e) {
|
||||
DHT_LOG.e("Can't process message: %s", e.what());
|
||||
DHT_LOG.w("Can't process message: %s", e.what());
|
||||
}
|
||||
}
|
||||
return scheduler.run();
|
||||
|
||||
+82
-51
@@ -152,13 +152,22 @@ DhtProxyClient::~DhtProxyClient()
|
||||
void
|
||||
DhtProxyClient::stop()
|
||||
{
|
||||
isDestroying_ = true;
|
||||
cancelAllOperations();
|
||||
cancelAllListeners();
|
||||
if (infoState_)
|
||||
infoState_->cancel = true;
|
||||
if (httpClientThread_.joinable())
|
||||
httpClientThread_.join();
|
||||
if (not isDestroying_.exchange(true)) {
|
||||
resolver_.reset();
|
||||
cancelAllListeners();
|
||||
if (infoState_)
|
||||
infoState_->cancel = true;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(requestLock_);
|
||||
for (auto& request : requests_)
|
||||
request.second->cancel();
|
||||
}
|
||||
if (not httpContext_.stopped())
|
||||
httpContext_.stop();
|
||||
if (httpClientThread_.joinable())
|
||||
httpClientThread_.join();
|
||||
requests_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Sp<Value>>
|
||||
@@ -179,13 +188,6 @@ DhtProxyClient::getLocalById(const InfoHash& k, Value::Id id) const {
|
||||
return s->second.ops.get(id);
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyClient::cancelAllOperations()
|
||||
{
|
||||
if (!httpContext_.stopped())
|
||||
httpContext_.stop();
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyClient::cancelAllListeners()
|
||||
{
|
||||
@@ -270,10 +272,14 @@ DhtProxyClient::get(const InfoHash& key, GetCallback cb, DoneCallback donecb, Va
|
||||
if (logger_)
|
||||
logger_->d("[proxy:client] [get] [search %s]", key.to_c_str());
|
||||
|
||||
auto request = buildRequest("/" + key.toString());
|
||||
auto reqid = request->id();
|
||||
if (isDestroying_) {
|
||||
if (donecb) donecb(false, {});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
request->set_connection_type(restinio::http_connection_header_t::keep_alive);
|
||||
auto request = buildRequest("/" + key.toString());
|
||||
auto reqid = request->id();
|
||||
//request->set_connection_type(restinio::http_connection_header_t::keep_alive);
|
||||
request->set_method(restinio::http_method_get());
|
||||
setHeaderFields(*request);
|
||||
|
||||
@@ -317,14 +323,14 @@ DhtProxyClient::get(const InfoHash& key, GetCallback cb, DoneCallback donecb, Va
|
||||
request->add_on_state_change_callback([this, reqid, opstate, donecb, key]
|
||||
(http::Request::State state, const http::Response& response){
|
||||
if (state == http::Request::State::DONE){
|
||||
if (response.status_code != 200){
|
||||
if (response.status_code != 200) {
|
||||
if (logger_)
|
||||
logger_->e("[proxy:client] [get %s] failed with code=%i", key.to_c_str(), response.status_code);
|
||||
opstate->ok.store(false);
|
||||
if (response.status_code == 0)
|
||||
if (not response.aborted and response.status_code == 0)
|
||||
opFailed();
|
||||
}
|
||||
if (donecb){
|
||||
if (donecb) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(lockCallbacks_);
|
||||
callbacks_.emplace_back([donecb, opstate](){
|
||||
@@ -334,25 +340,29 @@ DhtProxyClient::get(const InfoHash& key, GetCallback cb, DoneCallback donecb, Va
|
||||
}
|
||||
loopSignal_();
|
||||
}
|
||||
requests_.erase(reqid);
|
||||
if (not isDestroying_) {
|
||||
std::lock_guard<std::mutex> l(requestLock_);
|
||||
requests_.erase(reqid);
|
||||
}
|
||||
}
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> l(requestLock_);
|
||||
requests_[reqid] = request;
|
||||
}
|
||||
request->send();
|
||||
requests_[reqid] = request;
|
||||
}
|
||||
catch (const std::exception &e){
|
||||
if (logger_)
|
||||
logger_->e("[proxy:client] [get %s] error: %s", key.to_c_str(), e.what());
|
||||
requests_.erase(reqid);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyClient::put(const InfoHash& key, Sp<Value> val, DoneCallback cb, time_point created, bool permanent)
|
||||
{
|
||||
if (not val){
|
||||
if (cb)
|
||||
cb(false, {});
|
||||
if (not val or isDestroying_) {
|
||||
if (cb) cb(false, {});
|
||||
return;
|
||||
}
|
||||
if (logger_)
|
||||
@@ -438,9 +448,9 @@ DhtProxyClient::doPut(const InfoHash& key, Sp<Value> val, DoneCallbackSimple cb,
|
||||
if (logger_)
|
||||
logger_->d("[proxy:client] [put] [search %s] executing for %s", key.to_c_str(), val->toString().c_str());
|
||||
|
||||
auto request = buildRequest("/" + key.toString());
|
||||
auto reqid = request->id();
|
||||
try {
|
||||
auto request = buildRequest("/" + key.toString());
|
||||
auto reqid = request->id();
|
||||
request->set_method(restinio::http_method_post());
|
||||
setHeaderFields(*request);
|
||||
|
||||
@@ -466,21 +476,26 @@ DhtProxyClient::doPut(const InfoHash& key, Sp<Value> val, DoneCallbackSimple cb,
|
||||
if (not ok) {
|
||||
if (logger_)
|
||||
logger_->e("[proxy:client] [status] failed with code=%i", response.status_code);
|
||||
if (response.status_code == 0)
|
||||
if (not response.aborted and response.status_code == 0)
|
||||
opFailed();
|
||||
}
|
||||
if (cb)
|
||||
cb(ok);
|
||||
requests_.erase(reqid);
|
||||
if (not isDestroying_) {
|
||||
std::lock_guard<std::mutex> l(requestLock_);
|
||||
requests_.erase(reqid);
|
||||
}
|
||||
}
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> l(requestLock_);
|
||||
requests_[reqid] = request;
|
||||
}
|
||||
request->send();
|
||||
requests_[reqid] = request;
|
||||
}
|
||||
catch (const std::exception &e){
|
||||
if (logger_)
|
||||
logger_->e("[proxy:client] [put %s] error: %s", key.to_c_str(), e.what());
|
||||
requests_.erase(reqid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -564,9 +579,9 @@ DhtProxyClient::queryProxyInfo(std::shared_ptr<InfoState> infoState, sa_family_t
|
||||
{
|
||||
if (logger_)
|
||||
logger_->d("[proxy:client] [status] query ipv%i info", family == AF_INET ? 4 : 6);
|
||||
auto request = std::make_shared<http::Request>(httpContext_, resolver, family);
|
||||
auto reqid = request->id();
|
||||
try {
|
||||
auto request = std::make_shared<http::Request>(httpContext_, resolver, family);
|
||||
auto reqid = request->id();
|
||||
request->set_method(restinio::http_method_get());
|
||||
setHeaderFields(*request);
|
||||
request->add_on_state_change_callback([this, reqid, family, infoState]
|
||||
@@ -592,20 +607,24 @@ DhtProxyClient::queryProxyInfo(std::shared_ptr<InfoState> infoState, sa_family_t
|
||||
if (not infoState->cancel)
|
||||
onProxyInfos(proxyInfos, family);
|
||||
}
|
||||
requests_.erase(reqid);
|
||||
if (not isDestroying_) {
|
||||
std::lock_guard<std::mutex> l(requestLock_);
|
||||
requests_.erase(reqid);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (infoState->cancel.load())
|
||||
return;
|
||||
|
||||
requests_[reqid] = request;
|
||||
{
|
||||
std::lock_guard<std::mutex> l(requestLock_);
|
||||
requests_[reqid] = request;
|
||||
}
|
||||
request->send();
|
||||
}
|
||||
catch (const std::exception &e){
|
||||
if (logger_)
|
||||
logger_->e("[proxy:client] [status] error sending request: %s", e.what());
|
||||
requests_.erase(reqid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -688,11 +707,13 @@ DhtProxyClient::listen(const InfoHash& key, ValueCallback cb, Value::Filter filt
|
||||
{
|
||||
if (logger_)
|
||||
logger_->d("[proxy:client] [listen] [search %s]", key.to_c_str());
|
||||
if (isDestroying_)
|
||||
return 0;
|
||||
|
||||
std::lock_guard<std::mutex> lock(searchLock_);
|
||||
auto& search = searches_[key];
|
||||
auto query = std::make_shared<Query>(Select{}, std::move(where));
|
||||
return search.ops.listen(cb, query, filter, [this, key](Sp<Query>, ValueCallback cb, SyncCallback) -> size_t {
|
||||
std::lock_guard<std::mutex> lock(searchLock_);
|
||||
// Find search
|
||||
auto search = searches_.find(key);
|
||||
if (search == searches_.end()) {
|
||||
@@ -860,19 +881,24 @@ DhtProxyClient::handleExpireListener(const asio::error_code &ec, const InfoHash&
|
||||
if (logger_)
|
||||
logger_->e("[proxy:client] [unsubscribe %s] failed with code=%i",
|
||||
key.to_c_str(), response.status_code);
|
||||
if (response.status_code == 0)
|
||||
if (not response.aborted and response.status_code == 0)
|
||||
opFailed();
|
||||
}
|
||||
requests_.erase(reqid);
|
||||
if (not isDestroying_) {
|
||||
std::lock_guard<std::mutex> l(requestLock_);
|
||||
requests_.erase(reqid);
|
||||
}
|
||||
}
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> l(requestLock_);
|
||||
requests_[reqid] = request;
|
||||
}
|
||||
request->send();
|
||||
requests_[reqid] = request;
|
||||
}
|
||||
catch (const std::exception &e){
|
||||
if (logger_)
|
||||
logger_->e("[proxy:client] [unsubscribe %s] failed: %s", key.to_c_str(), e.what());
|
||||
requests_.erase(reqid);
|
||||
}
|
||||
} else {
|
||||
// stop the request
|
||||
@@ -901,10 +927,10 @@ DhtProxyClient::sendListen(const restinio::http_request_header_t header,
|
||||
{
|
||||
if (logger_)
|
||||
logger_->e("[proxy:client] [listen] sendListen: %d", (int)method);
|
||||
auto request = buildRequest();
|
||||
listener.request = request;
|
||||
auto reqid = request->id();
|
||||
try {
|
||||
auto request = buildRequest();
|
||||
listener.request = request;
|
||||
auto reqid = request->id();
|
||||
request->set_header(header);
|
||||
setHeaderFields(*request);
|
||||
if (method == ListenMethod::LISTEN)
|
||||
@@ -962,19 +988,24 @@ DhtProxyClient::sendListen(const restinio::http_request_header_t header,
|
||||
logger_->e("[proxy:client] [listen] send request #%i failed with code=%i",
|
||||
reqid, response.status_code);
|
||||
opstate->ok.store(false);
|
||||
if (response.status_code == 0)
|
||||
if (not response.aborted and response.status_code == 0)
|
||||
opFailed();
|
||||
}
|
||||
requests_.erase(reqid);
|
||||
if (not isDestroying_) {
|
||||
std::lock_guard<std::mutex> l(requestLock_);
|
||||
requests_.erase(reqid);
|
||||
}
|
||||
}
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> l(requestLock_);
|
||||
requests_[reqid] = request;
|
||||
}
|
||||
request->send();
|
||||
requests_[reqid] = request;
|
||||
}
|
||||
catch (const std::exception &e){
|
||||
if (logger_)
|
||||
logger_->e("[proxy:client] [listen] request #%i failed: %s", reqid, e.what());
|
||||
requests_.erase(reqid);
|
||||
logger_->e("[proxy:client] [listen] request failed: %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+128
-115
@@ -71,37 +71,30 @@ constexpr const std::chrono::minutes PRINT_STATS_PERIOD {2};
|
||||
class opendht_logger_t
|
||||
{
|
||||
public:
|
||||
opendht_logger_t(std::shared_ptr<dht::Logger> logger = {}){
|
||||
if (logger)
|
||||
m_logger = logger;
|
||||
opendht_logger_t(std::shared_ptr<Logger> logger = {}) : m_logger(std::move(logger)) {}
|
||||
|
||||
template <typename Builder>
|
||||
void trace(Builder&& /* msg_builder */) {
|
||||
/* if (m_logger) m_logger->d("[proxy:server] %s", msg_builder().c_str()); */
|
||||
}
|
||||
|
||||
template <typename Builder>
|
||||
void trace(Builder && msg_builder){
|
||||
if (m_logger)
|
||||
m_logger->d("[proxy:server] %s", msg_builder().c_str());
|
||||
void info(Builder&& msg_builder) {
|
||||
if (m_logger) m_logger->d("[proxy:server] %s", msg_builder().c_str());
|
||||
}
|
||||
|
||||
template <typename Builder>
|
||||
void info(Builder && msg_builder){
|
||||
if (m_logger)
|
||||
m_logger->d("[proxy:server] %s", msg_builder().c_str());
|
||||
void warn(Builder&& msg_builder) {
|
||||
if (m_logger) m_logger->w("[proxy:server] %s", msg_builder().c_str());
|
||||
}
|
||||
|
||||
template <typename Builder>
|
||||
void warn(Builder && msg_builder){
|
||||
if (m_logger)
|
||||
m_logger->w("[proxy:server] %s", msg_builder().c_str());
|
||||
}
|
||||
|
||||
template <typename Builder>
|
||||
void error(Builder && msg_builder){
|
||||
if (m_logger)
|
||||
m_logger->e("[proxy:server] %s", msg_builder().c_str());
|
||||
void error(Builder&& msg_builder) {
|
||||
if (m_logger) m_logger->e("[proxy:server] %s", msg_builder().c_str());
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<dht::Logger> m_logger;
|
||||
std::shared_ptr<Logger> m_logger;
|
||||
};
|
||||
|
||||
// connection listener
|
||||
@@ -166,9 +159,9 @@ struct DhtProxyServer::RestRouterTraits : public restinio::default_traits_t
|
||||
};
|
||||
|
||||
DhtProxyServer::DhtProxyServer(
|
||||
dht::crypto::Identity identity,
|
||||
crypto::Identity identity,
|
||||
std::shared_ptr<DhtRunner> dht, in_port_t port, const std::string& pushServer,
|
||||
std::shared_ptr<dht::Logger> logger
|
||||
std::shared_ptr<Logger> logger
|
||||
)
|
||||
: dht_(dht), logger_(logger),
|
||||
connListener_(std::make_shared<ConnectionListener>(std::bind(&DhtProxyServer::onConnectionClosed, this, std::placeholders::_1))),
|
||||
@@ -280,6 +273,27 @@ DhtProxyServer::io_context() const
|
||||
|
||||
DhtProxyServer::~DhtProxyServer()
|
||||
{
|
||||
if (dht_) {
|
||||
std::lock_guard<std::mutex> lock(lockListener_);
|
||||
for (auto& l : listeners_) {
|
||||
dht_->cancelListen(l.second.hash, std::move(l.second.token));
|
||||
if (l.second.response)
|
||||
l.second.response->done();
|
||||
}
|
||||
#ifdef OPENDHT_PUSH_NOTIFICATIONS
|
||||
for (auto& lm: pushListeners_) {
|
||||
for (auto& ls: lm.second.listeners)
|
||||
for (auto& l : ls.second) {
|
||||
if (l.expireNotifyTimer)
|
||||
l.expireNotifyTimer->cancel();
|
||||
if (l.expireTimer)
|
||||
l.expireTimer->cancel();
|
||||
dht_->cancelListen(ls.first, std::move(l.internalToken));
|
||||
}
|
||||
}
|
||||
pushListeners_.clear();
|
||||
#endif
|
||||
}
|
||||
if (logger_)
|
||||
logger_->d("[proxy:server] closing http server");
|
||||
if (httpServer_)
|
||||
@@ -372,7 +386,6 @@ HttpResponse DhtProxyServer::initHttpResponse(HttpResponse response) const
|
||||
response.append_header("Server", "RESTinio");
|
||||
response.append_header(restinio::http_field::content_type, "application/json");
|
||||
response.append_header(restinio::http_field::access_control_allow_origin, "*");
|
||||
response.connection_keep_alive();
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -460,14 +473,14 @@ DhtProxyServer::getNodeInfo(restinio::request_handle_t request,
|
||||
std::lock_guard<std::mutex> lck(statsMutex_);
|
||||
if (nodeInfo_.ipv4.good_nodes == 0 &&
|
||||
nodeInfo_.ipv6.good_nodes == 0){
|
||||
nodeInfo_ = this->dht_->getNodeInfo();
|
||||
nodeInfo_ = dht_->getNodeInfo();
|
||||
}
|
||||
result = nodeInfo_.toJson();
|
||||
// [ipv6:ipv4]:port or ipv4:port
|
||||
result["public_ip"] = request->remote_endpoint().address().to_string();
|
||||
auto output = Json::writeString(jsonBuilder_, result) + "\n";
|
||||
|
||||
auto response = this->initHttpResponse(request->create_response());
|
||||
auto response = initHttpResponse(request->create_response());
|
||||
response.append_body(output);
|
||||
return response.done();
|
||||
}
|
||||
@@ -480,17 +493,17 @@ DhtProxyServer::getStats(restinio::request_handle_t request,
|
||||
try {
|
||||
if (dht_){
|
||||
auto output = Json::writeString(jsonBuilder_, stats_.toJson()) + "\n";
|
||||
auto response = this->initHttpResponse(request->create_response());
|
||||
auto response = initHttpResponse(request->create_response());
|
||||
response.append_body(output);
|
||||
response.done();
|
||||
} else {
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_service_unavailable()));
|
||||
response.set_body(RESP_MSG_SERVICE_UNAVAILABLE);
|
||||
return response.done();
|
||||
}
|
||||
} catch (...){
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_internal_server_error()));
|
||||
response.set_body(RESP_MSG_INTERNAL_SERVER_ERRROR);
|
||||
return response.done();
|
||||
@@ -503,22 +516,22 @@ DhtProxyServer::get(restinio::request_handle_t request,
|
||||
restinio::router::route_params_t params)
|
||||
{
|
||||
requestNum_++;
|
||||
dht::InfoHash infoHash(params["hash"].to_string());
|
||||
InfoHash infoHash(params["hash"].to_string());
|
||||
if (!infoHash)
|
||||
infoHash = dht::InfoHash::get(params["hash"].to_string());
|
||||
infoHash = InfoHash::get(params["hash"].to_string());
|
||||
|
||||
if (!dht_){
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_service_unavailable()));
|
||||
response.set_body(RESP_MSG_SERVICE_UNAVAILABLE);
|
||||
return response.done();
|
||||
}
|
||||
|
||||
auto response = std::make_shared<ResponseByPartsBuilder>(
|
||||
this->initHttpResponse(request->create_response<ResponseByParts>()));
|
||||
initHttpResponse(request->create_response<ResponseByParts>()));
|
||||
response->flush();
|
||||
try {
|
||||
dht_->get(infoHash, [this, response](const dht::Sp<dht::Value>& value){
|
||||
dht_->get(infoHash, [this, response](const Sp<Value>& value){
|
||||
auto output = Json::writeString(jsonBuilder_, value->toJson()) + "\n";
|
||||
response->append_chunk(output);
|
||||
response->flush();
|
||||
@@ -528,7 +541,7 @@ DhtProxyServer::get(restinio::request_handle_t request,
|
||||
response->done();
|
||||
});
|
||||
} catch (const std::exception& e){
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_internal_server_error()));
|
||||
response.set_body(RESP_MSG_INTERNAL_SERVER_ERRROR);
|
||||
return response.done();
|
||||
@@ -541,18 +554,18 @@ DhtProxyServer::listen(restinio::request_handle_t request,
|
||||
restinio::router::route_params_t params)
|
||||
{
|
||||
requestNum_++;
|
||||
dht::InfoHash infoHash(params["hash"].to_string());
|
||||
InfoHash infoHash(params["hash"].to_string());
|
||||
if (!infoHash)
|
||||
infoHash = dht::InfoHash::get(params["hash"].to_string());
|
||||
infoHash = InfoHash::get(params["hash"].to_string());
|
||||
|
||||
if (!dht_){
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_service_unavailable()));
|
||||
response.set_body(RESP_MSG_SERVICE_UNAVAILABLE);
|
||||
return response.done();
|
||||
}
|
||||
auto response = std::make_shared<ResponseByPartsBuilder>(
|
||||
this->initHttpResponse(request->create_response<ResponseByParts>()));
|
||||
initHttpResponse(request->create_response<ResponseByParts>()));
|
||||
response->flush();
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(lockListener_);
|
||||
@@ -561,7 +574,7 @@ DhtProxyServer::listen(restinio::request_handle_t request,
|
||||
session.hash = infoHash;
|
||||
session.response = response;
|
||||
session.token = dht_->listen(infoHash, [this, response]
|
||||
(const std::vector<dht::Sp<dht::Value>>& values, bool expired){
|
||||
(const std::vector<Sp<Value>>& values, bool expired){
|
||||
for (const auto& value: values){
|
||||
auto jsonVal = value->toJson();
|
||||
if (expired)
|
||||
@@ -574,7 +587,7 @@ DhtProxyServer::listen(restinio::request_handle_t request,
|
||||
});
|
||||
|
||||
} catch (const std::exception& e){
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_internal_server_error()));
|
||||
response.set_body(RESP_MSG_INTERNAL_SERVER_ERRROR);
|
||||
return response.done();
|
||||
@@ -590,12 +603,12 @@ DhtProxyServer::subscribe(restinio::request_handle_t request,
|
||||
{
|
||||
requestNum_++;
|
||||
|
||||
dht::InfoHash infoHash(params["hash"].to_string());
|
||||
InfoHash infoHash(params["hash"].to_string());
|
||||
if (!infoHash)
|
||||
infoHash = dht::InfoHash::get(params["hash"].to_string());
|
||||
infoHash = InfoHash::get(params["hash"].to_string());
|
||||
|
||||
if (!dht_){
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_service_unavailable()));
|
||||
response.set_body(RESP_MSG_SERVICE_UNAVAILABLE);
|
||||
return response.done();
|
||||
@@ -606,14 +619,14 @@ DhtProxyServer::subscribe(restinio::request_handle_t request,
|
||||
auto* char_data = reinterpret_cast<const char*>(request->body().data());
|
||||
auto reader = std::unique_ptr<Json::CharReader>(jsonReaderBuilder_.newCharReader());
|
||||
if (!reader->parse(char_data, char_data + request->body().size(), &root, &err)){
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_bad_request()));
|
||||
response.set_body(RESP_MSG_JSON_INCORRECT);
|
||||
return response.done();
|
||||
}
|
||||
auto pushToken = root["key"].asString();
|
||||
if (pushToken.empty()){
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_bad_request()));
|
||||
response.set_body(RESP_MSG_NO_TOKEN);
|
||||
return response.done();
|
||||
@@ -643,11 +656,11 @@ DhtProxyServer::subscribe(restinio::request_handle_t request,
|
||||
listener.expireNotifyTimer->expires_at(timeout - proxy::OP_MARGIN);
|
||||
// Send response header
|
||||
auto response = std::make_shared<ResponseByPartsBuilder>(
|
||||
this->initHttpResponse(request->create_response<ResponseByParts>()));
|
||||
initHttpResponse(request->create_response<ResponseByParts>()));
|
||||
response->flush();
|
||||
// No Refresh
|
||||
if (!root.isMember("refresh") or !root["refresh"].asBool()){
|
||||
dht_->get(infoHash, [this, response](const dht::Sp<dht::Value>& value){
|
||||
dht_->get(infoHash, [this, response](const Sp<Value>& value){
|
||||
auto output = Json::writeString(jsonBuilder_, value->toJson()) + "\n";
|
||||
response->append_chunk(output);
|
||||
response->flush();
|
||||
@@ -709,12 +722,12 @@ DhtProxyServer::subscribe(restinio::request_handle_t request,
|
||||
listener.expireTimer->expires_at(timeout);
|
||||
listener.expireTimer->async_wait(std::bind(&DhtProxyServer::handleCancelPushListen, this,
|
||||
std::placeholders::_1, pushToken, infoHash, clientId));
|
||||
auto response = this->initHttpResponse(request->create_response());
|
||||
auto response = initHttpResponse(request->create_response());
|
||||
response.set_body("{}\n");
|
||||
return response.done();
|
||||
}
|
||||
catch (...) {
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_internal_server_error()));
|
||||
response.set_body(RESP_MSG_INTERNAL_SERVER_ERRROR);
|
||||
return response.done();
|
||||
@@ -728,12 +741,12 @@ DhtProxyServer::unsubscribe(restinio::request_handle_t request,
|
||||
{
|
||||
requestNum_++;
|
||||
|
||||
dht::InfoHash infoHash(params["hash"].to_string());
|
||||
InfoHash infoHash(params["hash"].to_string());
|
||||
if (!infoHash)
|
||||
infoHash = dht::InfoHash::get(params["hash"].to_string());
|
||||
infoHash = InfoHash::get(params["hash"].to_string());
|
||||
|
||||
if (!dht_){
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_service_unavailable()));
|
||||
response.set_body(RESP_MSG_SERVICE_UNAVAILABLE);
|
||||
return response.done();
|
||||
@@ -748,7 +761,7 @@ DhtProxyServer::unsubscribe(restinio::request_handle_t request,
|
||||
auto reader = std::unique_ptr<Json::CharReader>(jsonReaderBuilder_.newCharReader());
|
||||
|
||||
if (!reader->parse(char_data, char_data + request->body().size(), &root, &err)){
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_bad_request()));
|
||||
response.set_body(RESP_MSG_JSON_INCORRECT);
|
||||
return response.done();
|
||||
@@ -759,11 +772,11 @@ DhtProxyServer::unsubscribe(restinio::request_handle_t request,
|
||||
auto clientId = root["client_id"].asString();
|
||||
|
||||
handleCancelPushListen(asio::error_code() /*success*/, pushToken, infoHash, clientId);
|
||||
auto response = this->initHttpResponse(request->create_response());
|
||||
auto response = initHttpResponse(request->create_response());
|
||||
return response.done();
|
||||
}
|
||||
catch (...) {
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_internal_server_error()));
|
||||
response.set_body(RESP_MSG_INTERNAL_SERVER_ERRROR);
|
||||
return response.done();
|
||||
@@ -787,7 +800,7 @@ DhtProxyServer::handleNotifyPushListenExpire(const asio::error_code &ec, const s
|
||||
|
||||
void
|
||||
DhtProxyServer::handleCancelPushListen(const asio::error_code &ec, const std::string pushToken,
|
||||
const dht::InfoHash key, const std::string clientId)
|
||||
const InfoHash key, const std::string clientId)
|
||||
{
|
||||
if (ec == asio::error::operation_aborted)
|
||||
return;
|
||||
@@ -829,13 +842,13 @@ DhtProxyServer::sendPushNotification(const std::string& token, Json::Value&& jso
|
||||
if (pushServer_.empty())
|
||||
return;
|
||||
|
||||
auto request = std::make_shared<http::Request>(io_context(), pushHostPort_.first, pushHostPort_.second,
|
||||
httpsServer_ ? true : false, logger_);
|
||||
auto reqid = request->id();
|
||||
unsigned reqid = 0;
|
||||
try {
|
||||
auto request = std::make_shared<http::Request>(io_context(), pushHostPort_.first, pushHostPort_.second,
|
||||
httpsServer_ ? true : false, logger_);
|
||||
reqid = request->id();
|
||||
request->set_target("/api/push");
|
||||
request->set_method(restinio::http_method_post());
|
||||
|
||||
request->set_header_field(restinio::http_field_t::host, pushServer_.c_str());
|
||||
request->set_header_field(restinio::http_field_t::user_agent, "RESTinio client");
|
||||
request->set_header_field(restinio::http_field_t::accept, "*/*");
|
||||
@@ -856,29 +869,29 @@ DhtProxyServer::sendPushNotification(const std::string& token, Json::Value&& jso
|
||||
|
||||
Json::Value content;
|
||||
content["notifications"] = std::move(notifications);
|
||||
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
wbuilder["commentStyle"] = "None";
|
||||
wbuilder["indentation"] = "";
|
||||
|
||||
auto body = Json::writeString(wbuilder, content);
|
||||
request->set_body(body);
|
||||
|
||||
request->set_body(Json::writeString(jsonBuilder_, content));
|
||||
request->add_on_state_change_callback([this, reqid]
|
||||
(const http::Request::State state, const http::Response response){
|
||||
(http::Request::State state, const http::Response& response){
|
||||
if (state == http::Request::State::DONE){
|
||||
if (logger_ and response.status_code != 200)
|
||||
logger_->e("[proxy:server] [notification] push failed: %i", response.status_code);
|
||||
std::lock_guard<std::mutex> l(requestLock_);
|
||||
requests_.erase(reqid);
|
||||
}
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> l(requestLock_);
|
||||
requests_[reqid] = request;
|
||||
}
|
||||
request->send();
|
||||
requests_[reqid] = request;
|
||||
}
|
||||
catch (const std::exception &e){
|
||||
if (logger_)
|
||||
logger_->e("[proxy:server] [notification] error send push: %i", e.what());
|
||||
requests_.erase(reqid);
|
||||
if (reqid) {
|
||||
std::lock_guard<std::mutex> l(requestLock_);
|
||||
requests_.erase(reqid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -916,9 +929,9 @@ DhtProxyServer::put(restinio::request_handle_t request,
|
||||
restinio::router::route_params_t params)
|
||||
{
|
||||
requestNum_++;
|
||||
dht::InfoHash infoHash(params["hash"].to_string());
|
||||
InfoHash infoHash(params["hash"].to_string());
|
||||
if (!infoHash)
|
||||
infoHash = dht::InfoHash::get(params["hash"].to_string());
|
||||
infoHash = InfoHash::get(params["hash"].to_string());
|
||||
|
||||
if (!dht_){
|
||||
auto response = initHttpResponse(request->create_response(restinio::status_service_unavailable()));
|
||||
@@ -938,7 +951,7 @@ DhtProxyServer::put(restinio::request_handle_t request,
|
||||
auto reader = std::unique_ptr<Json::CharReader>(jsonReaderBuilder_.newCharReader());
|
||||
|
||||
if (reader->parse(char_data, char_data + request->body().size(), &root, &err)){
|
||||
auto value = std::make_shared<dht::Value>(root);
|
||||
auto value = std::make_shared<Value>(root);
|
||||
bool permanent = root.isMember("permanent");
|
||||
if (logger_)
|
||||
logger_->d("[proxy:server] [put %s] %s %s", infoHash.toString().c_str(),
|
||||
@@ -994,18 +1007,18 @@ DhtProxyServer::put(restinio::request_handle_t request,
|
||||
dht_->put(infoHash, value, [this, request, value](bool ok){
|
||||
if (ok){
|
||||
auto output = Json::writeString(jsonBuilder_, value->toJson()) + "\n";
|
||||
auto response = this->initHttpResponse(request->create_response());
|
||||
auto response = initHttpResponse(request->create_response());
|
||||
response.append_body(output);
|
||||
response.done();
|
||||
} else {
|
||||
auto response = this->initHttpResponse(request->create_response(
|
||||
auto response = initHttpResponse(request->create_response(
|
||||
restinio::status_bad_gateway()));
|
||||
response.set_body(RESP_MSG_PUT_FAILED);
|
||||
response.done();
|
||||
}
|
||||
}, dht::time_point::max(), permanent);
|
||||
}, time_point::max(), permanent);
|
||||
} else {
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_bad_request()));
|
||||
response.set_body(RESP_MSG_JSON_INCORRECT);
|
||||
return response.done();
|
||||
@@ -1013,7 +1026,7 @@ DhtProxyServer::put(restinio::request_handle_t request,
|
||||
} catch (const std::exception& e){
|
||||
if (logger_)
|
||||
logger_->d("[proxy:server] error in put: %s", e.what());
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_internal_server_error()));
|
||||
response.set_body(RESP_MSG_INTERNAL_SERVER_ERRROR);
|
||||
return response.done();
|
||||
@@ -1023,22 +1036,23 @@ DhtProxyServer::put(restinio::request_handle_t request,
|
||||
|
||||
#ifdef OPENDHT_PROXY_SERVER_IDENTITY
|
||||
|
||||
RequestStatus DhtProxyServer::putSigned(restinio::request_handle_t request,
|
||||
restinio::router::route_params_t params) const
|
||||
RequestStatus
|
||||
DhtProxyServer::putSigned(restinio::request_handle_t request,
|
||||
restinio::router::route_params_t params) const
|
||||
{
|
||||
requestNum_++;
|
||||
dht::InfoHash infoHash(params["hash"].to_string());
|
||||
InfoHash infoHash(params["hash"].to_string());
|
||||
if (!infoHash)
|
||||
infoHash = dht::InfoHash::get(params["hash"].to_string());
|
||||
infoHash = InfoHash::get(params["hash"].to_string());
|
||||
|
||||
if (!dht_){
|
||||
auto response = this->initHttpResponse(
|
||||
if (!dht_) {
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_service_unavailable()));
|
||||
response.set_body(RESP_MSG_SERVICE_UNAVAILABLE);
|
||||
return response.done();
|
||||
}
|
||||
else if (request->body().empty()){
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_bad_request()));
|
||||
response.set_body(RESP_MSG_MISSING_PARAMS);
|
||||
return response.done();
|
||||
@@ -1057,18 +1071,18 @@ RequestStatus DhtProxyServer::putSigned(restinio::request_handle_t request,
|
||||
dht_->putSigned(infoHash, value, [this, request, value](bool ok){
|
||||
if (ok){
|
||||
auto output = Json::writeString(jsonBuilder_, value->toJson()) + "\n";
|
||||
auto response = this->initHttpResponse(request->create_response());
|
||||
auto response = initHttpResponse(request->create_response());
|
||||
response.append_body(output);
|
||||
response.done();
|
||||
} else {
|
||||
auto response = this->initHttpResponse(request->create_response(
|
||||
auto response = initHttpResponse(request->create_response(
|
||||
restinio::status_bad_gateway()));
|
||||
response.set_body(RESP_MSG_PUT_FAILED);
|
||||
response.done();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_bad_request()));
|
||||
response.set_body(RESP_MSG_JSON_INCORRECT);
|
||||
return response.done();
|
||||
@@ -1076,7 +1090,7 @@ RequestStatus DhtProxyServer::putSigned(restinio::request_handle_t request,
|
||||
} catch (const std::exception& e){
|
||||
if (logger_)
|
||||
logger_->d("[proxy:server] error in put: %s", e.what());
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_internal_server_error()));
|
||||
response.set_body(RESP_MSG_INTERNAL_SERVER_ERRROR);
|
||||
return response.done();
|
||||
@@ -1089,18 +1103,18 @@ DhtProxyServer::putEncrypted(restinio::request_handle_t request,
|
||||
restinio::router::route_params_t params)
|
||||
{
|
||||
requestNum_++;
|
||||
dht::InfoHash infoHash(params["hash"].to_string());
|
||||
InfoHash infoHash(params["hash"].to_string());
|
||||
if (!infoHash)
|
||||
infoHash = dht::InfoHash::get(params["hash"].to_string());
|
||||
infoHash = InfoHash::get(params["hash"].to_string());
|
||||
|
||||
if (!dht_){
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_service_unavailable()));
|
||||
response.set_body(RESP_MSG_SERVICE_UNAVAILABLE);
|
||||
return response.done();
|
||||
}
|
||||
else if (request->body().empty()){
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_bad_request()));
|
||||
response.set_body(RESP_MSG_MISSING_PARAMS);
|
||||
return response.done();
|
||||
@@ -1115,7 +1129,7 @@ DhtProxyServer::putEncrypted(restinio::request_handle_t request,
|
||||
if (reader->parse(char_data, char_data + request->body().size(), &root, &err)){
|
||||
InfoHash to(root["to"].asString());
|
||||
if (!to){
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_bad_request()));
|
||||
response.set_body(RESP_MSG_DESTINATION_NOT_FOUND);
|
||||
return response.done();
|
||||
@@ -1124,18 +1138,18 @@ DhtProxyServer::putEncrypted(restinio::request_handle_t request,
|
||||
dht_->putEncrypted(infoHash, to, value, [this, request, value](bool ok){
|
||||
if (ok){
|
||||
auto output = Json::writeString(jsonBuilder_, value->toJson()) + "\n";
|
||||
auto response = this->initHttpResponse(request->create_response());
|
||||
auto response = initHttpResponse(request->create_response());
|
||||
response.append_body(output);
|
||||
response.done();
|
||||
} else {
|
||||
auto response = this->initHttpResponse(request->create_response(
|
||||
auto response = initHttpResponse(request->create_response(
|
||||
restinio::status_bad_gateway()));
|
||||
response.set_body(RESP_MSG_PUT_FAILED);
|
||||
response.done();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_bad_request()));
|
||||
response.set_body(RESP_MSG_JSON_INCORRECT);
|
||||
return response.done();
|
||||
@@ -1143,7 +1157,7 @@ DhtProxyServer::putEncrypted(restinio::request_handle_t request,
|
||||
} catch (const std::exception& e){
|
||||
if (logger_)
|
||||
logger_->d("[proxy:server] error in put: %s", e.what());
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_internal_server_error()));
|
||||
response.set_body(RESP_MSG_INTERNAL_SERVER_ERRROR);
|
||||
return response.done();
|
||||
@@ -1157,7 +1171,7 @@ RequestStatus
|
||||
DhtProxyServer::options(restinio::request_handle_t request,
|
||||
restinio::router::route_params_t /*params*/)
|
||||
{
|
||||
this->requestNum_++;
|
||||
requestNum_++;
|
||||
#ifdef OPENDHT_PROXY_SERVER_IDENTITY
|
||||
const auto methods = "OPTIONS, GET, POST, LISTEN, SIGN, ENCRYPT";
|
||||
#else
|
||||
@@ -1176,34 +1190,33 @@ DhtProxyServer::getFiltered(restinio::request_handle_t request,
|
||||
{
|
||||
requestNum_++;
|
||||
auto value = params["value"].to_string();
|
||||
dht::InfoHash infoHash(params["hash"].to_string());
|
||||
InfoHash infoHash(params["hash"].to_string());
|
||||
if (!infoHash)
|
||||
infoHash = dht::InfoHash::get(params["hash"].to_string());
|
||||
infoHash = InfoHash::get(params["hash"].to_string());
|
||||
|
||||
if (!dht_){
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_service_unavailable()));
|
||||
response.set_body(RESP_MSG_SERVICE_UNAVAILABLE);
|
||||
return response.done();
|
||||
}
|
||||
|
||||
auto response = std::make_shared<ResponseByPartsBuilder>(
|
||||
this->initHttpResponse(request->create_response<ResponseByParts>()));
|
||||
initHttpResponse(request->create_response<ResponseByParts>()));
|
||||
response->flush();
|
||||
try {
|
||||
dht_->get(infoHash, [this, response](const dht::Sp<dht::Value>& value){
|
||||
auto output = Json::writeString(jsonBuilder_, value->toJson()) + "\n";
|
||||
response->append_chunk(output);
|
||||
response->flush();
|
||||
return true;
|
||||
},
|
||||
[response] (bool /*ok*/){
|
||||
response->done();
|
||||
},
|
||||
{}, value
|
||||
);
|
||||
dht_->get(infoHash,
|
||||
[this, response](const Sp<Value>& value) {
|
||||
response->append_chunk(Json::writeString(jsonBuilder_, value->toJson()) + "\n");
|
||||
response->flush();
|
||||
return true;
|
||||
},
|
||||
[response] (bool /*ok*/){
|
||||
response->done();
|
||||
},
|
||||
{}, value);
|
||||
} catch (const std::exception& e){
|
||||
auto response = this->initHttpResponse(
|
||||
auto response = initHttpResponse(
|
||||
request->create_response(restinio::status_internal_server_error()));
|
||||
response.set_body(RESP_MSG_INTERNAL_SERVER_ERRROR);
|
||||
return response.done();
|
||||
|
||||
+247
-149
@@ -100,7 +100,7 @@ DhtRunner::run(const char* ip4, const char* ip6, const char* service, const Conf
|
||||
void
|
||||
DhtRunner::run(const SockAddr& local4, const SockAddr& local6, const Config& config, Context&& context)
|
||||
{
|
||||
if (not running) {
|
||||
if (running == State::Idle) {
|
||||
if (not context.sock)
|
||||
context.sock.reset(new net::UdpSocket(local4, local6, context.logger ? *context.logger : Logger{}));
|
||||
run(config, std::move(context));
|
||||
@@ -111,11 +111,14 @@ void
|
||||
DhtRunner::run(const Config& config, Context&& context)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
if (running)
|
||||
auto expected = State::Idle;
|
||||
if (not running.compare_exchange_strong(expected, State::Running))
|
||||
return;
|
||||
|
||||
if (context.logger)
|
||||
if (context.logger) {
|
||||
logger_ = context.logger;
|
||||
logger_->d("[runner %p] state changed to Running", this);
|
||||
}
|
||||
|
||||
context.sock->setOnReceive([&] (std::unique_ptr<net::ReceivedPacket>&& pkt) {
|
||||
{
|
||||
@@ -148,16 +151,15 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
dht_via_proxy_->setLocalCertificateStore(std::move(context.certificateStore));
|
||||
}
|
||||
|
||||
running = true;
|
||||
if (not config.threaded)
|
||||
return;
|
||||
dht_thread = std::thread([this]() {
|
||||
while (running) {
|
||||
while (running != State::Idle) {
|
||||
std::unique_lock<std::mutex> lk(dht_mtx);
|
||||
time_point wakeup = loop_();
|
||||
|
||||
auto hasJobToDo = [this]() {
|
||||
if (not running)
|
||||
if (running == State::Idle)
|
||||
return true;
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(sock_mtx);
|
||||
@@ -219,36 +221,84 @@ DhtRunner::run(const Config& config, Context&& context)
|
||||
|
||||
void
|
||||
DhtRunner::shutdown(ShutdownCallback cb) {
|
||||
if (not running) {
|
||||
cb();
|
||||
auto expected = State::Running;
|
||||
if (not running.compare_exchange_strong(expected, State::Stopping)) {
|
||||
if (expected == State::Stopping and ongoing_ops) {
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
shutdownCallbacks_.emplace_back(std::move(cb));
|
||||
}
|
||||
else if (cb) cb();
|
||||
return;
|
||||
}
|
||||
if (logger_)
|
||||
logger_->d("[runner %p] state changed to Stopping, %zu ongoing ops", this, ongoing_ops.load());
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
ongoing_ops++;
|
||||
shutdownCallbacks_.emplace_back(std::move(cb));
|
||||
pending_ops_prio.emplace([=](SecureDht&) mutable {
|
||||
auto onShutdown = [this]{ opEnded(); };
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
if (dht_via_proxy_)
|
||||
dht_via_proxy_->shutdown(cb);
|
||||
dht_via_proxy_->shutdown(onShutdown);
|
||||
#endif
|
||||
if (dht_)
|
||||
dht_->shutdown(cb);
|
||||
dht_->shutdown(onShutdown);
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::opEnded() {
|
||||
if (--ongoing_ops == 0)
|
||||
checkShutdown();
|
||||
}
|
||||
|
||||
DoneCallback
|
||||
DhtRunner::bindOpDoneCallback(DoneCallback&& cb) {
|
||||
return [this, cb = std::move(cb)](bool ok, const std::vector<std::shared_ptr<Node>>& nodes){
|
||||
if (cb) cb(ok, nodes);
|
||||
opEnded();
|
||||
};
|
||||
}
|
||||
|
||||
DoneCallbackSimple
|
||||
DhtRunner::bindOpDoneCallback(DoneCallbackSimple&& cb) {
|
||||
return [this, cb = std::move(cb)](bool ok){
|
||||
if (cb) cb(ok);
|
||||
opEnded();
|
||||
};
|
||||
}
|
||||
|
||||
bool
|
||||
DhtRunner::checkShutdown() {
|
||||
if (running != State::Stopping or ongoing_ops)
|
||||
return false;
|
||||
decltype(shutdownCallbacks_) cbs;
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
cbs = std::move(shutdownCallbacks_);
|
||||
}
|
||||
for (auto& cb : cbs)
|
||||
if (cb) cb();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::join()
|
||||
{
|
||||
if (peerDiscovery_)
|
||||
peerDiscovery_->stop();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
running = false;
|
||||
if (running.exchange(State::Idle) == State::Idle)
|
||||
return;
|
||||
cv.notify_all();
|
||||
bootstrap_cv.notify_all();
|
||||
if (peerDiscovery_)
|
||||
peerDiscovery_->stop();
|
||||
if (dht_)
|
||||
if (auto sock = dht_->getSocket())
|
||||
sock->stop();
|
||||
if (logger_)
|
||||
logger_->d("[runner %p] state changed to Idle", this);
|
||||
}
|
||||
|
||||
if (dht_thread.joinable())
|
||||
@@ -257,14 +307,14 @@ DhtRunner::join()
|
||||
if (bootstrap_thread.joinable())
|
||||
bootstrap_thread.join();
|
||||
|
||||
if (peerDiscovery_) {
|
||||
if (peerDiscovery_)
|
||||
peerDiscovery_->join();
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops = decltype(pending_ops)();
|
||||
pending_ops_prio = decltype(pending_ops_prio)();
|
||||
ongoing_ops = 0;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
@@ -283,6 +333,15 @@ DhtRunner::getBound(sa_family_t af) const {
|
||||
return SockAddr{};
|
||||
}
|
||||
|
||||
in_port_t
|
||||
DhtRunner::getBoundPort(sa_family_t af) const {
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
if (dht_)
|
||||
if (auto sock = dht_->getSocket())
|
||||
return sock->getPort(af);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::dumpTables() const
|
||||
{
|
||||
@@ -409,6 +468,7 @@ DhtRunner::getNodeInfo() const {
|
||||
NodeInfo info;
|
||||
info.id = getId();
|
||||
info.node_id = getNodeId();
|
||||
info.ongoing_ops = ongoing_ops;
|
||||
info.ipv4 = dht_->getNodesStats(AF_INET);
|
||||
info.ipv6 = dht_->getNodesStats(AF_INET6);
|
||||
return info;
|
||||
@@ -563,12 +623,15 @@ DhtRunner::loop_()
|
||||
void
|
||||
DhtRunner::get(InfoHash hash, GetCallback vcb, DoneCallback dcb, Value::Filter f, Where w)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) mutable {
|
||||
dht.get(hash, std::move(vcb), std::move(dcb), std::move(f), std::move(w));
|
||||
});
|
||||
if (running != State::Running) {
|
||||
if (dcb) dcb(false, {});
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
ongoing_ops++;
|
||||
pending_ops.emplace([=](SecureDht& dht) mutable {
|
||||
dht.get(hash, std::move(vcb), bindOpDoneCallback(std::move(dcb)), std::move(f), std::move(w));
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
@@ -579,12 +642,15 @@ DhtRunner::get(const std::string& key, GetCallback vcb, DoneCallbackSimple dcb,
|
||||
}
|
||||
void
|
||||
DhtRunner::query(const InfoHash& hash, QueryCallback cb, DoneCallback done_cb, Query q) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) mutable {
|
||||
dht.query(hash, std::move(cb), std::move(done_cb), std::move(q));
|
||||
});
|
||||
if (running != State::Running) {
|
||||
if (done_cb) done_cb(false, {});
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
ongoing_ops++;
|
||||
pending_ops.emplace([=](SecureDht& dht) mutable {
|
||||
dht.query(hash, std::move(cb), bindOpDoneCallback(std::move(done_cb)), std::move(q));
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
@@ -592,32 +658,34 @@ std::future<size_t>
|
||||
DhtRunner::listen(InfoHash hash, ValueCallback vcb, Value::Filter f, Where w)
|
||||
{
|
||||
auto ret_token = std::make_shared<std::promise<size_t>>();
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) mutable {
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
auto tokenbGlobal = listener_token_++;
|
||||
auto& listener = listeners_[tokenbGlobal];
|
||||
listener.hash = hash;
|
||||
listener.f = std::move(f);
|
||||
listener.w = std::move(w);
|
||||
listener.gcb = [hash,vcb,tokenbGlobal,this](const std::vector<Sp<Value>>& vals, bool expired) {
|
||||
if (not vcb(vals, expired)) {
|
||||
cancelListen(hash, tokenbGlobal);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
if (auto token = dht.listen(hash, listener.gcb, listener.f, listener.w)) {
|
||||
if (use_proxy) listener.tokenProxyDht = token;
|
||||
else listener.tokenClassicDht = token;
|
||||
}
|
||||
ret_token->set_value(tokenbGlobal);
|
||||
#else
|
||||
ret_token->set_value(dht.listen(hash, std::move(vcb), std::move(f), std::move(w)));
|
||||
#endif
|
||||
});
|
||||
if (running != State::Running) {
|
||||
ret_token->set_value(0);
|
||||
return ret_token->get_future();
|
||||
}
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) mutable {
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
auto tokenbGlobal = listener_token_++;
|
||||
auto& listener = listeners_[tokenbGlobal];
|
||||
listener.hash = hash;
|
||||
listener.f = std::move(f);
|
||||
listener.w = std::move(w);
|
||||
listener.gcb = [hash,vcb,tokenbGlobal,this](const std::vector<Sp<Value>>& vals, bool expired) {
|
||||
if (not vcb(vals, expired)) {
|
||||
cancelListen(hash, tokenbGlobal);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
if (auto token = dht.listen(hash, listener.gcb, listener.f, listener.w)) {
|
||||
if (use_proxy) listener.tokenProxyDht = token;
|
||||
else listener.tokenClassicDht = token;
|
||||
}
|
||||
ret_token->set_value(tokenbGlobal);
|
||||
#else
|
||||
ret_token->set_value(dht.listen(hash, std::move(vcb), std::move(f), std::move(w)));
|
||||
#endif
|
||||
});
|
||||
cv.notify_all();
|
||||
return ret_token->get_future();
|
||||
}
|
||||
@@ -631,73 +699,77 @@ DhtRunner::listen(const std::string& key, GetCallback vcb, Value::Filter f, Wher
|
||||
void
|
||||
DhtRunner::cancelListen(InfoHash h, size_t token)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
pending_ops.emplace([=](SecureDht&) {
|
||||
auto it = listeners_.find(token);
|
||||
if (it == listeners_.end()) return;
|
||||
if (it->second.tokenClassicDht)
|
||||
dht_->cancelListen(h, it->second.tokenClassicDht);
|
||||
if (it->second.tokenProxyDht and dht_via_proxy_)
|
||||
dht_via_proxy_->cancelListen(h, it->second.tokenProxyDht);
|
||||
listeners_.erase(it);
|
||||
});
|
||||
pending_ops.emplace([=](SecureDht&) {
|
||||
auto it = listeners_.find(token);
|
||||
if (it == listeners_.end()) return;
|
||||
if (it->second.tokenClassicDht)
|
||||
dht_->cancelListen(h, it->second.tokenClassicDht);
|
||||
if (it->second.tokenProxyDht and dht_via_proxy_)
|
||||
dht_via_proxy_->cancelListen(h, it->second.tokenProxyDht);
|
||||
listeners_.erase(it);
|
||||
});
|
||||
#else
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.cancelListen(h, token);
|
||||
});
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.cancelListen(h, token);
|
||||
});
|
||||
#endif // OPENDHT_PROXY_CLIENT
|
||||
}
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::cancelListen(InfoHash h, std::shared_future<size_t> ftoken)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
pending_ops.emplace([=](SecureDht&) {
|
||||
auto it = listeners_.find(ftoken.get());
|
||||
if (it == listeners_.end()) return;
|
||||
if (it->second.tokenClassicDht)
|
||||
dht_->cancelListen(h, it->second.tokenClassicDht);
|
||||
if (it->second.tokenProxyDht and dht_via_proxy_)
|
||||
dht_via_proxy_->cancelListen(h, it->second.tokenProxyDht);
|
||||
listeners_.erase(it);
|
||||
});
|
||||
pending_ops.emplace([=](SecureDht&) {
|
||||
auto it = listeners_.find(ftoken.get());
|
||||
if (it == listeners_.end()) return;
|
||||
if (it->second.tokenClassicDht)
|
||||
dht_->cancelListen(h, it->second.tokenClassicDht);
|
||||
if (it->second.tokenProxyDht and dht_via_proxy_)
|
||||
dht_via_proxy_->cancelListen(h, it->second.tokenProxyDht);
|
||||
listeners_.erase(it);
|
||||
});
|
||||
#else
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.cancelListen(h, ftoken.get());
|
||||
});
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.cancelListen(h, ftoken.get());
|
||||
});
|
||||
#endif // OPENDHT_PROXY_CLIENT
|
||||
}
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::put(InfoHash hash, Value&& value, DoneCallback cb, time_point created, bool permanent)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
auto sv = std::make_shared<Value>(std::move(value));
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.put(hash, sv, cb, created, permanent);
|
||||
});
|
||||
if (running != State::Running) {
|
||||
if (cb) cb(false, {});
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
ongoing_ops++;
|
||||
pending_ops.emplace([=,
|
||||
cb = std::move(cb),
|
||||
sv = std::make_shared<Value>(std::move(value))
|
||||
] (SecureDht& dht) mutable {
|
||||
dht.put(hash, sv, bindOpDoneCallback(std::move(cb)), created, permanent);
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::put(InfoHash hash, std::shared_ptr<Value> value, DoneCallback cb, time_point created, bool permanent)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.put(hash, value, cb, created, permanent);
|
||||
});
|
||||
if (running != State::Running) {
|
||||
if (cb) cb(false, {});
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
ongoing_ops++;
|
||||
pending_ops.emplace([=, cb = std::move(cb)](SecureDht& dht) mutable {
|
||||
dht.put(hash, value, bindOpDoneCallback(std::move(cb)), created, permanent);
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
@@ -710,36 +782,38 @@ DhtRunner::put(const std::string& key, Value&& value, DoneCallbackSimple cb, tim
|
||||
void
|
||||
DhtRunner::cancelPut(const InfoHash& h, Value::Id id)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.cancelPut(h, id);
|
||||
});
|
||||
}
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.cancelPut(h, id);
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::cancelPut(const InfoHash& h, const std::shared_ptr<Value>& value)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.cancelPut(h, value->id);
|
||||
});
|
||||
}
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.cancelPut(h, value->id);
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::putSigned(InfoHash hash, std::shared_ptr<Value> value, DoneCallback cb, bool permanent)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.putSigned(hash, value, cb, permanent);
|
||||
});
|
||||
if (running != State::Running) {
|
||||
if (cb) cb(false, {});
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
ongoing_ops++;
|
||||
pending_ops.emplace([=,
|
||||
cb = std::move(cb),
|
||||
value = std::move(value)
|
||||
](SecureDht& dht) mutable {
|
||||
dht.putSigned(hash, value, bindOpDoneCallback(std::move(cb)), permanent);
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
@@ -758,12 +832,18 @@ DhtRunner::putSigned(const std::string& key, Value&& value, DoneCallbackSimple c
|
||||
void
|
||||
DhtRunner::putEncrypted(InfoHash hash, InfoHash to, std::shared_ptr<Value> value, DoneCallback cb, bool permanent)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.putEncrypted(hash, to, value, cb, permanent);
|
||||
});
|
||||
if (running != State::Running) {
|
||||
if (cb) cb(false, {});
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
ongoing_ops++;
|
||||
pending_ops.emplace([=,
|
||||
cb = std::move(cb),
|
||||
value = std::move(value)
|
||||
] (SecureDht& dht) mutable {
|
||||
dht.putEncrypted(hash, to, value, bindOpDoneCallback(std::move(cb)), permanent);
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
@@ -808,7 +888,7 @@ DhtRunner::tryBootstrapContinuously()
|
||||
++ping_count;
|
||||
try {
|
||||
bootstrap(SockAddr::resolve(it->first, it->second), [&](bool) {
|
||||
if (not running)
|
||||
if (running != State::Running)
|
||||
return;
|
||||
{
|
||||
std::unique_lock<std::mutex> blck(mtx);
|
||||
@@ -822,15 +902,15 @@ DhtRunner::tryBootstrapContinuously()
|
||||
}
|
||||
}
|
||||
// wait at least until the next BOOTSTRAP_PERIOD
|
||||
bootstrap_cv.wait_until(blck, next, [&]() { return not running; });
|
||||
bootstrap_cv.wait_until(blck, next, [&]() { return running != State::Running; });
|
||||
// wait for bootstrap requests to end.
|
||||
if (running)
|
||||
bootstrap_cv.wait(blck, [&]() { return not running or ping_count == 0; });
|
||||
if (running != State::Running)
|
||||
bootstrap_cv.wait(blck, [&]() { return running != State::Running or ping_count == 0; });
|
||||
}
|
||||
// update state
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(dht_mtx);
|
||||
bootstraping = running and
|
||||
bootstraping = running == State::Running and
|
||||
status4 == NodeStatus::Disconnected and
|
||||
status6 == NodeStatus::Disconnected;
|
||||
}
|
||||
@@ -867,19 +947,28 @@ DhtRunner::clearBootstrap()
|
||||
void
|
||||
DhtRunner::bootstrap(std::vector<SockAddr> nodes, DoneCallbackSimple&& cb)
|
||||
{
|
||||
if (running != State::Running) {
|
||||
cb(false);
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([=](SecureDht& dht) mutable {
|
||||
ongoing_ops++;
|
||||
pending_ops_prio.emplace([
|
||||
cb = bindOpDoneCallback(std::move(cb)),
|
||||
nodes = std::move(nodes)
|
||||
] (SecureDht& dht) mutable {
|
||||
auto rem = cb ? std::make_shared<std::pair<size_t, bool>>(nodes.size(), false) : nullptr;
|
||||
for (auto& node : nodes) {
|
||||
if (node.getPort() == 0)
|
||||
node.setPort(net::DHT_DEFAULT_PORT);
|
||||
dht.pingNode(std::move(node), cb ? [rem,cb](bool ok) {
|
||||
dht.pingNode(std::move(node), [rem,cb](bool ok) {
|
||||
auto& r = *rem;
|
||||
r.first--;
|
||||
r.second |= ok;
|
||||
if (not r.first)
|
||||
if (r.first == 0) {
|
||||
cb(r.second);
|
||||
} : DoneCallbackSimple{});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
cv.notify_all();
|
||||
@@ -888,8 +977,13 @@ DhtRunner::bootstrap(std::vector<SockAddr> nodes, DoneCallbackSimple&& cb)
|
||||
void
|
||||
DhtRunner::bootstrap(const SockAddr& addr, DoneCallbackSimple&& cb)
|
||||
{
|
||||
if (running != State::Running) {
|
||||
if (cb) cb(false);
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([addr, cb](SecureDht& dht) mutable {
|
||||
ongoing_ops++;
|
||||
pending_ops_prio.emplace([addr, cb = bindOpDoneCallback(std::move(cb))](SecureDht& dht) mutable {
|
||||
dht.pingNode(std::move(addr), std::move(cb));
|
||||
});
|
||||
cv.notify_all();
|
||||
@@ -898,48 +992,52 @@ DhtRunner::bootstrap(const SockAddr& addr, DoneCallbackSimple&& cb)
|
||||
void
|
||||
DhtRunner::bootstrap(const InfoHash& id, const SockAddr& address)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([id, address](SecureDht& dht) mutable {
|
||||
dht.insertNode(id, address);
|
||||
});
|
||||
}
|
||||
if (running != State::Running)
|
||||
return;
|
||||
std::unique_lock<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([id, address](SecureDht& dht) mutable {
|
||||
dht.insertNode(id, address);
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::bootstrap(const std::vector<NodeExport>& nodes)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([=](SecureDht& dht) {
|
||||
for (auto& node : nodes)
|
||||
dht.insertNode(node);
|
||||
});
|
||||
}
|
||||
if (running != State::Running)
|
||||
return;
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([=](SecureDht& dht) {
|
||||
for (auto& node : nodes)
|
||||
dht.insertNode(node);
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::connectivityChanged()
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([=](SecureDht& dht) {
|
||||
dht.connectivityChanged();
|
||||
});
|
||||
}
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops_prio.emplace([=](SecureDht& dht) {
|
||||
dht.connectivityChanged();
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunner::findCertificate(InfoHash hash, std::function<void(const std::shared_ptr<crypto::Certificate>)> cb) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
pending_ops.emplace([=](SecureDht& dht) {
|
||||
dht.findCertificate(hash, cb);
|
||||
});
|
||||
DhtRunner::findCertificate(InfoHash hash, std::function<void(const Sp<crypto::Certificate>&)> cb) {
|
||||
if (running != State::Running) {
|
||||
cb({});
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> lck(storage_mtx);
|
||||
ongoing_ops++;
|
||||
pending_ops.emplace([this, hash, cb = std::move(cb)] (SecureDht& dht) {
|
||||
dht.findCertificate(hash, [this, cb = std::move(cb)](const Sp<crypto::Certificate>& crt){
|
||||
cb(crt);
|
||||
opEnded();
|
||||
});
|
||||
});
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
|
||||
+314
-205
@@ -33,12 +33,11 @@ constexpr char HTTP_HEADER_CONNECTION[] = "Connection";
|
||||
constexpr char HTTP_HEADER_CONNECTION_KEEP_ALIVE[] = "keep-alive";
|
||||
constexpr char HTTP_HEADER_CONNECTION_CLOSE[] = "close";
|
||||
constexpr char HTTP_HEADER_CONTENT_LENGTH[] = "Content-Length";
|
||||
constexpr char HTTP_HEADER_CONTENT_TYPE[] = "Content-Type";
|
||||
constexpr char HTTP_HEADER_CONTENT_TYPE_JSON[] = "application/json";
|
||||
constexpr char HTTP_HEADER_TRANSFER_ENCODING[] = "Transfer-Encoding";
|
||||
constexpr char HTTP_HEADER_TRANSFER_ENCODING_CHUNKED[] = "chunked";
|
||||
constexpr char HTTP_HEADER_DELIM[] = "\r\n\r\n";
|
||||
constexpr char BODY_VALUE_DELIM[] = "\n";
|
||||
constexpr char BODY_VALUE_DELIM = '\n';
|
||||
|
||||
Url::Url(const std::string& url): url(url)
|
||||
{
|
||||
@@ -77,10 +76,10 @@ Url::Url(const std::string& url): url(url)
|
||||
|
||||
// connection
|
||||
|
||||
unsigned int Connection::ids_ = 1;
|
||||
std::atomic_uint Connection::ids_ {1};
|
||||
|
||||
Connection::Connection(asio::io_context& ctx, const bool ssl, std::shared_ptr<dht::Logger> l)
|
||||
: id_(Connection::ids_++), ctx_(ctx), logger_(l)
|
||||
: id_(Connection::ids_++), ctx_(ctx), istream_(&read_buf_), logger_(l)
|
||||
{
|
||||
if (ssl){
|
||||
ssl_ctx_ = std::make_shared<asio::ssl::context>(asio::ssl::context::sslv23);
|
||||
@@ -88,18 +87,18 @@ Connection::Connection(asio::io_context& ctx, const bool ssl, std::shared_ptr<dh
|
||||
ssl_ctx_->set_verify_mode(asio::ssl::verify_none);
|
||||
ssl_socket_ = std::make_unique<ssl_socket_t>(ctx_, ssl_ctx_);
|
||||
if (logger_)
|
||||
logger_->d("[http:client] [connection:%i] start https session", id_);
|
||||
logger_->d("[connection:%i] start https session", id_);
|
||||
}
|
||||
else {
|
||||
socket_ = std::make_unique<socket_t>(ctx);
|
||||
if (logger_)
|
||||
logger_->d("[http:client] [connection:%i] start http session", id_);
|
||||
logger_->d("[connection:%i] start http session", id_);
|
||||
}
|
||||
}
|
||||
|
||||
Connection::Connection(asio::io_context& ctx, std::shared_ptr<dht::crypto::Certificate> server_ca,
|
||||
const dht::crypto::Identity& identity, std::shared_ptr<dht::Logger> l)
|
||||
: id_(Connection::ids_++), ctx_(ctx), logger_(l)
|
||||
: id_(Connection::ids_++), ctx_(ctx), istream_(&read_buf_), logger_(l)
|
||||
{
|
||||
ssl_ctx_ = std::make_shared<asio::ssl::context>(asio::ssl::context::sslv23);
|
||||
ssl_ctx_->set_default_verify_paths();
|
||||
@@ -110,7 +109,7 @@ Connection::Connection(asio::io_context& ctx, std::shared_ptr<dht::crypto::Certi
|
||||
if (ec)
|
||||
throw std::runtime_error("Error adding certificate authority: " + ec.message());
|
||||
else if (logger_)
|
||||
logger_->d("[http:client] [connection:%i] certficate authority %s", id_, server_ca->getUID().c_str());
|
||||
logger_->d("[connection:%i] certficate authority %s", id_, server_ca->getUID().c_str());
|
||||
}
|
||||
if (identity.first){
|
||||
auto key = identity.first->serialize();
|
||||
@@ -125,7 +124,7 @@ Connection::Connection(asio::io_context& ctx, std::shared_ptr<dht::crypto::Certi
|
||||
if (ec)
|
||||
throw std::runtime_error("Error adding client certificate: " + ec.message());
|
||||
else if (logger_)
|
||||
logger_->d("[http:client] [connection:%i] client certificate %s", id_, identity.second->getUID().c_str());
|
||||
logger_->d("[connection:%i] client certificate %s", id_, identity.second->getUID().c_str());
|
||||
}
|
||||
ssl_ctx_->set_verify_mode(asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert);
|
||||
ssl_socket_ = std::make_unique<ssl_socket_t>(ctx_, ssl_ctx_);
|
||||
@@ -139,36 +138,29 @@ Connection::~Connection()
|
||||
void
|
||||
Connection::close()
|
||||
{
|
||||
if (!is_open())
|
||||
return;
|
||||
asio::error_code ec;
|
||||
if (ssl_socket_)
|
||||
ssl_socket_->close(ec);
|
||||
else if (socket_)
|
||||
socket_->close(ec);
|
||||
if (ssl_socket_) {
|
||||
if (ssl_socket_->is_open())
|
||||
ssl_socket_->close(ec);
|
||||
}
|
||||
else if (socket_) {
|
||||
if (socket_->is_open())
|
||||
socket_->close(ec);
|
||||
}
|
||||
if (ec and logger_)
|
||||
logger_->e("[http:client] [connection:%i] error closing: %s", id_, ec.message().c_str());
|
||||
}
|
||||
|
||||
unsigned int
|
||||
Connection::id()
|
||||
{
|
||||
return id_;
|
||||
logger_->e("[connection:%i] error closing: %s", id_, ec.message().c_str());
|
||||
}
|
||||
|
||||
bool
|
||||
Connection::is_open()
|
||||
Connection::is_open() const
|
||||
{
|
||||
if (ssl_socket_)
|
||||
return ssl_socket_->is_open();
|
||||
else if (socket_)
|
||||
return socket_->is_open();
|
||||
else
|
||||
return false;
|
||||
if (ssl_socket_) return ssl_socket_->is_open();
|
||||
else if (socket_) return socket_->is_open();
|
||||
else return false;
|
||||
}
|
||||
|
||||
bool
|
||||
Connection::is_ssl()
|
||||
Connection::is_ssl() const
|
||||
{
|
||||
return ssl_ctx_ ? true : false;
|
||||
}
|
||||
@@ -176,24 +168,26 @@ Connection::is_ssl()
|
||||
void
|
||||
Connection::set_ssl_verification(const asio::ip::tcp::endpoint& endpoint, const asio::ssl::verify_mode verify_mode)
|
||||
{
|
||||
if (ssl_socket_ and verify_mode != asio::ssl::verify_none){
|
||||
auto hostname = endpoint.address().to_string();
|
||||
if (ssl_socket_ and verify_mode != asio::ssl::verify_none) {
|
||||
ssl_socket_->asio_ssl_stream().set_verify_mode(verify_mode);
|
||||
ssl_socket_->asio_ssl_stream().set_verify_callback(
|
||||
[this, hostname](bool preverified, asio::ssl::verify_context& ctx) -> bool {
|
||||
ssl_socket_->asio_ssl_stream().set_verify_callback([
|
||||
id = id_,
|
||||
logger = logger_,
|
||||
hostname = endpoint.address().to_string()
|
||||
] (bool preverified, asio::ssl::verify_context& ctx) -> bool {
|
||||
if (logger)
|
||||
logger->d("[connection:%i] verify %s compliance to RFC 2818", id, hostname.c_str());
|
||||
if (preverified)
|
||||
return preverified;
|
||||
// starts from CA and goes down the presented chain
|
||||
auto verifier = asio::ssl::rfc2818_verification(hostname);
|
||||
bool verified = verifier(preverified, ctx);
|
||||
auto verify_ec = X509_STORE_CTX_get_error(ctx.native_handle());
|
||||
if (verified != 0 /*X509_V_OK*/ and logger_)
|
||||
logger_->e("[http::connection:%i] ssl verification error=%i", id_, verify_ec);
|
||||
if (verified != 0 /*X509_V_OK*/ and logger)
|
||||
logger->e("[http::connection:%i] ssl verification error=%i", id, verify_ec);
|
||||
return verified;
|
||||
}
|
||||
);
|
||||
if (logger_)
|
||||
logger_->d("[http:client] [connection:%i] verify %s compliance to RFC 2818", id_, hostname.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,19 +197,12 @@ Connection::input()
|
||||
return write_buf_;
|
||||
}
|
||||
|
||||
asio::streambuf&
|
||||
Connection::data()
|
||||
{
|
||||
return read_buf_;
|
||||
}
|
||||
|
||||
std::string
|
||||
Connection::read_bytes(const size_t bytes)
|
||||
Connection::read_bytes(size_t bytes)
|
||||
{
|
||||
std::string content;
|
||||
std::istream is(&read_buf_);
|
||||
content.resize(bytes);
|
||||
auto rb = is.readsome(&content[0], bytes);
|
||||
auto rb = istream_.readsome(&content[0], bytes);
|
||||
content.resize(rb);
|
||||
return content;
|
||||
}
|
||||
@@ -224,8 +211,7 @@ std::string
|
||||
Connection::read_until(const char delim)
|
||||
{
|
||||
std::string content;
|
||||
std::istream is(&read_buf_);
|
||||
std::getline(is, content, delim);
|
||||
std::getline(istream_, content, delim);
|
||||
return content;
|
||||
}
|
||||
|
||||
@@ -243,23 +229,28 @@ Connection::async_connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, Conn
|
||||
void
|
||||
Connection::async_handshake(HandlerCb cb)
|
||||
{
|
||||
if (ssl_socket_)
|
||||
if (ssl_socket_) {
|
||||
std::weak_ptr<Connection> wthis = shared_from_this();
|
||||
ssl_socket_->async_handshake(asio::ssl::stream<asio::ip::tcp::socket>::client,
|
||||
[this, cb](const asio::error_code& ec)
|
||||
[wthis, cb](const asio::error_code& ec)
|
||||
{
|
||||
if (ec == asio::error::operation_aborted)
|
||||
return;
|
||||
auto verify_ec = SSL_get_verify_result(ssl_socket_->asio_ssl_stream().native_handle());
|
||||
if (logger_){
|
||||
if (verify_ec == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT /*18*/
|
||||
|| verify_ec == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN /*19*/)
|
||||
logger_->d("[http:client] [connection:%i] allow self-signed certificate in handshake", id_);
|
||||
else if (verify_ec != X509_V_OK)
|
||||
logger_->e("[http:client] [connection:%i] verify handshake error: %i", id_, verify_ec);
|
||||
if (auto sthis = wthis.lock()) {
|
||||
auto& this_ = *sthis;
|
||||
auto verify_ec = SSL_get_verify_result(this_.ssl_socket_->asio_ssl_stream().native_handle());
|
||||
if (this_.logger_) {
|
||||
if (verify_ec == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT /*18*/
|
||||
|| verify_ec == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN /*19*/)
|
||||
this_.logger_->d("[connection:%i] allow self-signed certificate in handshake", this_.id_);
|
||||
else if (verify_ec != X509_V_OK)
|
||||
this_.logger_->e("[connection:%i] verify handshake error: %i", this_.id_, verify_ec);
|
||||
}
|
||||
}
|
||||
if (cb)
|
||||
cb(ec);
|
||||
});
|
||||
}
|
||||
else if (socket_)
|
||||
cb(asio::error::no_protocol_option);
|
||||
else if (cb)
|
||||
@@ -269,40 +260,37 @@ Connection::async_handshake(HandlerCb cb)
|
||||
void
|
||||
Connection::async_write(BytesHandlerCb cb)
|
||||
{
|
||||
if (!is_open())
|
||||
return;
|
||||
if (ssl_socket_)
|
||||
asio::async_write(*ssl_socket_, write_buf_, cb);
|
||||
else if (socket_)
|
||||
asio::async_write(*socket_, write_buf_, cb);
|
||||
else if (cb)
|
||||
cb(asio::error::operation_aborted, 0);
|
||||
if (!is_open()) return;
|
||||
if (ssl_socket_) asio::async_write(*ssl_socket_, write_buf_, cb);
|
||||
else if (socket_) asio::async_write(*socket_, write_buf_, cb);
|
||||
else if (cb) cb(asio::error::operation_aborted, 0);
|
||||
}
|
||||
|
||||
void
|
||||
Connection::async_read_until(const char* delim, BytesHandlerCb cb)
|
||||
{
|
||||
if (!is_open())
|
||||
return;
|
||||
if (ssl_socket_)
|
||||
asio::async_read_until(*ssl_socket_, read_buf_, delim, cb);
|
||||
else if (socket_)
|
||||
asio::async_read_until(*socket_, read_buf_, delim, cb);
|
||||
else if (cb)
|
||||
cb(asio::error::operation_aborted, 0);
|
||||
if (!is_open()) return;
|
||||
if (ssl_socket_) asio::async_read_until(*ssl_socket_, read_buf_, delim, cb);
|
||||
else if (socket_) asio::async_read_until(*socket_, read_buf_, delim, cb);
|
||||
else if (cb) cb(asio::error::operation_aborted, 0);
|
||||
}
|
||||
|
||||
void
|
||||
Connection::async_read(const size_t bytes, BytesHandlerCb cb)
|
||||
Connection::async_read_until(char delim, BytesHandlerCb cb)
|
||||
{
|
||||
if (!is_open())
|
||||
return;
|
||||
if (ssl_socket_)
|
||||
asio::async_read(*ssl_socket_, read_buf_, asio::transfer_exactly(bytes), cb);
|
||||
else if (socket_)
|
||||
asio::async_read(*socket_, read_buf_, asio::transfer_exactly(bytes), cb);
|
||||
else if (cb)
|
||||
cb(asio::error::operation_aborted, 0);
|
||||
if (!is_open()) return;
|
||||
if (ssl_socket_) asio::async_read_until(*ssl_socket_, read_buf_, delim, cb);
|
||||
else if (socket_) asio::async_read_until(*socket_, read_buf_, delim, cb);
|
||||
else if (cb) cb(asio::error::operation_aborted, 0);
|
||||
}
|
||||
|
||||
void
|
||||
Connection::async_read(size_t bytes, BytesHandlerCb cb)
|
||||
{
|
||||
if (!is_open()) return;
|
||||
if (ssl_socket_) asio::async_read(*ssl_socket_, read_buf_, asio::transfer_exactly(bytes), cb);
|
||||
else if (socket_) asio::async_read(*socket_, read_buf_, asio::transfer_exactly(bytes), cb);
|
||||
else if (cb) cb(asio::error::operation_aborted, 0);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -310,7 +298,7 @@ Connection::timeout(const std::chrono::seconds timeout, HandlerCb cb)
|
||||
{
|
||||
if (!is_open()){
|
||||
if (logger_)
|
||||
logger_->e("[http:client] [connection:%i] closed, can't timeout", id_);
|
||||
logger_->e("[connection:%i] closed, can't timeout", id_);
|
||||
if (cb)
|
||||
cb(asio::error::operation_aborted);
|
||||
return;
|
||||
@@ -318,12 +306,12 @@ Connection::timeout(const std::chrono::seconds timeout, HandlerCb cb)
|
||||
if (!timeout_timer_)
|
||||
timeout_timer_ = std::make_unique<asio::steady_timer>(ctx_);
|
||||
timeout_timer_->expires_at(std::chrono::steady_clock::now() + timeout);
|
||||
timeout_timer_->async_wait([this, cb](const asio::error_code &ec){
|
||||
timeout_timer_->async_wait([id=id_, logger=logger_, cb](const asio::error_code &ec){
|
||||
if (ec == asio::error::operation_aborted)
|
||||
return;
|
||||
else if (ec){
|
||||
if (logger_)
|
||||
logger_->e("[http:client] [connection:%i] timeout error: %s", id_, ec.message().c_str());
|
||||
if (logger)
|
||||
logger->e("[connection:%i] timeout error: %s", id, ec.message().c_str());
|
||||
}
|
||||
if (cb)
|
||||
cb(ec);
|
||||
@@ -438,64 +426,76 @@ Resolver::resolve(const std::string& host, const std::string& service)
|
||||
|
||||
// Request
|
||||
|
||||
unsigned int Request::ids_ = 1;
|
||||
std::atomic_uint Request::ids_ {1};
|
||||
|
||||
|
||||
Request::Request(asio::io_context& ctx, const std::string& url, const Json::Value& json, OnJsonCb jsoncb,
|
||||
std::shared_ptr<dht::Logger> logger)
|
||||
: id_(Request::ids_++), ctx_(ctx),
|
||||
resolver_(std::make_shared<Resolver>(ctx, url, logger)), logger_(logger)
|
||||
: logger_(std::move(logger)), id_(Request::ids_++), ctx_(ctx),
|
||||
resolver_(std::make_shared<Resolver>(ctx, url, logger))
|
||||
{
|
||||
init_default_headers();
|
||||
set_header_field(restinio::http_field_t::content_type, "application/json");
|
||||
set_header_field(restinio::http_field_t::accept, "application/json");
|
||||
set_header_field(restinio::http_field_t::content_type, HTTP_HEADER_CONTENT_TYPE_JSON);
|
||||
set_header_field(restinio::http_field_t::accept, HTTP_HEADER_CONTENT_TYPE_JSON);
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
set_body(Json::writeString(wbuilder, json));
|
||||
add_on_state_change_callback([this, jsoncb](State state, const Response& response){
|
||||
if (state != Request::State::DONE)
|
||||
return;
|
||||
Json::Value json;
|
||||
std::string err;
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
|
||||
if (!reader->parse(response.body.data(), response.body.data() + response.body.size(), &json, &err) and logger_)
|
||||
logger_->e("[http:client] [request:%i] can't parse response to json", id_, err.c_str());
|
||||
if (response.status_code != 0) {
|
||||
std::string err;
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
|
||||
if (!reader->parse(response.body.data(), response.body.data() + response.body.size(), &json, &err) and logger_)
|
||||
logger_->e("[http:request:%i] can't parse response to json", id_, err.c_str());
|
||||
}
|
||||
if (jsoncb)
|
||||
jsoncb(json, response.status_code);
|
||||
});
|
||||
}
|
||||
|
||||
Request::Request(asio::io_context& ctx, const std::string& url, std::shared_ptr<dht::Logger> logger)
|
||||
: id_(Request::ids_++), ctx_(ctx),
|
||||
resolver_(std::make_shared<Resolver>(ctx, url, logger)), logger_(logger)
|
||||
: logger_(logger), id_(Request::ids_++), ctx_(ctx),
|
||||
resolver_(std::make_shared<Resolver>(ctx, url, logger))
|
||||
{
|
||||
init_default_headers();
|
||||
}
|
||||
|
||||
Request::Request(asio::io_context& ctx, const std::string& url, OnDoneCb onDone, std::shared_ptr<dht::Logger> logger)
|
||||
: logger_(logger), id_(Request::ids_++), ctx_(ctx), resolver_(std::make_shared<Resolver>(ctx, url, logger))
|
||||
{
|
||||
init_default_headers();
|
||||
add_on_state_change_callback([this, onDone](State state, const Response& response){
|
||||
if (state == Request::State::DONE)
|
||||
onDone(response);
|
||||
});
|
||||
}
|
||||
|
||||
Request::Request(asio::io_context& ctx, const std::string& host, const std::string& service,
|
||||
const bool ssl, std::shared_ptr<dht::Logger> logger)
|
||||
: id_(Request::ids_++), ctx_(ctx),
|
||||
resolver_(std::make_shared<Resolver>(ctx, host, service, ssl, logger)), logger_(logger)
|
||||
: logger_(logger), id_(Request::ids_++), ctx_(ctx),
|
||||
resolver_(std::make_shared<Resolver>(ctx, host, service, ssl, logger))
|
||||
{
|
||||
init_default_headers();
|
||||
}
|
||||
|
||||
Request::Request(asio::io_context& ctx, std::shared_ptr<Resolver> resolver, sa_family_t family)
|
||||
: id_(Request::ids_++), ctx_(ctx), family_(family), resolver_(resolver), logger_(resolver->getLogger())
|
||||
: logger_(resolver->getLogger()), id_(Request::ids_++), ctx_(ctx), family_(family), resolver_(resolver)
|
||||
{
|
||||
init_default_headers();
|
||||
}
|
||||
|
||||
Request::Request(asio::io_context& ctx, std::vector<asio::ip::tcp::endpoint>&& endpoints, const bool ssl,
|
||||
std::shared_ptr<dht::Logger> logger)
|
||||
: id_(Request::ids_++), ctx_(ctx),
|
||||
resolver_(std::make_shared<Resolver>(ctx, std::move(endpoints), ssl, logger)), logger_(logger)
|
||||
: logger_(logger), id_(Request::ids_++), ctx_(ctx),
|
||||
resolver_(std::make_shared<Resolver>(ctx, std::move(endpoints), ssl, logger))
|
||||
{
|
||||
init_default_headers();
|
||||
}
|
||||
|
||||
Request::Request(asio::io_context& ctx, std::shared_ptr<Resolver> resolver, const std::string& target, sa_family_t family)
|
||||
: id_(Request::ids_++), ctx_(ctx), family_(family), resolver_(resolver), logger_(resolver->getLogger())
|
||||
: logger_(resolver->getLogger()), id_(Request::ids_++), ctx_(ctx), family_(family), resolver_(resolver)
|
||||
{
|
||||
set_header_field(restinio::http_field_t::host, get_url().host + ":" + get_url().service);
|
||||
set_target(target);
|
||||
@@ -503,13 +503,16 @@ Request::Request(asio::io_context& ctx, std::shared_ptr<Resolver> resolver, cons
|
||||
|
||||
Request::~Request()
|
||||
{
|
||||
resolver_.reset();
|
||||
terminate(asio::error::connection_aborted);
|
||||
}
|
||||
|
||||
void
|
||||
Request::init_default_headers()
|
||||
{
|
||||
const auto& url = resolver_->get_url();
|
||||
set_header_field(restinio::http_field_t::host, url.host + ":" + url.service);
|
||||
set_header_field(restinio::http_field_t::user_agent, "Mozilla/5.0");
|
||||
set_header_field(restinio::http_field_t::accept, "text/html");
|
||||
set_target(url.target);
|
||||
}
|
||||
|
||||
@@ -607,7 +610,7 @@ void
|
||||
Request::build()
|
||||
{
|
||||
std::stringstream request;
|
||||
bool append_body = true;
|
||||
bool append_body = !body_.empty();
|
||||
|
||||
// first header
|
||||
request << header_.method().c_str() << " " << header_.request_target() << " " <<
|
||||
@@ -621,27 +624,28 @@ Request::build()
|
||||
}
|
||||
|
||||
// last connection header
|
||||
std::string conn_str = "";
|
||||
const char* conn_str = nullptr;
|
||||
switch (connection_type_){
|
||||
case restinio::http_connection_header_t::upgrade:
|
||||
throw std::invalid_argument("upgrade");
|
||||
break;
|
||||
case restinio::http_connection_header_t::keep_alive:
|
||||
conn_str = "keep-alive";
|
||||
break;
|
||||
case restinio::http_connection_header_t::upgrade:
|
||||
if (logger_)
|
||||
logger_->e("Unsupported connection type 'upgrade', fallback to 'close'");
|
||||
// fallthrough
|
||||
case restinio::http_connection_header_t::close:
|
||||
conn_str = "close";
|
||||
connection_type_ = restinio::http_connection_header_t::close;
|
||||
conn_str = "close"; // default
|
||||
break;
|
||||
}
|
||||
if (!conn_str.empty())
|
||||
if (conn_str)
|
||||
request << "Connection: " << conn_str << "\r\n";
|
||||
|
||||
// body & content-length
|
||||
if (!body_.empty())
|
||||
request << "Content-Length: " << body_.size() << "\r\n\r\n";
|
||||
// last delim
|
||||
if (append_body)
|
||||
request << body_ << "\r\n";
|
||||
if (append_body) {
|
||||
request << "Content-Length: " << body_.size() << "\r\n\r\n"
|
||||
<< body_;
|
||||
} else
|
||||
request << "\r\n";
|
||||
request_ = request.str();
|
||||
}
|
||||
|
||||
@@ -680,7 +684,7 @@ Request::init_parser()
|
||||
if (!parser_)
|
||||
parser_ = std::make_unique<http_parser>();
|
||||
http_parser_init(parser_.get(), HTTP_RESPONSE);
|
||||
parser_->data = static_cast<void*>(&cbs_);
|
||||
parser_->data = static_cast<void*>(this);
|
||||
|
||||
if (!parser_s_)
|
||||
parser_s_ = std::make_unique<http_parser_settings>();
|
||||
@@ -689,57 +693,63 @@ Request::init_parser()
|
||||
// user registered callbacks wrappers to store its data in the response
|
||||
std::lock_guard<std::mutex> lock(cbs_mutex_);
|
||||
cbs_.on_status = [this, statusCb = std::move(cbs_.on_status)](unsigned int status_code){
|
||||
std::cout << "on_status " << status_code << std::endl;
|
||||
response_.status_code = status_code;
|
||||
if (statusCb)
|
||||
statusCb(status_code);
|
||||
};
|
||||
auto header_field = std::make_shared<std::string>();
|
||||
cbs_.on_header_field = [header_field, headerFieldCb = std::move(cbs_.on_header_field)](const char* at, size_t length) {
|
||||
std::cout << "on_header_field " << std::string(at, length) << std::endl;
|
||||
*header_field = std::string(at, length);
|
||||
if (headerFieldCb)
|
||||
headerFieldCb(at, length);
|
||||
};
|
||||
cbs_.on_header_value = [this, header_field, headerValueCb = std::move(cbs_.on_header_value)](const char* at, size_t length) {
|
||||
std::cout << "on_header_value " << std::string(at, length) << std::endl;
|
||||
response_.headers[*header_field] = std::string(at, length);
|
||||
if (headerValueCb)
|
||||
headerValueCb(at, length);
|
||||
};
|
||||
cbs_.on_headers_complete = [this](){
|
||||
notify_state_change(State::HEADER_RECEIVED);
|
||||
std::cout << "on_headers_complete " << std::endl;
|
||||
onHeadersComplete();
|
||||
};
|
||||
cbs_.on_body = [bodyCb = std::move(cbs_.on_body)](const char* at, size_t length) {
|
||||
std::cout << "on_body " << length << std::endl;
|
||||
if (bodyCb)
|
||||
bodyCb(at, length);
|
||||
};
|
||||
cbs_.on_message_complete = [this](){
|
||||
std::cout << "on_message_complete " << std::endl;
|
||||
if (logger_)
|
||||
logger_->d("[http:client] [request:%i] response: message complete", id_);
|
||||
logger_->d("[http:request:%i] response: message complete", id_);
|
||||
message_complete_.store(true);
|
||||
};
|
||||
}
|
||||
// http_parser raw c callback (note: no context can be passed into them)
|
||||
parser_s_->on_status = [](http_parser* parser, const char* /*at*/, size_t /*length*/) -> int {
|
||||
static_cast<Callbacks*>(parser->data)->on_status(parser->status_code);
|
||||
static_cast<Request*>(parser->data)->cbs_.on_status(parser->status_code);
|
||||
return 0;
|
||||
};
|
||||
parser_s_->on_header_field = [](http_parser* parser, const char* at, size_t length) -> int {
|
||||
static_cast<Callbacks*>(parser->data)->on_header_field(at, length);
|
||||
static_cast<Request*>(parser->data)->cbs_.on_header_field(at, length);
|
||||
return 0;
|
||||
};
|
||||
parser_s_->on_header_value = [](http_parser* parser, const char* at, size_t length) -> int {
|
||||
static_cast<Callbacks*>(parser->data)->on_header_value(at, length);
|
||||
static_cast<Request*>(parser->data)->cbs_.on_header_value(at, length);
|
||||
return 0;
|
||||
};
|
||||
parser_s_->on_body = [](http_parser* parser, const char* at, size_t length) -> int {
|
||||
static_cast<Callbacks*>(parser->data)->on_body(at, length);
|
||||
static_cast<Request*>(parser->data)->cbs_.on_body(at, length);
|
||||
return 0;
|
||||
};
|
||||
parser_s_->on_headers_complete = [](http_parser* parser) -> int {
|
||||
static_cast<Callbacks*>(parser->data)->on_headers_complete();
|
||||
static_cast<Request*>(parser->data)->cbs_.on_headers_complete();
|
||||
return 0;
|
||||
};
|
||||
parser_s_->on_message_complete = [](http_parser* parser) -> int {
|
||||
static_cast<Callbacks*>(parser->data)->on_message_complete();
|
||||
static_cast<Request*>(parser->data)->cbs_.on_message_complete();
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
@@ -749,7 +759,7 @@ Request::connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, HandlerCb cb)
|
||||
{
|
||||
if (endpoints.empty()){
|
||||
if (logger_)
|
||||
logger_->e("[http:client] [request:%i] connect: no endpoints provided", id_);
|
||||
logger_->e("[http:request:%i] connect: no endpoints provided", id_);
|
||||
if (cb)
|
||||
cb(asio::error::connection_aborted);
|
||||
return;
|
||||
@@ -757,8 +767,8 @@ Request::connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, HandlerCb cb)
|
||||
if (logger_){
|
||||
std::string eps = "";
|
||||
for (const auto& endpoint : endpoints)
|
||||
eps.append(endpoint.address().to_string() + " ");
|
||||
logger_->d("[http:client] [request:%i] connect begin: %s", id_, eps.c_str());
|
||||
eps.append(endpoint.address().to_string() + ":" + std::to_string(endpoint.port()) + " ");
|
||||
logger_->d("[http:request:%i] connect begin: %s", id_, eps.c_str());
|
||||
}
|
||||
if (get_url().protocol == "https"){
|
||||
if (server_ca_)
|
||||
@@ -770,31 +780,44 @@ Request::connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, HandlerCb cb)
|
||||
conn_ = std::make_shared<Connection>(ctx_, false/*ssl*/, logger_);
|
||||
|
||||
// try to connect to any until one works
|
||||
conn_->async_connect(std::move(endpoints), [this, cb]
|
||||
std::weak_ptr<Request> wthis = shared_from_this();
|
||||
conn_->async_connect(std::move(endpoints), [wthis, cb]
|
||||
(const asio::error_code& ec, const asio::ip::tcp::endpoint& endpoint){
|
||||
auto sthis = wthis.lock();
|
||||
if (not sthis)
|
||||
return;
|
||||
auto& this_ = *sthis;
|
||||
if (ec == asio::error::operation_aborted){
|
||||
terminate(ec);
|
||||
this_.terminate(ec);
|
||||
return;
|
||||
}
|
||||
else if (ec and logger_)
|
||||
logger_->e("[http:client] [request:%i] connect: failed with all endpoints", id_);
|
||||
else if (ec and this_.logger_)
|
||||
this_.logger_->e("[http:request:%i] connect: failed with all endpoints", this_.id_);
|
||||
else {
|
||||
if (logger_)
|
||||
logger_->d("[http:client] [request:%i] connect success", id_);
|
||||
if (this_.logger_)
|
||||
this_.logger_->d("[http:request:%i] connect success", this_.id_);
|
||||
|
||||
if (get_url().protocol == "https"){
|
||||
if (server_ca_)
|
||||
conn_->set_ssl_verification(endpoint, asio::ssl::verify_peer
|
||||
const auto& url = this_.get_url();
|
||||
auto port = endpoint.port();
|
||||
if ((url.protocol == "http" && port == (in_port_t)80)
|
||||
|| (url.protocol == "https" && port == (in_port_t)443))
|
||||
this_.set_header_field(restinio::http_field_t::host, url.host);
|
||||
else
|
||||
this_.set_header_field(restinio::http_field_t::host, url.host + ":" + std::to_string(port));
|
||||
|
||||
if (url.protocol == "https") {
|
||||
if (this_.server_ca_)
|
||||
this_.conn_->set_ssl_verification(endpoint, asio::ssl::verify_peer
|
||||
| asio::ssl::verify_fail_if_no_peer_cert);
|
||||
|
||||
if (conn_ and conn_->is_open() and conn_->is_ssl()){
|
||||
conn_->async_handshake([this, cb](const asio::error_code& ec){
|
||||
if (this_.conn_ and this_.conn_->is_open() and this_.conn_->is_ssl()){
|
||||
this_.conn_->async_handshake([id = this_.id_, cb, logger = this_.logger_](const asio::error_code& ec){
|
||||
if (ec == asio::error::operation_aborted)
|
||||
return;
|
||||
if (ec and logger_)
|
||||
logger_->e("[http:client] [request:%i] handshake error: %s", id_, ec.message().c_str());
|
||||
else if (logger_)
|
||||
logger_->d("[http:client] [request:%i] handshake success", id_);
|
||||
if (ec and logger)
|
||||
logger->e("[http:request:%i] handshake error: %s", id, ec.message().c_str());
|
||||
else if (logger)
|
||||
logger->d("[http:request:%i] handshake success", id);
|
||||
if (cb)
|
||||
cb(ec);
|
||||
});
|
||||
@@ -814,23 +837,29 @@ Request::send()
|
||||
{
|
||||
notify_state_change(State::CREATED);
|
||||
|
||||
resolver_->add_callback([this](const asio::error_code& ec,
|
||||
std::vector<asio::ip::tcp::endpoint> endpoints){
|
||||
if (ec){
|
||||
if (logger_)
|
||||
logger_->e("[http:client] [request:%i] resolve error: %s", id_, ec.message().c_str());
|
||||
terminate(asio::error::connection_aborted);
|
||||
std::weak_ptr<Request> wthis = shared_from_this();
|
||||
resolver_->add_callback([wthis](const asio::error_code& ec,
|
||||
std::vector<asio::ip::tcp::endpoint> endpoints) {
|
||||
if (auto sthis = wthis.lock()) {
|
||||
auto& this_ = *sthis;
|
||||
if (ec){
|
||||
if (this_.logger_)
|
||||
this_.logger_->e("[http:request:%i] resolve error: %s", this_.id_, ec.message().c_str());
|
||||
this_.terminate(asio::error::connection_aborted);
|
||||
}
|
||||
else if (!this_.conn_ or !this_.conn_->is_open()) {
|
||||
this_.connect(std::move(endpoints), [wthis](const asio::error_code &ec) {
|
||||
if (auto sthis = wthis.lock()) {
|
||||
if (ec)
|
||||
sthis->terminate(asio::error::not_connected);
|
||||
else
|
||||
sthis->post();
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
this_.post();
|
||||
}
|
||||
else if (!conn_ or !conn_->is_open()){
|
||||
connect(std::move(endpoints), [this](const asio::error_code &ec){
|
||||
if (ec)
|
||||
terminate(asio::error::not_connected);
|
||||
else
|
||||
post();
|
||||
});
|
||||
}
|
||||
else
|
||||
post();
|
||||
}, family_);
|
||||
}
|
||||
|
||||
@@ -844,17 +873,21 @@ Request::post()
|
||||
build();
|
||||
init_parser();
|
||||
|
||||
if (logger_){
|
||||
std::string header; std::getline(std::istringstream(request_), header);
|
||||
logger_->d("[http:client] [request:%i] send: %s", id_, header.c_str());
|
||||
}
|
||||
if (logger_)
|
||||
logger_->d("[http:request:%i] send:\n%s", id_, request_.c_str());
|
||||
|
||||
// write the request to buffer
|
||||
std::ostream request_stream(&conn_->input());
|
||||
request_stream << request_;
|
||||
|
||||
// send the request
|
||||
notify_state_change(State::SENDING);
|
||||
conn_->async_write(std::bind(&Request::handle_request, this, std::placeholders::_1));
|
||||
|
||||
std::weak_ptr<Request> wthis = shared_from_this();
|
||||
conn_->async_write([wthis](const asio::error_code& ec, size_t) {
|
||||
if (auto sthis = wthis.lock())
|
||||
sthis->handle_request(ec);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
@@ -864,16 +897,21 @@ Request::terminate(const asio::error_code& ec)
|
||||
return;
|
||||
|
||||
if (ec != asio::error::eof and ec != asio::error::operation_aborted and logger_)
|
||||
logger_->e("[http:client] [request:%i] end with error: %s", id_, ec.message().c_str());
|
||||
logger_->e("[http:request:%i] end with error: %s", id_, ec.message().c_str());
|
||||
|
||||
// set response outcome, ignore end of file and abort
|
||||
if (!ec or ec == asio::error::eof or ec == asio::error::operation_aborted)
|
||||
response_.status_code = 200;
|
||||
if ((!ec or ec == asio::error::eof) and parser_)
|
||||
response_.status_code = parser_->status_code;
|
||||
else
|
||||
response_.status_code = 0;
|
||||
|
||||
response_.aborted = ec == asio::error::operation_aborted;
|
||||
|
||||
if (logger_)
|
||||
logger_->d("[http:client] [request:%i] done", id_);
|
||||
logger_->d("[http:request:%i] done with status code %u", id_, response_.status_code);
|
||||
if (connection_type_ != restinio::http_connection_header_t::keep_alive)
|
||||
if (auto c = conn_)
|
||||
c->close();
|
||||
notify_state_change(State::DONE);
|
||||
}
|
||||
|
||||
@@ -889,15 +927,19 @@ Request::handle_request(const asio::error_code& ec)
|
||||
return;
|
||||
}
|
||||
if (logger_)
|
||||
logger_->d("[http:client] [request:%i] send success", id_);
|
||||
logger_->d("[http:request:%i] send success", id_);
|
||||
// read response
|
||||
notify_state_change(State::RECEIVING);
|
||||
conn_->async_read_until(HTTP_HEADER_DELIM, std::bind(&Request::handle_response_header,
|
||||
this, std::placeholders::_1));
|
||||
|
||||
std::weak_ptr<Request> wthis = shared_from_this();
|
||||
conn_->async_read_until(HTTP_HEADER_DELIM, [wthis](const asio::error_code& ec, size_t n_bytes){
|
||||
if (auto sthis = wthis.lock())
|
||||
sthis->handle_response_header(ec, n_bytes);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
Request::handle_response_header(const asio::error_code& ec)
|
||||
Request::handle_response_header(const asio::error_code& ec, size_t n_bytes)
|
||||
{
|
||||
if (ec && ec != asio::error::eof){
|
||||
terminate(ec);
|
||||
@@ -907,36 +949,90 @@ Request::handle_response_header(const asio::error_code& ec)
|
||||
terminate(asio::error::not_connected);
|
||||
return;
|
||||
}
|
||||
const auto& dat = conn_->read_bytes(n_bytes);
|
||||
if (logger_)
|
||||
logger_->d("[http:client] [request:%i] response headers received", id_);
|
||||
logger_->d("[http:request:%i] read %zu bytes:\n\"%.*s\"", id_, n_bytes, dat.size(), dat.data());
|
||||
|
||||
std::cout << "\n " << std::hex
|
||||
<< std::setfill('0') << std::setw(2) << (unsigned)dat[dat.size() - 4]
|
||||
<< std::setfill('0') << std::setw(2) << (unsigned)dat[dat.size() - 3]
|
||||
<< std::setfill('0') << std::setw(2) << (unsigned)dat[dat.size() - 2]
|
||||
<< std::setfill('0') << std::setw(2) << (unsigned)dat[dat.size() - 1]
|
||||
<< std::endl;
|
||||
|
||||
// read the header
|
||||
std::string header;
|
||||
/*std::string header;
|
||||
std::string headers;
|
||||
std::istream is(&conn_->data());
|
||||
while (std::getline(is, header) && header != "\r"){
|
||||
while (std::getline(conn_->data(), header) && header != "\r"){
|
||||
headers.append(header + "\n");
|
||||
}
|
||||
headers.append("\n");
|
||||
parse_request(headers);
|
||||
parse_request(headers);*/
|
||||
/*auto& dat = conn_->data();
|
||||
std::vector<uint8_t> indat;*/
|
||||
auto ret = http_parser_execute(parser_.get(), parser_s_.get(), dat.data(), dat.size());
|
||||
if (ret != dat.size()) {
|
||||
if (logger_)
|
||||
logger_->e("Error parsing HTTP: %zu %s %s", ret,
|
||||
http_errno_name(HTTP_PARSER_ERRNO(parser_)),
|
||||
http_errno_description(HTTP_PARSER_ERRNO(parser_)));
|
||||
terminate(asio::error::invalid_argument);
|
||||
}
|
||||
logger_->d("[http:request:%i] read END", id_);
|
||||
}
|
||||
|
||||
if (headers_[restinio::http_field_t::expect] == "100-continue" and response_.status_code != 200){
|
||||
void
|
||||
Request::onHeadersComplete() {
|
||||
notify_state_change(State::HEADER_RECEIVED);
|
||||
|
||||
std::weak_ptr<Request> wthis = shared_from_this();
|
||||
|
||||
auto expect_it = headers_.find(restinio::http_field_t::expect);
|
||||
if (expect_it != headers_.end() and (expect_it->second == "100-continue") and response_.status_code != 200){
|
||||
notify_state_change(State::SENDING);
|
||||
request_.append(body_);
|
||||
std::ostream request_stream(&conn_->input());
|
||||
request_stream << body_ << "\r\n";
|
||||
conn_->async_write(std::bind(&Request::handle_request, this, std::placeholders::_1));
|
||||
conn_->async_write([wthis](const asio::error_code& ec, size_t) {
|
||||
if (auto sthis = wthis.lock())
|
||||
sthis->handle_request(ec);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// avoid creating non-existant headers by accessing the headers map without the presence of key
|
||||
auto connection_it = response_.headers.find(HTTP_HEADER_CONNECTION);
|
||||
auto content_length_it = response_.headers.find(HTTP_HEADER_CONTENT_LENGTH);
|
||||
auto transfer_encoding_it = response_.headers.find(HTTP_HEADER_TRANSFER_ENCODING);
|
||||
|
||||
// has content-length
|
||||
if (content_length_it != response_.headers.end())
|
||||
{
|
||||
unsigned int content_length = atoi(content_length_it->second.c_str());
|
||||
if (content_length_it != response_.headers.end()) {
|
||||
size_t content_length = atoi(content_length_it->second.c_str());
|
||||
auto dat = conn_->read_bytes(content_length);
|
||||
/*if (logger_)
|
||||
logger_->d("[http:request:%i] response body: %zu/%zu bytes received:\n\"%.*s\"", id_, dat.size(), content_length, dat.size(), dat.data());
|
||||
if (not dat.empty()) {
|
||||
if (dat.size() == content_length)
|
||||
dat += HTTP_HEADER_DELIM;
|
||||
http_parser_execute(parser_.get(), parser_s_.get(), dat.data(), dat.size());
|
||||
}*/
|
||||
if (dat.size() == content_length)
|
||||
ctx_.post([wthis, dat=std::move(dat)]{
|
||||
if (auto sthis = wthis.lock()) {
|
||||
http_parser_execute(sthis->parser_.get(), sthis->parser_s_.get(), dat.data(), dat.size());
|
||||
}
|
||||
});
|
||||
else
|
||||
conn_->async_read(content_length, [wthis](const asio::error_code& ec, size_t bytes){
|
||||
if (auto sthis = wthis.lock())
|
||||
sthis->handle_response_header(ec, bytes);
|
||||
});
|
||||
/*auto r = conn_->read_bytes(content_length);
|
||||
std::cout << "Has content length " << content_length << std::endl;
|
||||
std::cout << "Read content " << r.size() << std::endl;
|
||||
if (r.size() == content_length) {
|
||||
parse_request(r);
|
||||
}*/
|
||||
/*
|
||||
response_.body.append(conn_->read_bytes(content_length));
|
||||
// full body already in the header
|
||||
if (response_.body.size() + 1 == content_length) {
|
||||
@@ -946,15 +1042,19 @@ Request::handle_response_header(const asio::error_code& ec)
|
||||
terminate(asio::error::eof);
|
||||
}
|
||||
else { // more chunks to come (don't add the missing \n from std::getline)
|
||||
conn_->async_read(content_length - response_.body.size(),
|
||||
std::bind(&Request::handle_response_body, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
conn_->async_read(content_length - response_.body.size(), [wthis](const asio::error_code& ec, size_t bytes){
|
||||
if (auto sthis = wthis.lock())
|
||||
sthis->handle_response_header(ec, bytes);
|
||||
});
|
||||
}*/
|
||||
}
|
||||
// server wants to keep sending or we have content-length defined
|
||||
else if (connection_it != response_.headers.end() and connection_it->second == HTTP_HEADER_CONNECTION_KEEP_ALIVE)
|
||||
{
|
||||
conn_->async_read_until(BODY_VALUE_DELIM,
|
||||
std::bind(&Request::handle_response_body, this, std::placeholders::_1, std::placeholders::_2));
|
||||
conn_->async_read_until(BODY_VALUE_DELIM, [wthis](const asio::error_code& ec, size_t bytes){
|
||||
if (auto sthis = wthis.lock())
|
||||
sthis->handle_response_header(ec, bytes);
|
||||
});
|
||||
}
|
||||
// server wants to close the connection
|
||||
else if (connection_it != response_.headers.end() and connection_it->second == HTTP_HEADER_CONNECTION_CLOSE)
|
||||
@@ -970,15 +1070,17 @@ Request::handle_response_header(const asio::error_code& ec)
|
||||
transfer_encoding_it->second == HTTP_HEADER_TRANSFER_ENCODING_CHUNKED)
|
||||
{
|
||||
std::string chunk_size;
|
||||
std::getline(is, chunk_size);
|
||||
std::getline(conn_->data(), chunk_size);
|
||||
unsigned int content_length = std::stoul(chunk_size, nullptr, 16);
|
||||
conn_->async_read(content_length,
|
||||
std::bind(&Request::handle_response_body, this, std::placeholders::_1, std::placeholders::_2));
|
||||
conn_->async_read(content_length, [wthis](const asio::error_code& ec, size_t bytes){
|
||||
if (auto sthis = wthis.lock())
|
||||
sthis->handle_response_header(ec, bytes);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Request::handle_response_body(const asio::error_code& ec, const size_t bytes)
|
||||
Request::handle_response_body(const asio::error_code& ec, size_t bytes)
|
||||
{
|
||||
if (ec && ec != asio::error::eof){
|
||||
terminate(ec);
|
||||
@@ -989,7 +1091,7 @@ Request::handle_response_body(const asio::error_code& ec, const size_t bytes)
|
||||
return;
|
||||
}
|
||||
if (logger_)
|
||||
logger_->d("[http:client] [request:%i] response body: %i bytes received", id_, bytes);
|
||||
logger_->d("[http:request:%i] response body: %i bytes received", id_, bytes);
|
||||
|
||||
if (bytes == 0){
|
||||
terminate(asio::error::eof);
|
||||
@@ -1013,7 +1115,7 @@ Request::handle_response_body(const asio::error_code& ec, const size_t bytes)
|
||||
}
|
||||
// read and parse the chunked encoding fragment
|
||||
else {
|
||||
auto body = conn_->read_until(BODY_VALUE_DELIM[0]);
|
||||
auto body = conn_->read_until(BODY_VALUE_DELIM);
|
||||
response_.body += body;
|
||||
if (body == "0\r\n"){
|
||||
parse_request(response_.body);
|
||||
@@ -1023,6 +1125,8 @@ Request::handle_response_body(const asio::error_code& ec, const size_t bytes)
|
||||
parse_request(body + '\n');
|
||||
}
|
||||
|
||||
std::weak_ptr<Request> wthis = shared_from_this();
|
||||
|
||||
// should be executed after each parse_request who can trigger http_parser on_message_complete
|
||||
if (message_complete_.load()){
|
||||
terminate(asio::error::eof);
|
||||
@@ -1030,14 +1134,18 @@ Request::handle_response_body(const asio::error_code& ec, const size_t bytes)
|
||||
// has content-length
|
||||
else if (content_length_it != response_.headers.end() and response_.body.size() != content_length)
|
||||
{
|
||||
conn_->async_read(content_length - response_.body.size(),
|
||||
std::bind(&Request::handle_response_body, this, std::placeholders::_1, std::placeholders::_2));
|
||||
conn_->async_read(content_length - response_.body.size(), [wthis](const asio::error_code& ec, size_t bytes){
|
||||
if (auto sthis = wthis.lock())
|
||||
sthis->handle_response_body(ec, bytes);
|
||||
});
|
||||
}
|
||||
// server wants to keep sending
|
||||
else if (connection_it != response_.headers.end() and connection_it->second == HTTP_HEADER_CONNECTION_KEEP_ALIVE)
|
||||
{
|
||||
conn_->async_read_until(BODY_VALUE_DELIM,
|
||||
std::bind(&Request::handle_response_body, this, std::placeholders::_1, std::placeholders::_2));
|
||||
conn_->async_read_until(BODY_VALUE_DELIM, [wthis](const asio::error_code& ec, size_t bytes){
|
||||
if (auto sthis = wthis.lock())
|
||||
sthis->handle_response_body(ec, bytes);
|
||||
});
|
||||
}
|
||||
// server wants to close the connection
|
||||
else if (connection_it != response_.headers.end() and connection_it->second == HTTP_HEADER_CONNECTION_CLOSE)
|
||||
@@ -1052,16 +1160,17 @@ Request::handle_response_body(const asio::error_code& ec, const size_t bytes)
|
||||
else if (transfer_encoding_it != response_.headers.end() and
|
||||
transfer_encoding_it->second == HTTP_HEADER_TRANSFER_ENCODING_CHUNKED)
|
||||
{
|
||||
std::istream is(&conn_->data());
|
||||
std::string chunk_size;
|
||||
std::getline(is, chunk_size);
|
||||
std::getline(conn_->data(), chunk_size);
|
||||
if (chunk_size.size() == 0){
|
||||
parse_request(response_.body);
|
||||
terminate(asio::error::eof);
|
||||
}
|
||||
else
|
||||
conn_->async_read_until(BODY_VALUE_DELIM,
|
||||
std::bind(&Request::handle_response_body, this, std::placeholders::_1, std::placeholders::_2));
|
||||
conn_->async_read_until(BODY_VALUE_DELIM, [wthis](const asio::error_code& ec, size_t bytes){
|
||||
if (auto sthis = wthis.lock())
|
||||
sthis->handle_response_body(ec, bytes);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+57
-51
@@ -41,7 +41,6 @@ constexpr std::chrono::seconds NetworkEngine::RX_MAX_PACKET_TIME;
|
||||
constexpr std::chrono::seconds NetworkEngine::RX_TIMEOUT;
|
||||
|
||||
const std::string NetworkEngine::my_v {"RNG1"};
|
||||
constexpr size_t NetworkEngine::MAX_REQUESTS_PER_SEC;
|
||||
|
||||
static constexpr uint8_t v4prefix[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0};
|
||||
|
||||
@@ -100,10 +99,10 @@ RequestAnswer::RequestAnswer(ParsedMessage&& msg)
|
||||
{}
|
||||
|
||||
NetworkEngine::NetworkEngine(Logger& log, Scheduler& scheduler, std::unique_ptr<DatagramSocket>&& sock)
|
||||
: myid(zeroes), dht_socket(std::move(sock)), DHT_LOG(log), scheduler(scheduler)
|
||||
: myid(zeroes), dht_socket(std::move(sock)), DHT_LOG(log), rate_limiter((size_t)-1), scheduler(scheduler)
|
||||
{}
|
||||
|
||||
NetworkEngine::NetworkEngine(InfoHash& myid, NetId net, std::unique_ptr<DatagramSocket>&& sock, Logger& log, Scheduler& scheduler,
|
||||
NetworkEngine::NetworkEngine(InfoHash& myid, NetworkConfig c, std::unique_ptr<DatagramSocket>&& sock, Logger& log, Scheduler& scheduler,
|
||||
decltype(NetworkEngine::onError)&& onError,
|
||||
decltype(NetworkEngine::onNewNode)&& onNewNode,
|
||||
decltype(NetworkEngine::onReportedAddr)&& onReportedAddr,
|
||||
@@ -122,7 +121,9 @@ NetworkEngine::NetworkEngine(InfoHash& myid, NetId net, std::unique_ptr<Datagram
|
||||
onListen(std::move(onListen)),
|
||||
onAnnounce(std::move(onAnnounce)),
|
||||
onRefresh(std::move(onRefresh)),
|
||||
myid(myid), network(net), dht_socket(std::move(sock)), DHT_LOG(log), scheduler(scheduler)
|
||||
myid(myid), config(c), dht_socket(std::move(sock)), DHT_LOG(log),
|
||||
rate_limiter(config.max_req_per_sec),
|
||||
scheduler(scheduler)
|
||||
{}
|
||||
|
||||
NetworkEngine::~NetworkEngine() {
|
||||
@@ -148,7 +149,7 @@ NetworkEngine::tellListenerRefreshed(Sp<Node> n, Tid socket_id, const InfoHash&,
|
||||
{
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(4+(network?1:0));
|
||||
pk.pack_map(4+(config.network?1:0));
|
||||
|
||||
pk.pack(KEY_U);
|
||||
pk.pack_map(1 + (not values.empty()?1:0) + (not token.empty()?1:0));
|
||||
@@ -165,8 +166,8 @@ NetworkEngine::tellListenerRefreshed(Sp<Node> n, Tid socket_id, const InfoHash&,
|
||||
pk.pack(KEY_TID); pk.pack(socket_id);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_R);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
|
||||
// send response
|
||||
@@ -178,7 +179,7 @@ NetworkEngine::tellListenerExpired(Sp<Node> n, Tid socket_id, const InfoHash&, c
|
||||
{
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(4+(network?1:0));
|
||||
pk.pack_map(4+(config.network?1:0));
|
||||
|
||||
pk.pack(KEY_U);
|
||||
pk.pack_map(1 + (not values.empty()?1:0) + (not token.empty()?1:0));
|
||||
@@ -195,8 +196,8 @@ NetworkEngine::tellListenerExpired(Sp<Node> n, Tid socket_id, const InfoHash&, c
|
||||
pk.pack(KEY_TID); pk.pack(socket_id);
|
||||
pk.pack(KEY_Y); pk.pack(KEY_R);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
|
||||
// send response
|
||||
@@ -245,7 +246,7 @@ NetworkEngine::requestStep(Sp<Request> sreq)
|
||||
auto now = scheduler.time();
|
||||
auto& node = *req.node;
|
||||
if (req.isExpired(now)) {
|
||||
DHT_LOG.d(node.id, "[node %s] expired !", node.toString().c_str());
|
||||
// DHT_LOG.d(node.id, "[node %s] expired !", node.toString().c_str());
|
||||
node.setExpired();
|
||||
if (not node.id)
|
||||
requests.erase(req.tid);
|
||||
@@ -300,7 +301,7 @@ NetworkEngine::rateLimit(const SockAddr& addr)
|
||||
const auto& now = scheduler.time();
|
||||
|
||||
// occasional IP limiter maintenance (a few times every second at max rate)
|
||||
if (limiter_maintenance++ == MAX_REQUESTS_PER_SEC/8) {
|
||||
if (limiter_maintenance++ == config.max_peer_req_per_sec) {
|
||||
for (auto it = address_rate_limiter.begin(); it != address_rate_limiter.end();) {
|
||||
if (it->second.maintain(now) == 0)
|
||||
address_rate_limiter.erase(it++);
|
||||
@@ -311,7 +312,11 @@ NetworkEngine::rateLimit(const SockAddr& addr)
|
||||
}
|
||||
|
||||
// invoke per IP, then global rate limiter
|
||||
return address_rate_limiter[addr].limit(now) and rate_limiter.limit(now);
|
||||
return (config.max_peer_req_per_sec < 0
|
||||
or address_rate_limiter
|
||||
.emplace(addr, config.max_peer_req_per_sec).first->second
|
||||
.limit(now))
|
||||
and rate_limiter.limit(now);
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -380,8 +385,8 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, SockAddr f)
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg->network != network) {
|
||||
DHT_LOG.d("Received message from other network %u", msg->network);
|
||||
if (msg->network != config.network) {
|
||||
DHT_LOG.d("Received message from other config.network %u", msg->network);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -476,7 +481,8 @@ NetworkEngine::process(std::unique_ptr<ParsedMessage>&& msg, const SockAddr& fro
|
||||
node->received(now, req);
|
||||
if (not node->isClient())
|
||||
onNewNode(node, 1);
|
||||
throw DhtProtocolException {DhtProtocolException::UNKNOWN_TID, "Can't find transaction", msg->id};
|
||||
DHT_LOG.d(node->id, "[node %s] can't find transaction with id %u", node->toString().c_str(), msg->tid);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -619,7 +625,7 @@ NetworkEngine::sendPing(Sp<Node> node, RequestCb&& on_done, RequestExpiredCb&& o
|
||||
TransId tid (node->getNewTid());
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(5+(network?1:0));
|
||||
pk.pack_map(5+(config.network?1:0));
|
||||
|
||||
pk.pack(KEY_A); pk.pack_map(1);
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
@@ -629,8 +635,8 @@ NetworkEngine::sendPing(Sp<Node> node, RequestCb&& on_done, RequestExpiredCb&& o
|
||||
pk.pack_bin_body((const char*)tid.data(), tid.size());
|
||||
pk.pack(KEY_Y); pk.pack(KEY_Q);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
|
||||
auto req = std::make_shared<Request>(MessageType::Ping, tid.toInt(), node,
|
||||
@@ -656,7 +662,7 @@ void
|
||||
NetworkEngine::sendPong(const SockAddr& addr, Tid tid) {
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(4+(network?1:0));
|
||||
pk.pack_map(4+(config.network?1:0));
|
||||
|
||||
pk.pack(KEY_R); pk.pack_map(2);
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
@@ -667,8 +673,8 @@ NetworkEngine::sendPong(const SockAddr& addr, Tid tid) {
|
||||
pk.pack_bin_body((const char*)t.data(), t.size());
|
||||
pk.pack(KEY_Y); pk.pack(KEY_R);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
|
||||
send(addr, buffer.data(), buffer.size());
|
||||
@@ -680,7 +686,7 @@ NetworkEngine::sendFindNode(Sp<Node> n, const InfoHash& target, want_t want,
|
||||
TransId tid (n->getNewTid());
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(5+(network?1:0));
|
||||
pk.pack_map(5+(config.network?1:0));
|
||||
|
||||
pk.pack(KEY_A); pk.pack_map(2 + (want>0?1:0));
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
@@ -697,8 +703,8 @@ NetworkEngine::sendFindNode(Sp<Node> n, const InfoHash& target, want_t want,
|
||||
pk.pack_bin_body((const char*)tid.data(), tid.size());
|
||||
pk.pack(KEY_Y); pk.pack(KEY_Q);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
|
||||
auto req = std::make_shared<Request>(MessageType::FindNode, tid.toInt(), n,
|
||||
@@ -726,7 +732,7 @@ NetworkEngine::sendGetValues(Sp<Node> n, const InfoHash& info_hash, const Query&
|
||||
TransId tid (n->getNewTid());
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(5+(network?1:0));
|
||||
pk.pack_map(5+(config.network?1:0));
|
||||
|
||||
pk.pack(KEY_A); pk.pack_map(2 +
|
||||
(query.where.getFilter() or not query.select.getSelection().empty() ? 1:0) +
|
||||
@@ -746,8 +752,8 @@ NetworkEngine::sendGetValues(Sp<Node> n, const InfoHash& info_hash, const Query&
|
||||
pk.pack_bin_body((const char*)tid.data(), tid.size());
|
||||
pk.pack(KEY_Y); pk.pack(KEY_Q);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
|
||||
auto req = std::make_shared<Request>(MessageType::GetValues, tid.toInt(), n,
|
||||
@@ -861,9 +867,9 @@ NetworkEngine::sendValueParts(const TransId& tid, const std::vector<Blob>& svals
|
||||
end = std::min(start + MTU, v.size());
|
||||
buffer.clear();
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(3+(network?1:0));
|
||||
if (network) {
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
pk.pack_map(3+(config.network?1:0));
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
pk.pack(KEY_Y); pk.pack(KEY_V);
|
||||
pk.pack(KEY_TID); pk.pack_bin(tid.size());
|
||||
@@ -886,7 +892,7 @@ NetworkEngine::sendNodesValues(const SockAddr& addr, Tid tid, const Blob& nodes,
|
||||
{
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(4+(network?1:0));
|
||||
pk.pack_map(4+(config.network?1:0));
|
||||
|
||||
pk.pack(KEY_R);
|
||||
pk.pack_map(2 + (not st.empty()?1:0) + (nodes.size()>0?1:0) + (nodes6.size()>0?1:0) + (not token.empty()?1:0));
|
||||
@@ -927,8 +933,8 @@ NetworkEngine::sendNodesValues(const SockAddr& addr, Tid tid, const Blob& nodes,
|
||||
pk.pack_bin_body((const char*)t.data(), t.size());
|
||||
pk.pack(KEY_Y); pk.pack(KEY_R);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
|
||||
// send response
|
||||
@@ -1017,7 +1023,7 @@ NetworkEngine::sendListen(Sp<Node> n,
|
||||
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(5+(network?1:0));
|
||||
pk.pack_map(5+(config.network?1:0));
|
||||
|
||||
auto has_query = query.where.getFilter() or not query.select.getSelection().empty();
|
||||
pk.pack(KEY_A); pk.pack_map(4 + has_query);
|
||||
@@ -1035,8 +1041,8 @@ NetworkEngine::sendListen(Sp<Node> n,
|
||||
pk.pack_bin_body((const char*)tid.data(), tid.size());
|
||||
pk.pack(KEY_Y); pk.pack(KEY_Q);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
|
||||
auto req = std::make_shared<Request>(MessageType::Listen, tid.toInt(), n,
|
||||
@@ -1060,7 +1066,7 @@ void
|
||||
NetworkEngine::sendListenConfirmation(const SockAddr& addr, Tid tid) {
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(4+(network?1:0));
|
||||
pk.pack_map(4+(config.network?1:0));
|
||||
|
||||
pk.pack(KEY_R); pk.pack_map(2);
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
@@ -1071,8 +1077,8 @@ NetworkEngine::sendListenConfirmation(const SockAddr& addr, Tid tid) {
|
||||
pk.pack_bin_body((const char*)t.data(), t.size());
|
||||
pk.pack(KEY_Y); pk.pack(KEY_R);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
|
||||
send(addr, buffer.data(), buffer.size());
|
||||
@@ -1090,7 +1096,7 @@ NetworkEngine::sendAnnounceValue(Sp<Node> n,
|
||||
TransId tid (n->getNewTid());
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(5+(network?1:0));
|
||||
pk.pack_map(5+(config.network?1:0));
|
||||
|
||||
pk.pack(KEY_A); pk.pack_map((created < scheduler.time() ? 5 : 4));
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
@@ -1107,8 +1113,8 @@ NetworkEngine::sendAnnounceValue(Sp<Node> n,
|
||||
pk.pack_bin_body((const char*)tid.data(), tid.size());
|
||||
pk.pack(KEY_Y); pk.pack(KEY_Q);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
|
||||
auto req = std::make_shared<Request>(MessageType::AnnounceValue, tid.toInt(), n,
|
||||
@@ -1148,7 +1154,7 @@ NetworkEngine::sendRefreshValue(Sp<Node> n,
|
||||
TransId tid (n->getNewTid());
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(5+(network?1:0));
|
||||
pk.pack_map(5+(config.network?1:0));
|
||||
|
||||
pk.pack(KEY_A); pk.pack_map(4);
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
@@ -1161,8 +1167,8 @@ NetworkEngine::sendRefreshValue(Sp<Node> n,
|
||||
pk.pack_bin_body((const char*)tid.data(), tid.size());
|
||||
pk.pack(KEY_Y); pk.pack(KEY_Q);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
|
||||
auto req = std::make_shared<Request>(MessageType::Refresh, tid.toInt(), n,
|
||||
@@ -1193,7 +1199,7 @@ void
|
||||
NetworkEngine::sendValueAnnounced(const SockAddr& addr, Tid tid, Value::Id vid) {
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
pk.pack_map(4+(network?1:0));
|
||||
pk.pack_map(4+(config.network?1:0));
|
||||
|
||||
pk.pack(KEY_R); pk.pack_map(3);
|
||||
pk.pack(KEY_REQ_ID); pk.pack(myid);
|
||||
@@ -1205,8 +1211,8 @@ NetworkEngine::sendValueAnnounced(const SockAddr& addr, Tid tid, Value::Id vid)
|
||||
pk.pack_bin_body((const char*)t.data(), t.size());
|
||||
pk.pack(KEY_Y); pk.pack(KEY_R);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
|
||||
send(addr, buffer.data(), buffer.size());
|
||||
@@ -1237,8 +1243,8 @@ NetworkEngine::sendError(const SockAddr& addr,
|
||||
pk.pack_bin_body((const char*)t.data(), t.size());
|
||||
pk.pack(KEY_Y); pk.pack(KEY_E);
|
||||
pk.pack(KEY_UA); pk.pack(my_v);
|
||||
if (network) {
|
||||
pk.pack(KEY_NETID); pk.pack(network);
|
||||
if (config.network) {
|
||||
pk.pack(KEY_NETID); pk.pack(config.network);
|
||||
}
|
||||
|
||||
send(addr, buffer.data(), buffer.size());
|
||||
|
||||
+33
-25
@@ -26,6 +26,7 @@
|
||||
#define close(x) closesocket(x)
|
||||
#define write(s, b, f) send(s, b, (int)strlen(b), 0)
|
||||
#else
|
||||
#include <sys/select.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
@@ -112,11 +113,13 @@ UdpSocket::UdpSocket(in_port_t port, const Logger& l) : logger(l) {
|
||||
SockAddr bind6;
|
||||
bind6.setFamily(AF_INET6);
|
||||
bind6.setPort(port);
|
||||
std::lock_guard<std::mutex> lk(lock);
|
||||
openSockets(bind4, bind6);
|
||||
}
|
||||
|
||||
UdpSocket::UdpSocket(const SockAddr& bind4, const SockAddr& bind6, const Logger& l) : logger(l)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(lock);
|
||||
openSockets(bind4, bind6);
|
||||
}
|
||||
|
||||
@@ -154,6 +157,7 @@ UdpSocket::sendTo(const SockAddr& dest, const uint8_t* data, size_t size, bool r
|
||||
int err = errno;
|
||||
logger.d("Can't send message to %s: %s", dest.toString().c_str(), strerror(err));
|
||||
if (err == EPIPE || err == ENOTCONN || err == ECONNRESET) {
|
||||
std::lock_guard<std::mutex> lk(lock);
|
||||
auto bind4 = std::move(bound4), bind6 = std::move(bound6);
|
||||
openSockets(bind4, bind6);
|
||||
return sendTo(dest, data, size, false);
|
||||
@@ -180,8 +184,8 @@ UdpSocket::openSockets(const SockAddr& bind4, const SockAddr& bind6)
|
||||
udpPipe(stopfds);
|
||||
#endif
|
||||
int stop_readfd = stopfds[0];
|
||||
stopfd = stopfds[1];
|
||||
|
||||
stopfd = stopfds[1];
|
||||
s4 = -1;
|
||||
s6 = -1;
|
||||
|
||||
@@ -224,18 +228,18 @@ UdpSocket::openSockets(const SockAddr& bind4, const SockAddr& bind6)
|
||||
}
|
||||
|
||||
running = true;
|
||||
rcv_thread = std::thread([this, stop_readfd]() {
|
||||
int selectFd = std::max({s4, s6, stop_readfd}) + 1;
|
||||
rcv_thread = std::thread([this, stop_readfd, ls4=s4, ls6=s6]() mutable {
|
||||
int selectFd = std::max({ls4, ls6, stop_readfd}) + 1;
|
||||
try {
|
||||
while (running) {
|
||||
fd_set readfds;
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(stop_readfd, &readfds);
|
||||
if(s4 >= 0)
|
||||
FD_SET(s4, &readfds);
|
||||
if(s6 >= 0)
|
||||
FD_SET(s6, &readfds);
|
||||
if(ls4 >= 0)
|
||||
FD_SET(ls4, &readfds);
|
||||
if(ls6 >= 0)
|
||||
FD_SET(ls6, &readfds);
|
||||
|
||||
int rc = select(selectFd, &readfds, nullptr, nullptr, nullptr);
|
||||
if (rc < 0) {
|
||||
@@ -259,10 +263,10 @@ UdpSocket::openSockets(const SockAddr& bind4, const SockAddr& bind6)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (s4 >= 0 && FD_ISSET(s4, &readfds))
|
||||
rc = recvfrom(s4, (char*)buf.data(), buf.size(), 0, (sockaddr*)&from, &from_len);
|
||||
else if (s6 >= 0 && FD_ISSET(s6, &readfds))
|
||||
rc = recvfrom(s6, (char*)buf.data(), buf.size(), 0, (sockaddr*)&from, &from_len);
|
||||
else if (ls4 >= 0 && FD_ISSET(ls4, &readfds))
|
||||
rc = recvfrom(ls4, (char*)buf.data(), buf.size(), 0, (sockaddr*)&from, &from_len);
|
||||
else if (ls6 >= 0 && FD_ISSET(ls6, &readfds))
|
||||
rc = recvfrom(ls6, (char*)buf.data(), buf.size(), 0, (sockaddr*)&from, &from_len);
|
||||
else
|
||||
continue;
|
||||
|
||||
@@ -276,25 +280,28 @@ UdpSocket::openSockets(const SockAddr& bind4, const SockAddr& bind6)
|
||||
logger.e("Error receiving packet: %s", strerror(errno));
|
||||
int err = errno;
|
||||
if (err == EPIPE || err == ENOTCONN || err == ECONNRESET) {
|
||||
if (s4 >= 0) {
|
||||
close(s4);
|
||||
std::lock_guard<std::mutex> lk(lock);
|
||||
if (ls4 >= 0) {
|
||||
close(ls4);
|
||||
try {
|
||||
s4 = bindSocket(bound4, bound4);
|
||||
ls4 = bindSocket(bound4, bound4);
|
||||
} catch (const DhtException& e) {
|
||||
logger.e("Can't bind inet socket: %s", e.what());
|
||||
}
|
||||
}
|
||||
if (s6 >= 0) {
|
||||
close(s6);
|
||||
if (ls6 >= 0) {
|
||||
close(ls6);
|
||||
try {
|
||||
s6 = bindSocket(bound6, bound6);
|
||||
ls6 = bindSocket(bound6, bound6);
|
||||
} catch (const DhtException& e) {
|
||||
logger.e("Can't bind inet6 socket: %s", e.what());
|
||||
}
|
||||
}
|
||||
if (s4 < 0 && s6 < 0)
|
||||
if (ls4 < 0 && ls6 < 0)
|
||||
break;
|
||||
selectFd = std::max({s4, s6, stop_readfd}) + 1;
|
||||
s4 = ls4;
|
||||
s6 = ls6;
|
||||
selectFd = std::max({ls4, ls6, stop_readfd}) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -302,16 +309,17 @@ UdpSocket::openSockets(const SockAddr& bind4, const SockAddr& bind6)
|
||||
} catch (const std::exception& e) {
|
||||
logger.e("Error in UdpSocket rx thread: %s", e.what());
|
||||
}
|
||||
if (s4 >= 0)
|
||||
close(s4);
|
||||
if (s6 >= 0)
|
||||
close(s6);
|
||||
if (ls4 >= 0)
|
||||
close(ls4);
|
||||
if (ls6 >= 0)
|
||||
close(ls6);
|
||||
if (stop_readfd != -1)
|
||||
close(stop_readfd);
|
||||
std::lock_guard<std::mutex> lk(lock);
|
||||
s4 = -1;
|
||||
s6 = -1;
|
||||
bound4 = {};
|
||||
bound6 = {};
|
||||
if (stop_readfd != -1)
|
||||
close(stop_readfd);
|
||||
if (stopfd != -1)
|
||||
close(stopfd);
|
||||
stopfd = -1;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "cryptotester.h"
|
||||
|
||||
#include "opendht/crypto.h"
|
||||
#include <opendht/crypto.h>
|
||||
|
||||
namespace test {
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(CryptoTester);
|
||||
@@ -61,13 +61,13 @@ CryptoTester::testSignatureEncryption() {
|
||||
{
|
||||
std::vector<uint8_t> encrypted = public_key.encrypt(data1);
|
||||
encrypted[1]++;
|
||||
CPPUNIT_ASSERT_THROW(key.decrypt(encrypted), dht::crypto::DecryptError);
|
||||
CPPUNIT_ASSERT_THROW(key.decrypt(encrypted), std::runtime_error);
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<uint8_t> encrypted = public_key.encrypt(data2);
|
||||
encrypted[2]++;
|
||||
CPPUNIT_ASSERT_THROW(key.decrypt(encrypted), dht::crypto::DecryptError);
|
||||
CPPUNIT_ASSERT_THROW(key.decrypt(encrypted), std::runtime_error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+92
-58
@@ -27,28 +27,26 @@
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace test {
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(DhtProxyTester);
|
||||
|
||||
void
|
||||
DhtProxyTester::setUp() {
|
||||
logger = dht::log::getStdLogger();
|
||||
|
||||
nodePeer.run(0, /*identity*/{}, /*threaded*/true);
|
||||
nodePeer.run(0);
|
||||
|
||||
nodeProxy = std::make_shared<dht::DhtRunner>();
|
||||
nodeProxy->run(0, /*identity*/{}, /*threaded*/true);
|
||||
nodeProxy->run(0);
|
||||
nodeProxy->bootstrap(nodePeer.getBound());
|
||||
|
||||
auto serverCAIdentity = dht::crypto::generateEcIdentity("DHT Node CA");
|
||||
auto serverIdentity = dht::crypto::generateIdentity("DHT Node", serverCAIdentity);
|
||||
|
||||
serverProxy = std::unique_ptr<dht::DhtProxyServer>(
|
||||
new dht::DhtProxyServer(
|
||||
serverProxy = std::make_unique<dht::DhtProxyServer>(
|
||||
//dht::crypto::Identity{}, // http
|
||||
serverIdentity, // https
|
||||
nodeProxy, 8080, /*pushServer*/"127.0.0.1:8090", logger));
|
||||
nodeProxy, 8080, /*pushServer*/"127.0.0.1:8090");
|
||||
|
||||
clientConfig.server_ca = serverCAIdentity.second;
|
||||
clientConfig.client_identity = dht::crypto::generateIdentity("DhtProxyTester");
|
||||
@@ -56,27 +54,31 @@ DhtProxyTester::setUp() {
|
||||
clientConfig.threaded = true;
|
||||
clientConfig.push_node_id = "dhtnode";
|
||||
clientConfig.proxy_server = "https://127.0.0.1:8080";
|
||||
|
||||
dht::DhtRunner::Context clientContext {};
|
||||
clientContext.logger = logger;
|
||||
|
||||
nodeClient = std::make_shared<dht::DhtRunner>();
|
||||
nodeClient->run(0, clientConfig, std::move(clientContext));
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyTester::tearDown() {
|
||||
logger->d("[tester:proxy] stopping peer node");
|
||||
nodePeer.join();
|
||||
nodeClient->join();
|
||||
logger->d("[tester:proxy] stopping proxy server");
|
||||
serverProxy.reset(nullptr);
|
||||
logger->d("[tester:proxy] stopping proxy node");
|
||||
nodeProxy->join();
|
||||
nodeClient.join();
|
||||
|
||||
bool done = false;
|
||||
std::condition_variable cv;
|
||||
std::mutex cv_m;
|
||||
nodeProxy->shutdown([&]{
|
||||
std::lock_guard<std::mutex> lk(cv_m);
|
||||
done = true;
|
||||
cv.notify_all();
|
||||
});
|
||||
std::unique_lock<std::mutex> lk(cv_m);
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&]{ return done; }));
|
||||
serverProxy.reset();
|
||||
nodeProxy.reset();
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyTester::testGetPut() {
|
||||
nodeClient.run(0, clientConfig);
|
||||
|
||||
bool done = false;
|
||||
std::condition_variable cv;
|
||||
std::mutex cv_m;
|
||||
@@ -85,22 +87,24 @@ DhtProxyTester::testGetPut() {
|
||||
dht::Value val {"Hey! It's been a long time. How have you been?"};
|
||||
auto val_data = val.data;
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(cv_m);
|
||||
nodePeer.put(key, std::move(val), [&](bool) {
|
||||
std::lock_guard<std::mutex> lk(cv_m);
|
||||
done = true;
|
||||
cv.notify_all();
|
||||
});
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; }));
|
||||
std::unique_lock<std::mutex> lk(cv_m);
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]{ return done; }));
|
||||
}
|
||||
|
||||
auto vals = nodeClient->get(key).get();
|
||||
auto vals = nodeClient.get(key).get();
|
||||
CPPUNIT_ASSERT(not vals.empty());
|
||||
CPPUNIT_ASSERT(vals.front()->data == val_data);
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyTester::testListen() {
|
||||
nodeClient.run(0, clientConfig);
|
||||
|
||||
std::condition_variable cv;
|
||||
std::mutex cv_m;
|
||||
std::unique_lock<std::mutex> lk(cv_m);
|
||||
@@ -117,11 +121,11 @@ DhtProxyTester::testListen() {
|
||||
done = true;
|
||||
cv.notify_all();
|
||||
});
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; }));
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]{ return done; }));
|
||||
done = false;
|
||||
|
||||
std::vector<dht::Blob> values;
|
||||
auto token = nodeClient->listen(key, [&](const std::vector<std::shared_ptr<dht::Value>>& v, bool expired) {
|
||||
auto token = nodeClient.listen(key, [&](const std::vector<std::shared_ptr<dht::Value>>& v, bool expired) {
|
||||
if (not expired) {
|
||||
std::lock_guard<std::mutex> lk(cv_m);
|
||||
for (const auto& value : v)
|
||||
@@ -131,7 +135,7 @@ DhtProxyTester::testListen() {
|
||||
}
|
||||
return true;
|
||||
});
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; }));
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]{ return done; }));
|
||||
done = false;
|
||||
// Here values should contains 1 values
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<int>(values.size()), 1);
|
||||
@@ -142,8 +146,8 @@ DhtProxyTester::testListen() {
|
||||
dht::Value secondVal {"You're a monster"};
|
||||
auto secondVal_data = secondVal.data;
|
||||
nodePeer.put(key, std::move(secondVal));
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; }));
|
||||
nodeClient->cancelListen(key, std::move(token));
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]{ return done; }));
|
||||
nodeClient.cancelListen(key, std::move(token));
|
||||
// Here values should contains 2 values
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<int>(values.size()), 2);
|
||||
CPPUNIT_ASSERT(values.back() == secondVal_data);
|
||||
@@ -151,7 +155,8 @@ DhtProxyTester::testListen() {
|
||||
|
||||
void
|
||||
DhtProxyTester::testResubscribeGetValues() {
|
||||
nodeClient->setPushNotificationToken("atlas");
|
||||
clientConfig.push_token = "atlas";
|
||||
nodeClient.run(0, clientConfig);
|
||||
|
||||
bool done = false;
|
||||
std::condition_variable cv;
|
||||
@@ -169,59 +174,55 @@ DhtProxyTester::testResubscribeGetValues() {
|
||||
done = true;
|
||||
cv.notify_all();
|
||||
});
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; }));
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]{ return done; }));
|
||||
done = false;
|
||||
|
||||
// Send a first subscribe, the value is sent via a push notification
|
||||
// So ignore values here.
|
||||
nodeClient->listen(key, [&](const std::vector<std::shared_ptr<dht::Value>>&, bool) {
|
||||
nodeClient.listen(key, [&](const std::vector<std::shared_ptr<dht::Value>>&, bool) {
|
||||
return true;
|
||||
});
|
||||
cv.wait_for(lk, std::chrono::seconds(1));
|
||||
|
||||
// Reboot node (to avoid cache)
|
||||
nodeClient->join();
|
||||
clientConfig.push_token = "atlas";
|
||||
|
||||
dht::DhtRunner::Context clientContext {};
|
||||
clientContext.logger = logger;
|
||||
nodeClient->run(0, clientConfig, std::move(clientContext));
|
||||
nodeClient.join();
|
||||
clientConfig.push_token = "";
|
||||
nodeClient.run(0, clientConfig);
|
||||
|
||||
// For the second subscribe, the proxy will return the value in the body
|
||||
auto values = std::vector<dht::Blob>();
|
||||
auto ftoken = nodeClient->listen(key, [&](const std::vector<std::shared_ptr<dht::Value>>& v, bool expired) {
|
||||
std::vector<std::shared_ptr<dht::Value>> values;
|
||||
auto ftoken = nodeClient.listen(key, [&](const std::vector<std::shared_ptr<dht::Value>>& v, bool expired) {
|
||||
if (not expired) {
|
||||
std::lock_guard<std::mutex> lk(cv_m);
|
||||
for (const auto& value : v)
|
||||
values.emplace_back(value->data);
|
||||
values.insert(values.end(), v.begin(), v.end());
|
||||
done = true;
|
||||
cv.notify_all();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; }));
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]{ return done; }));
|
||||
auto token = ftoken.get();
|
||||
CPPUNIT_ASSERT(token);
|
||||
nodeClient->cancelListen(key, token);
|
||||
nodeClient.cancelListen(key, token);
|
||||
// Here values should still contains 1 values
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)1u, values.size());
|
||||
CPPUNIT_ASSERT(firstVal_data == values.front());
|
||||
CPPUNIT_ASSERT(firstVal_data == values.front()->data);
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyTester::testPutGet40KChars()
|
||||
{
|
||||
nodeClient.run(0, clientConfig);
|
||||
constexpr size_t N = 40000;
|
||||
|
||||
// Arrange
|
||||
auto key = dht::InfoHash::get("testPutGet40KChars");
|
||||
std::vector<std::shared_ptr<dht::Value>> values;
|
||||
std::string mtu = "";
|
||||
for (int i = 0; i < 40000; i++){
|
||||
if (i % 2 == 0)
|
||||
mtu.append("M");
|
||||
else
|
||||
mtu.append("T");
|
||||
}
|
||||
std::vector<uint8_t> mtu;
|
||||
mtu.reserve(N);
|
||||
for (size_t i = 0; i < N; i++)
|
||||
mtu.emplace_back((i % 2) ? 'T' : 'M');
|
||||
std::condition_variable cv;
|
||||
std::mutex cv_m;
|
||||
std::unique_lock<std::mutex> lk(cv_m);
|
||||
@@ -230,28 +231,61 @@ DhtProxyTester::testPutGet40KChars()
|
||||
|
||||
// Act
|
||||
dht::Value val {mtu};
|
||||
nodeClient->put(key, std::move(val), [&](bool ok) {
|
||||
nodePeer.put(key, std::move(val), [&](bool ok) {
|
||||
std::lock_guard<std::mutex> lk(cv_m);
|
||||
done_put = ok;
|
||||
cv.notify_all();
|
||||
});
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done_put; }));
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]{ return done_put; }));
|
||||
|
||||
nodeClient->get(key, [&](const std::vector<std::shared_ptr<dht::Value>>& vals){
|
||||
values = vals;
|
||||
nodeClient.get(key, [&](const std::vector<std::shared_ptr<dht::Value>>& vals){
|
||||
values.insert(values.end(), vals.begin(), vals.end());
|
||||
return true;
|
||||
},[&](bool ok){
|
||||
std::lock_guard<std::mutex> lk(cv_m);
|
||||
done_get = ok;
|
||||
cv.notify_all();
|
||||
});
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done_get; }));
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]{ return done_get; }));
|
||||
|
||||
// Assert
|
||||
dht::Value mtu_val {mtu};
|
||||
for (const auto &value: values){
|
||||
CPPUNIT_ASSERT(value->data == mtu_val.data);
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)1u, values.size());
|
||||
for (const auto &value: values)
|
||||
CPPUNIT_ASSERT(value->data == mtu);
|
||||
}
|
||||
|
||||
void
|
||||
DhtProxyTester::testFuzzy()
|
||||
{
|
||||
constexpr size_t N = 40000;
|
||||
|
||||
// Arrange
|
||||
auto key = dht::InfoHash::get("testFuzzy");
|
||||
std::vector<std::shared_ptr<dht::Value>> values;
|
||||
std::vector<uint8_t> mtu;
|
||||
mtu.reserve(N);
|
||||
for (size_t i = 0; i < N; i++)
|
||||
mtu.emplace_back((i % 2) ? 'T' : 'M');
|
||||
|
||||
// Act
|
||||
for (size_t i = 0; i < 100; i++) {
|
||||
auto nodeTest = std::make_shared<dht::DhtRunner>();
|
||||
nodeTest->run(0, clientConfig);
|
||||
nodeTest->put(key, dht::Value(mtu), [&](bool ok) {
|
||||
CPPUNIT_ASSERT(ok);
|
||||
});
|
||||
nodeTest->get(key, [&](const std::vector<std::shared_ptr<dht::Value>>& vals){
|
||||
values.insert(values.end(), vals.begin(), vals.end());
|
||||
return true;
|
||||
},[&](bool ok){
|
||||
CPPUNIT_ASSERT(ok);
|
||||
});
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
|
||||
// Assert
|
||||
for (const auto &value: values)
|
||||
CPPUNIT_ASSERT(value->data == mtu);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
@@ -35,6 +35,7 @@ class DhtProxyTester : public CppUnit::TestFixture {
|
||||
CPPUNIT_TEST(testListen);
|
||||
CPPUNIT_TEST(testResubscribeGetValues);
|
||||
CPPUNIT_TEST(testPutGet40KChars);
|
||||
CPPUNIT_TEST(testFuzzy);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
@@ -65,15 +66,14 @@ class DhtProxyTester : public CppUnit::TestFixture {
|
||||
*/
|
||||
void testPutGet40KChars();
|
||||
|
||||
private:
|
||||
dht::DhtRunner nodePeer;
|
||||
void testFuzzy();
|
||||
|
||||
std::shared_ptr<dht::DhtRunner> nodeClient;
|
||||
private:
|
||||
dht::DhtRunner::Config clientConfig {};
|
||||
dht::DhtRunner nodePeer;
|
||||
dht::DhtRunner nodeClient;
|
||||
std::shared_ptr<dht::DhtRunner> nodeProxy;
|
||||
std::unique_ptr<dht::DhtProxyServer> serverProxy;
|
||||
|
||||
dht::DhtRunner::Config clientConfig {};
|
||||
std::shared_ptr<dht::Logger> logger {};
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
+22
-14
@@ -22,19 +22,32 @@
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace test {
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(DhtRunnerTester);
|
||||
|
||||
void
|
||||
DhtRunnerTester::setUp() {
|
||||
node1.run(42222, {}, true);
|
||||
node2.run(42232, {}, true);
|
||||
node1.run(42222);
|
||||
node2.run(42232);
|
||||
node2.bootstrap(node1.getBound());
|
||||
}
|
||||
|
||||
void
|
||||
DhtRunnerTester::tearDown() {
|
||||
unsigned done {0};
|
||||
std::condition_variable cv;
|
||||
std::mutex cv_m;
|
||||
auto shutdown = [&]{
|
||||
std::lock_guard<std::mutex> lk(cv_m);
|
||||
done++;
|
||||
cv.notify_all();
|
||||
};
|
||||
node1.shutdown(shutdown);
|
||||
node2.shutdown(shutdown);
|
||||
std::unique_lock<std::mutex> lk(cv_m);
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&]{ return done == 2; }));
|
||||
node1.join();
|
||||
node2.join();
|
||||
}
|
||||
@@ -90,27 +103,22 @@ DhtRunnerTester::testListen() {
|
||||
|
||||
for (unsigned i=0; i<N; i++) {
|
||||
node2.put(a, dht::Value("v1"), [&](bool ok) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
putCount++;
|
||||
if (ok) putOkCount++;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
putCount++;
|
||||
if (ok) putOkCount++;
|
||||
cv.notify_all();
|
||||
});
|
||||
node2.put(b, dht::Value("v2"), [&](bool ok) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
putCount++;
|
||||
if (ok) putOkCount++;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
putCount++;
|
||||
if (ok) putOkCount++;
|
||||
cv.notify_all();
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
cv.wait_for(lk, std::chrono::seconds(30), [&]{ return putCount == N * 2u; });
|
||||
CPPUNIT_ASSERT_EQUAL(N * 2u, putCount);
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]{ return putCount == N * 2u; }));
|
||||
CPPUNIT_ASSERT_EQUAL(N * 2u, putOkCount);
|
||||
}
|
||||
|
||||
|
||||
+47
-15
@@ -19,14 +19,15 @@
|
||||
|
||||
#include "httptester.h"
|
||||
|
||||
// std
|
||||
#include <opendht/log.h>
|
||||
#include <opendht/value.h>
|
||||
#include <opendht/dhtrunner.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
|
||||
|
||||
namespace test {
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(HttpTester);
|
||||
|
||||
@@ -34,26 +35,22 @@ void
|
||||
HttpTester::setUp() {
|
||||
logger = dht::log::getStdLogger();
|
||||
|
||||
nodePeer.run(0, /*identity*/{}, /*threaded*/true);
|
||||
nodePeer = std::make_shared<dht::DhtRunner>();
|
||||
nodePeer->run(0);
|
||||
|
||||
nodeProxy = std::make_shared<dht::DhtRunner>();
|
||||
auto nodeProxy = std::make_shared<dht::DhtRunner>();
|
||||
nodeProxy->run(0, /*identity*/{}, /*threaded*/true);
|
||||
nodeProxy->bootstrap(nodePeer.getBound());
|
||||
nodeProxy->bootstrap(nodePeer->getBound());
|
||||
|
||||
serverProxy = std::unique_ptr<dht::DhtProxyServer>(
|
||||
new dht::DhtProxyServer(
|
||||
/*http*/dht::crypto::Identity{}, nodeProxy, 8080, /*pushServer*/"127.0.0.1:8090", logger));
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
HttpTester::tearDown() {
|
||||
logger->d("[tester:http] stopping peer node");
|
||||
nodePeer.join();
|
||||
logger->d("[tester:http] stopping proxy server");
|
||||
serverProxy.reset(nullptr);
|
||||
logger->d("[tester:http] stopping proxy node");
|
||||
nodeProxy->join();
|
||||
serverProxy.reset();
|
||||
nodePeer->join();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -248,7 +245,7 @@ HttpTester::test_send_json() {
|
||||
if (status_code != 200 and logger)
|
||||
logger->e("[tester] [status] failed with code=%i", status_code);
|
||||
std::cout << "[tester] got response:\n" << value << std::endl;
|
||||
resp_val = value;
|
||||
resp_val = std::move(value);
|
||||
status = status_code;
|
||||
done = true;
|
||||
cv.notify_all();
|
||||
@@ -258,7 +255,42 @@ HttpTester::test_send_json() {
|
||||
// Assert
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; }));
|
||||
CPPUNIT_ASSERT(status == 200);
|
||||
CPPUNIT_ASSERT(resp_val["data"] == val.toJson()["data"]);
|
||||
CPPUNIT_ASSERT(resp_val["data"] == json["data"]);
|
||||
|
||||
done = false;
|
||||
request = std::make_shared<dht::http::Request>(serverProxy->io_context(), "http://google.ca",
|
||||
[&](const dht::http::Response& response){
|
||||
logger->w("got answer: %.*s", response.body.size(), response.body.data());
|
||||
status = response.status_code;
|
||||
done = true;
|
||||
cv.notify_all();
|
||||
}, logger);
|
||||
request->send();
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; }));
|
||||
//CPPUNIT_ASSERT(status == 200);
|
||||
|
||||
done = false;
|
||||
request = std::make_shared<dht::http::Request>(serverProxy->io_context(), "https://google.ca",
|
||||
[&](const dht::http::Response& response){
|
||||
logger->w("got answer: %.*s", response.body.size(), response.body.data());
|
||||
status = response.status_code;
|
||||
done = true;
|
||||
cv.notify_all();
|
||||
}, logger);
|
||||
request->send();
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; }));
|
||||
//CPPUNIT_ASSERT(status == 200);
|
||||
|
||||
done = false;
|
||||
request = std::make_shared<dht::http::Request>(serverProxy->io_context(), "https://google.ca/sdbjklwGBIP",
|
||||
[&](const dht::http::Response& response){
|
||||
logger->w("got answer: %.*s", response.body.size(), response.body.data());
|
||||
status = response.status_code;
|
||||
done = true;
|
||||
cv.notify_all();
|
||||
}, logger);
|
||||
request->send();
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(10), [&]{ return done; }));
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
+5
-7
@@ -23,12 +23,12 @@
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
|
||||
#include <opendht/value.h>
|
||||
#include <opendht/log.h>
|
||||
#include <opendht/http.h>
|
||||
|
||||
#include <opendht/dhtrunner.h>
|
||||
#include <opendht/dht_proxy_server.h>
|
||||
#include <asio.hpp>
|
||||
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
|
||||
namespace test {
|
||||
|
||||
@@ -89,9 +89,7 @@ class HttpTester : public CppUnit::TestFixture {
|
||||
|
||||
private:
|
||||
std::shared_ptr<dht::Logger> logger {};
|
||||
|
||||
dht::DhtRunner nodePeer;
|
||||
std::shared_ptr<dht::DhtRunner> nodeProxy;
|
||||
std::shared_ptr<dht::DhtRunner> nodePeer;
|
||||
std::unique_ptr<dht::DhtProxyServer> serverProxy;
|
||||
};
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ add_executable (dhtnode dhtnode.cpp tools_common.h)
|
||||
add_executable (dhtscanner dhtscanner.cpp tools_common.h)
|
||||
add_executable (dhtchat dhtchat.cpp tools_common.h)
|
||||
|
||||
target_link_libraries (dhtnode LINK_PUBLIC readline)
|
||||
target_link_libraries (dhtscanner LINK_PUBLIC readline)
|
||||
target_link_libraries (dhtchat LINK_PUBLIC readline)
|
||||
target_link_libraries (dhtnode LINK_PUBLIC ${READLINE_LIBRARIES})
|
||||
target_link_libraries (dhtscanner LINK_PUBLIC ${READLINE_LIBRARIES})
|
||||
target_link_libraries (dhtchat LINK_PUBLIC ${READLINE_LIBRARIES})
|
||||
|
||||
if (OPENDHT_SHARED)
|
||||
target_link_libraries (dhtnode LINK_PUBLIC opendht)
|
||||
@@ -19,7 +19,7 @@ endif ()
|
||||
|
||||
if (OPENDHT_C)
|
||||
add_executable (dhtcnode dhtcnode.c)
|
||||
target_link_libraries (dhtcnode LINK_PUBLIC opendht-c readline)
|
||||
target_link_libraries (dhtcnode LINK_PUBLIC opendht-c ${READLINE_LIBRARIES})
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED CMAKE_INSTALL_BINDIR)
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
bin_PROGRAMS = dhtnode dhtchat dhtscanner
|
||||
noinst_HEADERS = tools_common.h
|
||||
|
||||
AM_CPPFLAGS = -I../include @JsonCpp_CFLAGS@ @MsgPack_CFLAGS@
|
||||
AM_CPPFLAGS = -isystem @top_srcdir@/include @JsonCpp_CFLAGS@ @MsgPack_CFLAGS@
|
||||
|
||||
dhtnode_SOURCES = dhtnode.cpp
|
||||
dhtnode_LDFLAGS = -lopendht -lreadline -L@top_builddir@/src/.libs @Argon2_LDFLAGS@ @GnuTLS_LIBS@
|
||||
|
||||
+3
-43
@@ -37,11 +37,6 @@ const std::string printTime(const std::time_t& now) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
void print_node_info(const DhtRunner& dht, const dht_params&) {
|
||||
std::cout << "OpenDht node " << dht.getNodeId() << " running on port " << dht.getBoundPort() << std::endl;
|
||||
std::cout << "Public key ID " << dht.getId() << std::endl;
|
||||
}
|
||||
|
||||
void print_usage() {
|
||||
std::cout << "Usage: dhtchat [-n network_id] [-p local_port] [-b bootstrap_host[:port]]" << std::endl << std::endl;
|
||||
std::cout << "dhtchat, a simple OpenDHT command line chat client." << std::endl;
|
||||
@@ -62,48 +57,13 @@ main(int argc, char **argv)
|
||||
|
||||
DhtRunner dht;
|
||||
try {
|
||||
if (not params.id.first) {
|
||||
auto node_ca = std::make_unique<dht::crypto::Identity>(dht::crypto::generateEcIdentity("DHT Node CA"));
|
||||
params.id = dht::crypto::generateIdentity("DHT Chat Node", *node_ca);
|
||||
if (not params.save_identity.empty()) {
|
||||
dht::crypto::saveIdentity(*node_ca, params.save_identity + "_ca", params.privkey_pwd);
|
||||
dht::crypto::saveIdentity(params.id, params.save_identity, params.privkey_pwd);
|
||||
}
|
||||
}
|
||||
|
||||
dht::DhtRunner::Config config {};
|
||||
config.dht_config.node_config.network = params.network;
|
||||
config.dht_config.node_config.maintain_storage = false;
|
||||
config.dht_config.node_config.persist_path = params.persist_path;
|
||||
config.dht_config.id = params.id;
|
||||
config.threaded = true;
|
||||
config.proxy_server = params.proxyclient;
|
||||
config.push_node_id = "dhtnode";
|
||||
config.push_token = params.devicekey;
|
||||
config.peer_discovery = params.peer_discovery;
|
||||
config.peer_publish = params.peer_discovery;
|
||||
|
||||
dht::DhtRunner::Context context {};
|
||||
if (params.log) {
|
||||
if (params.syslog or (params.daemonize and params.logfile.empty()))
|
||||
context.logger = log::getSyslogLogger("dhtnode");
|
||||
else if (not params.logfile.empty())
|
||||
context.logger = log::getFileLogger(params.logfile);
|
||||
else
|
||||
context.logger = log::getStdLogger();
|
||||
}
|
||||
dht.run(params.port, config, std::move(context));
|
||||
params.generate_identity = true;
|
||||
auto dhtConf = getDhtConfig(params);
|
||||
dht.run(params.port, dhtConf.first, std::move(dhtConf.second));
|
||||
|
||||
if (not params.bootstrap.first.empty())
|
||||
dht.bootstrap(params.bootstrap.first.c_str(), params.bootstrap.second.c_str());
|
||||
|
||||
#ifdef OPENDHT_PROXY_CLIENT
|
||||
if (!params.proxyclient.empty()) {
|
||||
dht.setProxyServer(params.proxyclient);
|
||||
dht.enableProxy(true);
|
||||
}
|
||||
#endif //OPENDHT_PROXY_CLIENT
|
||||
|
||||
print_node_info(dht, params);
|
||||
std::cout << " type 'c {hash}' to join a channel" << std::endl << std::endl;
|
||||
|
||||
|
||||
+10
-49
@@ -48,18 +48,6 @@ void print_id_req() {
|
||||
std::cout << "An identity is required to perform this operation (run with -i)" << std::endl;
|
||||
}
|
||||
|
||||
void print_node_info(const std::shared_ptr<DhtRunner>& node, const dht_params& params) {
|
||||
std::cout << "OpenDHT node " << node->getNodeId() << " running on ";
|
||||
auto port4 = node->getBoundPort(AF_INET);
|
||||
auto port6 = node->getBoundPort(AF_INET6);
|
||||
if (port4 == port6)
|
||||
std::cout << "port " << port4 << std::endl;
|
||||
else
|
||||
std::cout << "IPv4 port " << port4 << ", IPv6 port " << port6 << std::endl;
|
||||
if (params.id.first)
|
||||
std::cout << "Public key ID " << node->getId() << std::endl;
|
||||
}
|
||||
|
||||
void print_help() {
|
||||
std::cout << "OpenDHT command line interface (CLI)" << std::endl;
|
||||
std::cout << "Possible commands:" << std::endl
|
||||
@@ -123,7 +111,7 @@ void cmd_loop(std::shared_ptr<DhtRunner>& node, dht_params& params
|
||||
#endif
|
||||
)
|
||||
{
|
||||
print_node_info(node, params);
|
||||
print_node_info(*node, params);
|
||||
std::cout << " (type 'h' or 'help' for a list of possible commands)" << std::endl << std::endl;
|
||||
|
||||
#ifndef WIN32_NATIVE
|
||||
@@ -152,11 +140,13 @@ void cmd_loop(std::shared_ptr<DhtRunner>& node, dht_params& params
|
||||
print_help();
|
||||
continue;
|
||||
} else if (op == "ll") {
|
||||
print_node_info(node, params);
|
||||
auto nodeInfo = node->getNodeInfo();
|
||||
print_node_info(*node, params);
|
||||
std::cout << nodeInfo.ongoing_ops << " ongoing operations" << std::endl;
|
||||
std::cout << "IPv4 stats:" << std::endl;
|
||||
std::cout << node->getNodesStats(AF_INET).toString() << std::endl;
|
||||
std::cout << nodeInfo.ipv4.toString() << std::endl;
|
||||
std::cout << "IPv6 stats:" << std::endl;
|
||||
std::cout << node->getNodesStats(AF_INET6).toString() << std::endl;
|
||||
std::cout << nodeInfo.ipv6.toString() << std::endl;
|
||||
#ifdef OPENDHT_PROXY_SERVER
|
||||
for (const auto& proxy : proxies) {
|
||||
std::cout << "Stats for proxy on port " << proxy.first << std::endl;
|
||||
@@ -532,37 +522,8 @@ main(int argc, char **argv)
|
||||
|
||||
auto node = std::make_shared<DhtRunner>();
|
||||
try {
|
||||
if (not params.id.first and params.generate_identity) {
|
||||
auto node_ca = std::make_unique<dht::crypto::Identity>(dht::crypto::generateEcIdentity("DHT Node CA"));
|
||||
params.id = dht::crypto::generateIdentity("DHT Node", *node_ca);
|
||||
if (not params.save_identity.empty()) {
|
||||
dht::crypto::saveIdentity(*node_ca, params.save_identity + "_ca", params.privkey_pwd);
|
||||
dht::crypto::saveIdentity(params.id, params.save_identity, params.privkey_pwd);
|
||||
}
|
||||
}
|
||||
|
||||
dht::DhtRunner::Config config {};
|
||||
config.dht_config.node_config.network = params.network;
|
||||
config.dht_config.node_config.maintain_storage = false;
|
||||
config.dht_config.node_config.persist_path = params.persist_path;
|
||||
config.dht_config.id = params.id;
|
||||
config.threaded = true;
|
||||
config.proxy_server = params.proxyclient;
|
||||
config.push_node_id = "dhtnode";
|
||||
config.push_token = params.devicekey;
|
||||
config.peer_discovery = params.peer_discovery;
|
||||
config.peer_publish = params.peer_discovery;
|
||||
|
||||
dht::DhtRunner::Context context {};
|
||||
if (params.log) {
|
||||
if (params.syslog or (params.daemonize and params.logfile.empty()))
|
||||
context.logger = log::getSyslogLogger("dhtnode");
|
||||
else if (not params.logfile.empty())
|
||||
context.logger = log::getFileLogger(params.logfile);
|
||||
else
|
||||
context.logger = log::getStdLogger();
|
||||
}
|
||||
node->run(params.port, config, std::move(context));
|
||||
auto dhtConf = getDhtConfig(params);
|
||||
node->run(params.port, dhtConf.first, std::move(dhtConf.second));
|
||||
|
||||
if (not params.bootstrap.first.empty()) {
|
||||
std::cout << "Bootstrap: " << params.bootstrap.first << ":" << params.bootstrap.second << std::endl;
|
||||
@@ -576,13 +537,13 @@ main(int argc, char **argv)
|
||||
#ifdef OPENDHT_PROXY_SERVER
|
||||
proxies.emplace(params.proxyserverssl, std::unique_ptr<DhtProxyServer>(
|
||||
new DhtProxyServer(params.proxy_id,
|
||||
node, params.proxyserverssl, params.pushserver, context.logger)));
|
||||
node, params.proxyserverssl, params.pushserver, dhtConf.second.logger)));
|
||||
}
|
||||
if (params.proxyserver) {
|
||||
proxies.emplace(params.proxyserver, std::unique_ptr<DhtProxyServer>(
|
||||
new DhtProxyServer(
|
||||
dht::crypto::Identity{},
|
||||
node, params.proxyserver, params.pushserver, context.logger)));
|
||||
node, params.proxyserver, params.pushserver, dhtConf.second.logger)));
|
||||
#else
|
||||
std::cerr << "DHT proxy server requested but OpenDHT built without proxy server support." << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
+3
-11
@@ -85,21 +85,13 @@ main(int argc, char **argv)
|
||||
|
||||
DhtRunner dht;
|
||||
try {
|
||||
dht.run(params.port, {}, true, params.network);
|
||||
|
||||
if (params.log) {
|
||||
if (params.syslog)
|
||||
log::enableSyslog(dht, "dhtnode");
|
||||
else if (not params.logfile.empty())
|
||||
log::enableFileLogging(dht, params.logfile);
|
||||
else
|
||||
log::enableLogging(dht);
|
||||
}
|
||||
auto dhtConf = getDhtConfig(params);
|
||||
dht.run(params.port, dhtConf.first, std::move(dhtConf.second));
|
||||
|
||||
if (not params.bootstrap.first.empty())
|
||||
dht.bootstrap(params.bootstrap.first.c_str(), params.bootstrap.second.c_str());
|
||||
|
||||
std::cout << "OpenDht node " << dht.getNodeId() << " running on port " << params.port << std::endl;
|
||||
print_node_info(dht, params);
|
||||
std::cout << "Scanning network..." << std::endl;
|
||||
auto all_nodes = std::make_shared<NodeSet>();
|
||||
|
||||
|
||||
+63
-1
@@ -131,8 +131,66 @@ struct dht_params {
|
||||
std::string privkey_pwd {};
|
||||
std::string proxy_privkey_pwd {};
|
||||
std::string save_identity {};
|
||||
bool no_rate_limit {false};
|
||||
};
|
||||
|
||||
std::pair<dht::DhtRunner::Config, dht::DhtRunner::Context>
|
||||
getDhtConfig(dht_params& params)
|
||||
{
|
||||
if (not params.id.first and params.generate_identity) {
|
||||
auto node_ca = std::make_unique<dht::crypto::Identity>(dht::crypto::generateEcIdentity("DHT Node CA"));
|
||||
params.id = dht::crypto::generateIdentity("DHT Node", *node_ca);
|
||||
if (not params.save_identity.empty()) {
|
||||
dht::crypto::saveIdentity(*node_ca, params.save_identity + "_ca", params.privkey_pwd);
|
||||
dht::crypto::saveIdentity(params.id, params.save_identity, params.privkey_pwd);
|
||||
}
|
||||
}
|
||||
|
||||
dht::DhtRunner::Config config {};
|
||||
config.dht_config.node_config.network = params.network;
|
||||
config.dht_config.node_config.maintain_storage = false;
|
||||
config.dht_config.node_config.persist_path = params.persist_path;
|
||||
config.dht_config.id = params.id;
|
||||
config.threaded = true;
|
||||
config.proxy_server = params.proxyclient;
|
||||
config.push_node_id = "dhtnode";
|
||||
config.push_token = params.devicekey;
|
||||
config.peer_discovery = params.peer_discovery;
|
||||
config.peer_publish = params.peer_discovery;
|
||||
if (params.no_rate_limit) {
|
||||
config.dht_config.node_config.max_req_per_sec = -1;
|
||||
config.dht_config.node_config.max_peer_req_per_sec = -1;
|
||||
}
|
||||
|
||||
dht::DhtRunner::Context context {};
|
||||
if (params.log) {
|
||||
if (params.syslog or (params.daemonize and params.logfile.empty()))
|
||||
context.logger = dht::log::getSyslogLogger("dhtnode");
|
||||
else if (not params.logfile.empty())
|
||||
context.logger = dht::log::getFileLogger(params.logfile);
|
||||
else
|
||||
context.logger = dht::log::getStdLogger();
|
||||
}
|
||||
if (context.logger) {
|
||||
context.statusChangedCallback = [logger = *context.logger](dht::NodeStatus status4, dht::NodeStatus status6) {
|
||||
logger.WARN("Connectivity changed: IPv4: %s, IPv6: %s", dht::statusToStr(status4), dht::statusToStr(status6));
|
||||
};
|
||||
}
|
||||
return {std::move(config), std::move(context)};
|
||||
}
|
||||
|
||||
void print_node_info(const dht::DhtRunner& node, const dht_params& params) {
|
||||
std::cout << "OpenDHT node " << node.getNodeId() << " running on ";
|
||||
auto port4 = node.getBoundPort(AF_INET);
|
||||
auto port6 = node.getBoundPort(AF_INET6);
|
||||
if (port4 == port6)
|
||||
std::cout << "port " << port4 << std::endl;
|
||||
else
|
||||
std::cout << "IPv4 port " << port4 << ", IPv6 port " << port6 << std::endl;
|
||||
if (params.id.first)
|
||||
std::cout << "Public key ID " << node.getId() << std::endl;
|
||||
}
|
||||
|
||||
static const constexpr struct option long_options[] = {
|
||||
{"help", no_argument , nullptr, 'h'},
|
||||
{"port", required_argument, nullptr, 'p'},
|
||||
@@ -147,6 +205,7 @@ static const constexpr struct option long_options[] = {
|
||||
{"daemonize", no_argument , nullptr, 'd'},
|
||||
{"service", no_argument , nullptr, 's'},
|
||||
{"peer-discovery", no_argument , nullptr, 'D'},
|
||||
{"no-rate-limit", no_argument , nullptr, 'U'},
|
||||
{"persist", required_argument, nullptr, 'f'},
|
||||
{"logfile", required_argument, nullptr, 'l'},
|
||||
{"syslog", no_argument , nullptr, 'L'},
|
||||
@@ -168,7 +227,7 @@ parseArgs(int argc, char **argv) {
|
||||
int opt;
|
||||
std::string privkey;
|
||||
std::string proxy_privkey;
|
||||
while ((opt = getopt_long(argc, argv, "hidsvDp:n:b:f:l:", long_options, nullptr)) != -1) {
|
||||
while ((opt = getopt_long(argc, argv, "hidsvDUp:n:b:f:l:", long_options, nullptr)) != -1) {
|
||||
switch (opt) {
|
||||
case 'p': {
|
||||
int port_arg = atoi(optarg);
|
||||
@@ -212,6 +271,9 @@ parseArgs(int argc, char **argv) {
|
||||
case 'n':
|
||||
params.network = strtoul(optarg, nullptr, 0);
|
||||
break;
|
||||
case 'U':
|
||||
params.no_rate_limit = true;
|
||||
break;
|
||||
case 'b':
|
||||
params.bootstrap = dht::splitPort((optarg[0] == '=') ? optarg+1 : optarg);
|
||||
if (not params.bootstrap.first.empty() and params.bootstrap.second.empty()) {
|
||||
|
||||
Reference in new issue
Block a user