Compare commits

...
58 Commits
Author SHA1 Message Date
Adrien Béraud c63030b591 fix 2019-12-05 17:41:46 -05:00
Adrien Béraud b3ae25602f WIP fix 2019-12-05 17:31:46 -05:00
Adrien Béraud 16f64684ed http tester 2019-12-05 13:18:38 -05:00
Adrien Béraud 82938289e2 proxy client: avoid destroying twice 2019-12-05 13:18:36 -05:00
Adrien Béraud a671b57273 http/request: add constructor 2019-12-05 13:18:33 -05:00
Adrien Béraud 17fb92654e http: check parser before use 2019-12-03 16:45:19 -05:00
Adrien Béraud 294034f00f proxy client: don't report connectivity failure for aborted requests 2019-12-03 16:21:33 -05:00
Adrien Béraud 36857c1f3d http/request: add aborted flag and actual status code 2019-12-03 16:21:04 -05:00
Adrien Béraud 7e9f5303bd github: don't run distcheck 2019-12-02 01:30:56 -05:00
Adrien Béraud c2780b1ca9 build/autoconf: always build python Makefile 2019-12-02 01:15:38 -05:00
Adrien Béraud 16b798324d github: disable python 2019-12-02 00:53:27 -05:00
Adrien Béraud bc850bf00c build/autotools: use proper path for tools build include 2019-12-02 00:30:37 -05:00
Adrien Béraud 0d8d302128 update argon2 2019-12-02 00:11:08 -05:00
Adrien Béraud 97013badfa github: install argon2 before configure 2019-12-02 00:06:49 -05:00
Adrien Béraud 31185c24f0 build/autoconf: always build doc Makefile 2019-12-01 23:49:04 -05:00
Adrien Béraud 2f6f8e2ac1 cmake/tools: link with readline dependenciess 2019-12-01 22:46:41 -05:00
zhengfish 8f141951ce fixed a compiling error in openwrt-18.04.5
Signed-off-by: zhengfish <zhengfish@gmail.com>
2019-12-01 22:41:29 -05:00
zhengfish 43d111b7dc fixed a linking error in openwrt-18.06: undefined reference to 'tgetnum'
Signed-off-by: zhengfish <zhengfish@gmail.com>
2019-12-01 22:41:29 -05:00
Adrien Béraud 620ba2a89b proxy server: fix warning 2019-12-01 21:01:42 -05:00
Adrien Béraud d1598380db proxy server: fix build without push notifications 2019-12-01 21:00:27 -05:00
Adrien Béraud 69fe399db0 proxy server: cancelListen on destroy 2019-12-01 20:54:48 -05:00
Adrien Béraud a60dde2e13 tests: use shutdown 2019-12-01 19:41:02 -05:00
Adrien Béraud 201982b1a9 dhtrunner: don't count cancel as ops 2019-12-01 19:40:44 -05:00
Adrien Béraud 83f2b272a7 dhtrunner: add ongoing op count to node info 2019-12-01 15:07:35 -05:00
Adrien Béraud 7e47d0458a tests/proxy: cleanup 2019-12-01 14:53:50 -05:00
Adrien Béraud f6d26757fa dhtrunner: wait for current ops to complete on shutdown 2019-12-01 14:53:36 -05:00
Adrien Béraud 476d0bca4f proxy client: lock while canceling requests 2019-12-01 14:52:42 -05:00
Adrien Béraud 259986e786 network engine: don't throw in case of unknown transaction 2019-12-01 14:52:21 -05:00
Adrien Béraud 8ff0c979d4 proxy server: cleanup 2019-11-30 20:36:20 -05:00
Adrien Béraud 913cb543c8 crypto: improve exception types 2019-11-30 19:42:02 -05:00
Adrien Béraud b380c9ffba http: cleanup 2019-11-30 18:47:29 -05:00
Adrien Béraud 8c717085b4 cmake: cleanup 2019-11-30 18:11:00 -05:00
Adrien Béraud 48742a1058 tests: fix catching exception 2019-11-30 18:11:00 -05:00
Adrien Béraud fe11285a03 http: close on request end with Connection: close, cleanup 2019-11-30 18:11:00 -05:00
Adrien Béraud fb34770a54 http: reuse connection istream, cleanup 2019-11-30 17:21:52 -05:00
Adrien Béraud 4a909419e9 http: handle Request lifetime when sharing resolver/connection 2019-11-30 17:21:52 -05:00
Adrien Béraud dab7099369 proxy server: cleanup 2019-11-28 22:16:22 -05:00
Adrien Béraud 9c84e59bbf autotools: update to C++14 2019-11-27 12:12:29 -05:00
Adrien Béraud eae383b3ed autotools: remove unneeded macros 2019-11-26 12:46:26 -05:00
Adrien Béraud 28bf78408d network utils: fix some race conditions 2019-11-26 12:24:37 -05:00
Adrien Béraud 7ed6f74e6b proxy_server: add mutex to protect requests 2019-11-26 12:24:28 -05:00
Adrien Béraud cf6d9fd8c2 proxy client: add lock for requests 2019-11-25 15:43:23 -05:00
Adrien Béraud 5df34bb752 http: use atomic ids 2019-11-25 15:42:45 -05:00
Adrien Béraud bebb66490d tests: fix proxy tester 2019-11-25 13:44:55 -05:00
Adrien Béraud 5f361748eb Update ccpp.yml 2019-11-19 13:59:55 -05:00
Adrien Béraud 984745624c Update ccpp.yml 2019-11-19 13:58:46 -05:00
Adrien Béraud f9e860fdc8 docker/github: use deps base image 2019-11-19 13:52:40 -05:00
Adrien Béraud 06ea3d23e5 docker: add dockerfiles for Ubuntu 18.04 (Bionic) 2019-11-19 13:44:45 -05:00
Adrien Béraud f0857a5925 Update ccpp.yml 2019-11-19 11:05:00 -05:00
Adrien Béraud 5a993fb902 Create ccpp.yml 2019-11-19 11:02:54 -05:00
Adrien Béraud 8a18c591e9 http: initialize connection type, cleanup 2019-11-12 16:45:00 -05:00
Adrien Béraud 9daa7ec283 dhtrunner: default peer request rate to global/8 2019-11-11 12:58:11 -05:00
Adrien Béraud 01f2ad9ea4 tools: show connectivity change in log 2019-11-11 11:54:44 -05:00
Adrien Béraud 2dd4cde035 tools: factorize print_node_info 2019-11-08 16:52:27 -05:00
Adrien Béraud cb0005d0eb tools: factor configuration parsing 2019-11-08 16:12:04 -05:00
Adrien Béraud eca340b785 tools: add argument to disable rate limiting 2019-11-08 15:29:53 -05:00
Adrien Béraud cdd035b801 rate limiter: cleanup 2019-11-07 19:17:27 -05:00
Adrien Béraud cb7d7f6614 dht: allow to configure rate limiter 2019-11-07 16:43:04 -05:00
42 changed files with 1798 additions and 938 deletions

No files matched your search

+27
View File
@@ -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
View File
@@ -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}
-2
View File
@@ -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
View File
@@ -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
View File
@@ -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
+13
View File
@@ -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
+28
View File
@@ -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*
+14
View File
@@ -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};
};
/**
+10 -4
View File
@@ -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;
+5 -7
View File
@@ -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_;
+2
View File
@@ -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_;
+18 -7
View File
@@ -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
View File
@@ -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
+17 -7
View File
@@ -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 {};
+23 -7
View File
@@ -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:
+9 -3
View File
@@ -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
View File
@@ -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
]])
+3
View File
@@ -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
+3
View File
@@ -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
+5 -3
View File
@@ -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
View File
@@ -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 \
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+3 -3
View File
@@ -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
View File
@@ -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
+6 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
};
+4 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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()) {