diff --git a/.clang-format b/.clang-format
index 2fa1f67a..12d0e405 100644
--- a/.clang-format
+++ b/.clang-format
@@ -4,22 +4,19 @@ AlignConsecutiveAssignments: 'true'
AlignConsecutiveDeclarations: 'false'
AlignEscapedNewlines: Right
AlignOperands: 'false'
-AlignTrailingComments: 'true'
+AlignTrailingComments: 'false'
AllowAllParametersOfDeclarationOnNextLine: 'false'
AllowShortBlocksOnASingleLine: 'false'
AllowShortCaseLabelsOnASingleLine: 'true'
AllowShortFunctionsOnASingleLine: None
-AllowShortIfStatementsOnASingleLine: 'true'
AllowShortLoopsOnASingleLine: 'false'
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: 'false'
-AlwaysBreakTemplateDeclarations: 'true'
BinPackArguments: 'true'
BinPackParameters: 'true'
BreakAfterJavaFieldAnnotations: 'true'
BreakBeforeBinaryOperators: None
-BreakBeforeBraces: Stroustrup
-BreakBeforeInheritanceComma: 'false'
+BreakBeforeBraces: Allman
BreakBeforeTernaryOperators: 'false'
BreakConstructorInitializers: BeforeColon
BreakStringLiterals: 'false'
@@ -56,4 +53,4 @@ SpacesInParentheses: 'false'
SpacesInSquareBrackets: 'false'
Standard: Cpp11
TabWidth: '2'
-UseTab: Never
\ No newline at end of file
+UseTab: Never
diff --git a/.gitignore b/.gitignore
index 04985b7f..7518f4e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,5 @@
._.DS_Store
Thumbs.db
._*
-.idea
\ No newline at end of file
+.idea
+.vs/*
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3f183c8e..a151f1ea 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,17 +1,21 @@
-cmake_minimum_required(VERSION 2.8.6)
+cmake_minimum_required(VERSION 3.5)
PROJECT(Zano)
set(VERSION "1.0")
-# if(POLICY CMP0043)
-# cmake_policy(SET CMP0043 OLD)
-# endif()
+if(POLICY CMP0043)
+ cmake_policy(SET CMP0043 NEW)
+endif()
+if(POLICY CMP0043)
+ cmake_policy(SET CMP0074 NEW)
+endif()
+
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
-# if(POLICY CMP0020)
-# cmake_policy(SET CMP0020 OLD)
-# endif()
if(CMAKE_SYSTEM_NAME STREQUAL "iOS" OR CMAKE_SYSTEM_NAME STREQUAL "Android")
add_definitions(-DMOBILE_WALLET_BUILD)
@@ -50,9 +54,12 @@ message("Generated with config types: ${CMAKE_CONFIGURATION_TYPES}, and built ty
enable_testing()
+set(OPENSSL_USE_STATIC_LIBS TRUE) # link statically
+find_package(OpenSSL REQUIRED)
+
if(APPLE)
- set(CMAKE_OSX_DEPLOYMENT_TARGET 10.10.5)
+ set(CMAKE_OSX_DEPLOYMENT_TARGET 10.12)
endif()
set(USE_PCH FALSE CACHE BOOL "Use shared precompiled headers")
@@ -60,7 +67,7 @@ set(DISABLE_TOR FALSE CACHE BOOL "Disable TOR library(and related tor-connect su
set(TESTNET FALSE CACHE BOOL "Compile for testnet")
set(BUILD_GUI FALSE CACHE BOOL "Build qt-daemon")
-include_directories(src contrib/eos_portable_archive contrib contrib/epee/include "${CMAKE_BINARY_DIR}/version" "${CMAKE_BINARY_DIR}/contrib/zlib")
+include_directories(src contrib/eos_portable_archive contrib contrib/epee/include ${OPENSSL_INCLUDE_DIR} "${CMAKE_BINARY_DIR}/version" "${CMAKE_BINARY_DIR}/contrib/zlib")
add_definitions(-DSTATICLIB)
@@ -69,6 +76,11 @@ if(TESTNET)
add_definitions(-DTESTNET)
endif()
+if(CAKEWALLET)
+ message("NOTICE: Building libraries for CAKEWALLET")
+ add_definitions(-DCAKEWALLET)
+endif()
+
set(OPENSSL_USE_STATIC_LIBS TRUE) # link statically
find_package(OpenSSL REQUIRED)
@@ -90,9 +102,9 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
endif()
if(MSVC)
- add_definitions("/D_CRT_SECURE_NO_WARNINGS /D_WIN32_WINNT=0x0600 /DWIN32_LEAN_AND_MEAN /DGTEST_HAS_TR1_TUPLE=0 /D__SSE4_1__")
+ add_definitions("/D_CRT_SECURE_NO_WARNINGS /D_WIN32_WINNT=0x0600 /DWIN32_LEAN_AND_MEAN /DGTEST_HAS_TR1_TUPLE=0")
add_compile_options(/bigobj /Zm1000 /Z7 /MP2 /W3 /GS- /wd4996 /wd4503 /wd4345 /wd4091 /FIinline_c.h)
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:10485760 /DEBUG dbghelp.lib")
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:10485760 /DEBUG dbghelp.lib crypt32.lib")
if(STATIC)
foreach(VAR CMAKE_C_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELEASE)
string(REPLACE "/MD" "/MT" ${VAR} "${${VAR}}")
@@ -106,7 +118,7 @@ else()
else()
set(ARCH_FLAG "-march=${ARCH}")
endif()
- set(WARNINGS "-Wall -Wextra -Wpointer-arith -Wvla -Wwrite-strings -Wno-error=extra -Wno-error=deprecated-declarations -Wno-error=sign-compare -Wno-error=strict-aliasing -Wno-error=type-limits -Wno-unused-parameter -Wno-error=unused-variable -Wno-aggregate-return")
+ set(WARNINGS "-Wall -Wextra -Wpointer-arith -Wvla -Wwrite-strings -Wno-error=extra -Wno-error=deprecated-declarations -Wno-error=sign-compare -Wno-error=strict-aliasing -Wno-error=type-limits -Wno-unused-parameter -Wno-error=unused-variable -Wno-aggregate-return -Wno-comment -Wno-unknown-pragmas -Wno-pragmas")
# if(NOT APPLE)
# set(WARNINGS "${WARNINGS} -Werror")
# endif()
@@ -138,16 +150,16 @@ else()
else()
set(APPLE_FLAG "")
endif()
- set(C_WARNINGS "-Waggregate-return -Wnested-externs -Wstrict-prototypes")
+ set(C_WARNINGS "-Waggregate-return -Wnested-externs -Wstrict-prototypes -Wno-comment")
set(CXX_WARNINGS "-Wno-reorder -Wno-missing-field-initializers")
- try_compile(STATIC_ASSERT_RES "${CMAKE_CURRENT_BINARY_DIR}/static-assert" "${CMAKE_CURRENT_SOURCE_DIR}/utils/test-static-assert.c" COMPILE_DEFINITIONS "-std=c11")
+ try_compile(STATIC_ASSERT_RES "${CMAKE_CURRENT_BINARY_DIR}/static-assert" "${CMAKE_CURRENT_SOURCE_DIR}/utils/test-static-assert.c" COMPILE_DEFINITIONS "-std=c++14")
if(STATIC_ASSERT_RES)
set(STATIC_ASSERT_FLAG "")
else()
set(STATIC_ASSERT_FLAG "-Dstatic_assert=_Static_assert")
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -D_GNU_SOURCE ${MINGW_FLAG} ${STATIC_ASSERT_FLAG} ${WARNINGS} ${C_WARNINGS} ${ARCH_FLAG}")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive -ftemplate-depth-1024 -std=c++11 -D_GNU_SOURCE ${APPLE_FLAG} ${MINGW_FLAG} ${WARNINGS} ${CXX_WARNINGS} ${ARCH_FLAG}")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive -ftemplate-depth-1024 -std=c++14 -D_GNU_SOURCE ${APPLE_FLAG} ${MINGW_FLAG} ${WARNINGS} ${CXX_WARNINGS} ${ARCH_FLAG}")
if (NOT APPLE AND NOT MSVC)
if (CLANG)
set(LLVM_USE_LINKER "gold")
@@ -182,6 +194,11 @@ else()
endif()
+# always use local Boost installation rather than the system-wide (unless ZANO_USE_SYSTEM_BOOST is defined for some reason, which is not recommended)
+if(NOT DEFINED ENV{ZANO_USE_SYSTEM_BOOST})
+ set(Boost_NO_SYSTEM_PATHS ON)
+endif()
+
if(MSVC)
set(Boost_USE_STATIC_LIBS ON)
endif()
@@ -204,8 +221,10 @@ if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Android")
set(Boost_LIBRARY_DIRS "${Boost_LIBRARY_DIRS}/${CMAKE_ANDROID_ARCH_ABI}/")
set(Boost_LIBRARIES "${Boost_LIBRARY_DIRS}libboost_system.a;${Boost_LIBRARY_DIRS}libboost_filesystem.a;${Boost_LIBRARY_DIRS}libboost_thread.a;${Boost_LIBRARY_DIRS}libboost_timer.a;${Boost_LIBRARY_DIRS}libboost_date_time.a;${Boost_LIBRARY_DIRS}libboost_chrono.a;${Boost_LIBRARY_DIRS}libboost_regex.a;${Boost_LIBRARY_DIRS}libboost_serialization.a;${Boost_LIBRARY_DIRS}libboost_atomic.a;${Boost_LIBRARY_DIRS}libboost_program_options.a")
+elseif(APPLE)
+ find_package(Boost 1.71 REQUIRED COMPONENTS system filesystem thread timer date_time chrono regex serialization atomic program_options locale)
else()
- find_package(Boost 1.70 REQUIRED COMPONENTS system filesystem thread timer date_time chrono regex serialization atomic program_options locale)
+ find_package(Boost 1.70 REQUIRED COMPONENTS system filesystem thread timer date_time chrono regex serialization atomic program_options locale log)
endif()
@@ -227,7 +246,7 @@ elseif(NOT MSVC)
endif()
if(BUILD_GUI)
- cmake_minimum_required(VERSION 2.8.11)
+ cmake_minimum_required(VERSION 3.1)
find_package(Qt5Widgets REQUIRED)
endif()
diff --git a/README.md b/README.md
index 13558e5f..1b661e0a 100644
--- a/README.md
+++ b/README.md
@@ -13,13 +13,13 @@ Be sure to clone the repository properly:\
### Dependencies
| component / version | minimum
(not recommended but may work) | recommended | most recent of what we have ever tested |
|--|--|--|--|
-| gcc (Linux) | 5.4.0 | 7.5.0 | 8.3.0 |
+| gcc (Linux) | 5.4.0 | 9.4.0 | 12.3.0 |
| llvm/clang (Linux) | UNKNOWN | 7.0.1 | 8.0.0 |
-| [MSVC](https://visualstudio.microsoft.com/downloads/) (Windows) | 2015 (14.0 update 1) | 2017 (15.9.0) | 2022 (17.4.2) |
-| [XCode](https://developer.apple.com/downloads/) (macOS) | 9.2 | 12.3 | 12.3 |
-| [CMake](https://cmake.org/download/) | 2.8.6 | 3.15.5 | 3.26.3 |
+| [MSVC](https://visualstudio.microsoft.com/downloads/) (Windows) | 2017 (15.9.30) | 2017 (15.9.30) | 2022 (17.7.5) |
+| [XCode](https://developer.apple.com/downloads/) (macOS) | 12.3 | 14.3 | 14.3 |
+| [CMake](https://cmake.org/download/) | 3.15.5 | 3.22.1 | 3.26.3 |
| [Boost](https://www.boost.org/users/download/) | 1.70 | 1.70 | 1.76 |
-| [OpenSSL](https://www.openssl.org/source/) [(win)](https://slproweb.com/products/Win32OpenSSL.html) | - | 1.1.1n | 1.1.1n |
+| [OpenSSL](https://www.openssl.org/source/) [(win)](https://slproweb.com/products/Win32OpenSSL.html) | 1.1.1n | 1.1.1w | 1.1.1w |
| [Qt](https://download.qt.io/archive/qt/) (*only for GUI*) | 5.8.0 | 5.11.2 | 5.15.2 |
Note:\
@@ -30,27 +30,37 @@ Note:\
### Linux
-Recommended OS version: Ubuntu 18.04 LTS.
+Recommended OS versions: Ubuntu 20.04, 22.04 LTS.
1. Prerequisites
[*server version*]
- sudo apt-get install -y build-essential g++ python-dev autotools-dev libicu-dev libbz2-dev cmake git screen checkinstall zlib1g-dev
+ sudo apt-get install -y build-essential g++ curl autotools-dev libicu-dev libbz2-dev cmake git screen checkinstall zlib1g-dev
[*GUI version*]
sudo apt-get install -y build-essential g++ python-dev autotools-dev libicu-dev libbz2-dev cmake git screen checkinstall zlib1g-dev mesa-common-dev libglu1-mesa-dev
-2. Download and build Boost
+2. Clone Zano into a local folder\
+ (If for some reason you need to use alternative Zano branch, change 'master' to the required branch name.)
+
+ git clone --recursive https://github.com/hyle-team/zano.git -b master
+
+ In the following steps we assume that you cloned Zano into '~/zano' folder in your home directory.
+
+3. Download and build Boost\
+ (Assuming you have cloned Zano into the 'zano' folder. If you used a different location for Zano, **edit line 4** accordingly.)
curl -OL https://boostorg.jfrog.io/artifactory/main/release/1.70.0/source/boost_1_70_0.tar.bz2
echo "430ae8354789de4fd19ee52f3b1f739e1fba576f0aded0897c3c2bc00fb38778 boost_1_70_0.tar.bz2" | shasum -c && tar -xjf boost_1_70_0.tar.bz2
- cd boost_1_70_0
+ rm boost_1_70_0.tar.bz2 && cd boost_1_70_0
+ patch -p0 < ../zano/utils/boost_1.70_gcc_8.patch || cd ..
./bootstrap.sh --with-libraries=system,filesystem,thread,date_time,chrono,regex,serialization,atomic,program_options,locale,timer,log
- ./b2
+ ./b2 && cd ..
+ Make sure that you see "The Boost C++ Libraries were successfully built!" message at the end.
-3. Install Qt\
+4. Install Qt\
(*GUI version only, skip this step if you're building server version*)
[*GUI version*]
@@ -61,20 +71,19 @@ Recommended OS version: Ubuntu 18.04 LTS.
Then follow the instructions in Wizard. Don't forget to tick the WebEngine module checkbox!
-4. Install OpenSSL
+5. Install OpenSSL
- We recommend installing OpenSSL v1.1.1n locally unless you would like to use the same version system-wide. Adjust the local path `/home/user/openssl` in the commands below according to your needs.
+ We recommend installing OpenSSL v1.1.1w locally unless you would like to use the same version system-wide.\
+ (Assuming that `$HOME` environment variable is set to your home directory. Otherwise, edit line 4 accordingly.)
- curl -OL https://www.openssl.org/source/openssl-1.1.1n.tar.gz
- echo "40dceb51a4f6a5275bde0e6bf20ef4b91bfc32ed57c0552e2e8e15463372b17a openssl-1.1.1n.tar.gz" | shasum -c && tar xaf openssl-1.1.1n.tar.gz
- cd openssl-1.1.1n/
- ./config --prefix=/home/user/openssl --openssldir=/home/user/openssl shared zlib
- make
- make test
- make install
+ curl -OL https://www.openssl.org/source/openssl-1.1.1w.tar.gz
+ echo "cf3098950cb4d853ad95c0841f1f9c6d3dc102dccfcacd521d93925208b76ac8 openssl-1.1.1w.tar.gz" | shasum -c && tar xaf openssl-1.1.1w.tar.gz
+ cd openssl-1.1.1w/
+ ./config --prefix=$HOME/openssl --openssldir=$HOME/openssl shared zlib
+ make && make test && make install && cd ..
-5. Set environment variables properly\
+6. [*OPTIONAL*] Set global environment variables for convenient use\
For instance, by adding the following lines to `~/.bashrc`
[*server version*]
@@ -89,18 +98,27 @@ For instance, by adding the following lines to `~/.bashrc`
export OPENSSL_ROOT_DIR=/home/user/openssl
export QT_PREFIX_PATH=/home/user/Qt5.11.2/5.11.2/gcc_64
-
-
-6. Build the binaries
- 1. Build daemon and simplewallet:
-
- cd zano/ && make -j1
- or
+ **NOTICE: Please edit the lines above according to your actual paths.**
+ **NOTICE 2:** Make sure you've restarted your terminal session (by reopening the terminal window or reconnecting the server) to apply these changes.
+
+8. Build the binaries
+ 1. If you skipped step 6 and did not set the environment variables:
+
+ cd zano && mkdir build && cd build
+ BOOST_ROOT=$HOME/boost_1_70_0 OPENSSL_ROOT_DIR=$HOME/openssl cmake ..
+ make -j1 daemon simplewallet
+
+ 2. If you set the variables in step 6:
+
cd zano && mkdir build && cd build
cmake ..
make -j1 daemon simplewallet
+ or simply:
+
+ cd zano && make -j1
+
**NOTICE**: If you are building on a machine with a relatively high amount of RAM or with the proper setting of virtual memory, then you can use `-j2` or `-j` option to speed up the building process. Use with caution.
**NOTICE 2**: If you'd like to build binaries for the testnet, use `cmake -D TESTNET=TRUE ..` instead of `cmake ..` .
@@ -110,12 +128,12 @@ For instance, by adding the following lines to `~/.bashrc`
cd zano
utils/build_script_linux.sh
-7. Look for the binaries in `build` folder
+ Look for the binaries in `build` folder
### Windows
-Recommended OS version: Windows 7 x64.
+Recommended OS version: Windows 7 x64, Windows 11 x64.
1. Install required prerequisites (Boost, Qt, CMake, OpenSSL).
2. Edit paths in `utils/configure_local_paths.cmd`.
3. Run one of `utils/configure_win64_msvsNNNN_gui.cmd` according to your MSVC version.
diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt
index 601672d5..a3220829 100644
--- a/contrib/CMakeLists.txt
+++ b/contrib/CMakeLists.txt
@@ -28,7 +28,7 @@ endif()
set_property(TARGET upnpc-static mdbx_chk mdbx_copy mdbx_dump mdbx_load mdbx_stat PROPERTY FOLDER "unused")
if(MSVC)
- set_property(TARGET ntdll_extra_target PROPERTY FOLDER "unused")
+ #set_property(TARGET ntdll_extra_target PROPERTY FOLDER "unused")
endif()
diff --git a/contrib/db/libmdbx/src/CMakeLists.txt b/contrib/db/libmdbx/src/CMakeLists.txt
index 04aead5f..7dfc94aa 100644
--- a/contrib/db/libmdbx/src/CMakeLists.txt
+++ b/contrib/db/libmdbx/src/CMakeLists.txt
@@ -45,61 +45,6 @@ if(CC_HAS_VISIBILITY AND (LTO_ENABLED OR INTERPROCEDURAL_OPTIMIZATION))
set_target_properties(mdbx PROPERTIES LINK_FLAGS "-fvisibility=hidden")
endif()
-if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
- if(MSVC)
- if(NOT MSVC_LIB_EXE)
- # Find lib.exe
- get_filename_component(CL_NAME ${CMAKE_C_COMPILER} NAME)
- string(REPLACE cl.exe lib.exe MSVC_LIB_EXE ${CL_NAME})
- find_program(MSVC_LIB_EXE ${MSVC_LIB_EXE})
- endif()
- if(MSVC_LIB_EXE)
- message(STATUS "Found MSVC's lib tool: ${MSVC_LIB_EXE}")
- set(MDBX_NTDLL_EXTRA_IMPLIB ${CMAKE_CURRENT_BINARY_DIR}/mdbx_ntdll_extra.lib)
- add_custom_command(OUTPUT ${MDBX_NTDLL_EXTRA_IMPLIB}
- COMMENT "Create extra-import-library for ntdll.dll"
- MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/elements/ntdll.def"
- COMMAND ${MSVC_LIB_EXE} /def:"${CMAKE_CURRENT_SOURCE_DIR}/elements/ntdll.def" /out:"${MDBX_NTDLL_EXTRA_IMPLIB}" ${INITIAL_CMAKE_STATIC_LINKER_FLAGS})
- else()
- message(SEND_ERROR "MSVC's lib tool not found")
- endif()
- elseif(MINGW OR MINGW64)
- if(NOT DLLTOOL)
- # Find dlltool
- get_filename_component(GCC_NAME ${CMAKE_C_COMPILER} NAME)
- string(REPLACE gcc dlltool DLLTOOL_NAME ${GCC_NAME})
- find_program(DLLTOOL NAMES ${DLLTOOL_NAME})
- endif()
- if(DLLTOOL)
- message(STATUS "Found dlltool: ${DLLTOOL}")
- set(MDBX_NTDLL_EXTRA_IMPLIB "${CMAKE_CURRENT_BINARY_DIR}/mdbx_ntdll_extra.a")
- add_custom_command(OUTPUT ${MDBX_NTDLL_EXTRA_IMPLIB}
- COMMENT "Create extra-import-library for ntdll.dll"
- MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/elements/ntdll.def"
- COMMAND ${DLLTOOL} -d "${CMAKE_CURRENT_SOURCE_DIR}/elements/ntdll.def" -l "${MDBX_NTDLL_EXTRA_IMPLIB}")
- else()
- message(SEND_ERROR "dlltool not found")
- endif()
- endif()
-endif()
-
-target_link_libraries(mdbx ${MDBX_LIBDEP_MODE} ${CMAKE_THREAD_LIBS_INIT})
-if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
- target_link_libraries(mdbx ${MDBX_LIBDEP_MODE} ntdll.lib)
- if(MDBX_NTDLL_EXTRA_IMPLIB)
- # LY: Sometimes Cmake requires a nightmarish magic for simple things.
- # 1) create a target out of the library compilation result
- add_custom_target(ntdll_extra_target DEPENDS ${MDBX_NTDLL_EXTRA_IMPLIB})
- # 2) create an library target out of the library compilation result
- add_library(ntdll_extra STATIC IMPORTED GLOBAL)
- add_dependencies(ntdll_extra ntdll_extra_target)
- # 3) specify where the library is (and where to find the headers)
- set_target_properties(ntdll_extra
- PROPERTIES
- IMPORTED_LOCATION ${MDBX_NTDLL_EXTRA_IMPLIB})
- target_link_libraries(mdbx ${MDBX_LIBDEP_MODE} ntdll_extra)
- endif()
-endif()
set_target_properties(mdbx PROPERTIES
INTERPROCEDURAL_OPTIMIZATION $
diff --git a/contrib/db/libmdbx/src/elements/internals.h b/contrib/db/libmdbx/src/elements/internals.h
index 6aba3740..48ac8f41 100644
--- a/contrib/db/libmdbx/src/elements/internals.h
+++ b/contrib/db/libmdbx/src/elements/internals.h
@@ -54,7 +54,7 @@
#endif
#if MDBX_DISABLE_GNU_SOURCE
#undef _GNU_SOURCE
-#elif defined(__linux__) || defined(__gnu_linux__)
+#elif ( defined(__linux__) || defined(__gnu_linux__) ) && !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
diff --git a/contrib/epee/include/misc_helpers.h b/contrib/epee/include/misc_helpers.h
index dd3067c8..58ddc3cd 100644
--- a/contrib/epee/include/misc_helpers.h
+++ b/contrib/epee/include/misc_helpers.h
@@ -54,18 +54,19 @@
catch(const std::exception& ex) \
{ \
(void)(ex); \
- LOG_ERROR("Exception at [" << location << "], what=" << ex.what()); \
custom_code; \
+ LOG_ERROR("Exception at [" << location << "], what=" << ex.what()); \
return return_val; \
} \
catch(...) \
{ \
- LOG_ERROR("Exception at [" << location << "], generic exception \"...\""); \
custom_code; \
+ LOG_ERROR("Exception at [" << location << "], generic exception \"...\""); \
return return_val; \
}
#define CATCH_ENTRY(location, return_val) CATCH_ENTRY_CUSTOM(location, (void)0, return_val)
#define CATCH_ENTRY2(return_val) CATCH_ENTRY_CUSTOM(LOCATION_SS, (void)0, return_val)
+#define CATCH_ENTRY_CUSTOM2(custom_code, return_val) CATCH_ENTRY_CUSTOM(LOCATION_SS, custom_code, return_val)
#define CATCH_ENTRY_L0(location, return_val) CATCH_ENTRY(location, return_val)
#define CATCH_ENTRY_L1(location, return_val) CATCH_ENTRY(location, return_val)
diff --git a/contrib/epee/include/misc_language.h b/contrib/epee/include/misc_language.h
index 1389ba03..5855d977 100644
--- a/contrib/epee/include/misc_language.h
+++ b/contrib/epee/include/misc_language.h
@@ -34,6 +34,7 @@
#include
#include
#include
+#include
#include "include_base_utils.h"
#include "auto_val_init.h"
@@ -273,6 +274,21 @@ namespace misc_utils
}
}
+ uint64_t get_avg() const
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ if (!queued_items.size())
+ return 0;
+
+ uint64_t summ = 0;
+ for (const auto& item : queued_items)
+ {
+ summ += *item.first;
+ }
+
+ return summ / queued_items.size();
+ }
+
template
friend std::ostream & operator<< (std::ostream &out, median_helper const &mh);
}; // class median_helper
@@ -291,26 +307,25 @@ namespace misc_utils
/************************************************************************/
/* */
/************************************************************************/
- template
- type_vec_type median(std::vector &v)
+ template
+ typename container_t::value_type median(container_t &v)
{
- //CRITICAL_REGION_LOCAL(m_lock);
+ typename container_t::value_type median{};
if(v.empty())
- return boost::value_initialized();
+ return median;
if(v.size() == 1)
return v[0];
- size_t n = (v.size()) / 2;
- std::sort(v.begin(), v.end());
- //nth_element(v.begin(), v.begin()+n-1, v.end());
- if(v.size()%2)
- {//1, 3, 5...
- return v[n];
- }else
- {//2, 4, 6...
- return (v[n-1] + v[n])/2;
+ auto median_it = v.begin() + v.size() / 2;
+ std::nth_element(v.begin(), median_it, v.end());
+ median = *median_it;
+ if (v.size() % 2 == 0)
+ {
+ auto max_it = std::max_element(v.begin(), median_it); // it's garanteed that after nth_element() the necessary element is in this interval
+ median = (median + *max_it) / 2; // average of [size/2-1] and [size/2] elements
}
+ return median;
}
/************************************************************************/
@@ -372,6 +387,11 @@ namespace misc_utils
virtual void do_call(){};
};
+ template
+ struct call_basic_param
+ {
+ virtual void do_call(param_t& p) {};
+ };
template
struct call_specific: public call_basic
@@ -386,12 +406,34 @@ namespace misc_utils
t_callback m_cb;
};
+ template
+ struct call_specific_param : public call_basic_param
+ {
+ call_specific_param(t_callback cb) :m_cb(cb)
+ {}
+ virtual void do_call(const param_t& p)
+ {
+ m_cb(p);
+ }
+ private:
+ t_callback m_cb;
+ };
+
+
+
template
auto build_abstract_callback(t_callback cb) -> std::shared_ptr
{
return std::shared_ptr(new call_specific(cb));
}
+
+ template
+ auto build_abstract_callback_param(t_callback cb) -> std::shared_ptr>
+ {
+ return std::shared_ptr>(new call_specific_param(cb));
+ }
+
template
@@ -427,6 +469,71 @@ namespace misc_utils
return res.first;
}
+
+ class events_dispatcher
+ {
+
+ public:
+
+ template
+ struct callback_entry
+ {
+ std::shared_ptr > m_cb;
+ };
+
+ std::map m_callbacks;
+
+ template
+ void SUBSCIRBE_DEBUG_EVENT(callback_t cb)
+ {
+ std::type_index ti = typeid(param_t);
+ auto it = m_callbacks.find(ti);
+ if (it != m_callbacks.end())
+ {
+ throw std::runtime_error("Handler for this type already registered");
+ }
+
+ callback_entry cb_entry = { epee::misc_utils::build_abstract_callback_param(cb) };
+
+ m_callbacks[ti] = cb_entry;
+ }
+
+ template
+ void UNSUBSCRIBE_DEBUG_EVENT()
+ {
+ std::type_index ti = typeid(param_t);
+ auto it = m_callbacks.find(ti);
+ if (it != m_callbacks.end())
+ {
+ m_callbacks.erase(it);
+ }
+ }
+
+ void UNSUBSCRIBE_ALL()
+ {
+ m_callbacks.clear();
+ }
+
+ template
+ void RAISE_DEBUG_EVENT(const param_t& p)
+ {
+ std::type_index ti = typeid(param_t);
+ auto it = m_callbacks.find(ti);
+ if (it != m_callbacks.end())
+ {
+ callback_entry* pcallback_entry = boost::any_cast>(&it->second);
+ if (!pcallback_entry)
+ {
+ throw std::runtime_error("Unexpected error: registered tipe holding something else in boost::eny");
+ }
+ pcallback_entry->m_cb->do_call(p);
+ }
+ }
+
+ };
+
+
+
} // namespace misc_utils
} // namespace epee
diff --git a/contrib/epee/include/misc_log_ex.h b/contrib/epee/include/misc_log_ex.h
index 0b957a5f..318237c9 100644
--- a/contrib/epee/include/misc_log_ex.h
+++ b/contrib/epee/include/misc_log_ex.h
@@ -116,29 +116,29 @@ DISABLE_VS_WARNINGS(4100)
#define ENABLE_CHANNEL_BY_DEFAULT(ch_name) \
static bool COMBINE(init_channel, __LINE__) UNUSED_ATTRIBUTE = epee::misc_utils::static_initializer([](){ \
- epee::log_space::log_singletone::enable_channel(ch_name); return true; \
+ epee::log_space::log_singletone::enable_channel(ch_name, false); return true; \
});
#if defined(ENABLE_LOGGING_INTERNAL)
-#define LOG_PRINT_CHANNEL_NO_PREFIX2(log_channel, log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() && epee::log_space::log_singletone::channel_enabled(log_channel))\
+#define LOG_PRINT_CHANNEL_NO_PREFIX2(log_channel, log_name, x, y) {if ( (y) <= epee::log_space::log_singletone::get_log_detalisation_level() && epee::log_space::log_singletone::channel_enabled(log_channel))\
{TRY_ENTRY();std::stringstream ss________; ss________ << x << std::endl; epee::log_space::log_singletone::do_log_message(ss________.str() , y, epee::log_space::console_color_default, false, log_name);CATCH_ALL_DO_NOTHING();}}
-#define LOG_PRINT_CHANNEL_NO_PREFIX_NO_POSTFIX2(log_channel, log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() && epee::log_space::log_singletone::channel_enabled(log_channel))\
+#define LOG_PRINT_CHANNEL_NO_PREFIX_NO_POSTFIX2(log_channel, log_name, x, y) {if ( (y) <= epee::log_space::log_singletone::get_log_detalisation_level() && epee::log_space::log_singletone::channel_enabled(log_channel))\
{TRY_ENTRY();std::stringstream ss________; ss________ << x; epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, false, log_name);CATCH_ALL_DO_NOTHING();}}
-#define LOG_PRINT_CHANNEL_NO_POSTFIX2(log_channel, log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() && epee::log_space::log_singletone::channel_enabled(log_channel))\
+#define LOG_PRINT_CHANNEL_NO_POSTFIX2(log_channel, log_name, x, y) {if ( (y) <= epee::log_space::log_singletone::get_log_detalisation_level() && epee::log_space::log_singletone::channel_enabled(log_channel))\
{TRY_ENTRY();std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x; epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, false, log_name);CATCH_ALL_DO_NOTHING();}}
-#define LOG_PRINT_CHANNEL2_CB(log_channel, log_name, x, y, cb) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() && epee::log_space::log_singletone::channel_enabled(log_channel))\
+#define LOG_PRINT_CHANNEL2_CB(log_channel, log_name, x, y, cb) {if ( (y) <= epee::log_space::log_singletone::get_log_detalisation_level() && epee::log_space::log_singletone::channel_enabled(log_channel))\
{TRY_ENTRY();std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x << std::endl;epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, false, log_name);cb(ss________.str());CATCH_ALL_DO_NOTHING();}}
-#define LOG_PRINT_CHANNEL_COLOR2_CB(log_channel, log_name, x, y, color, cb) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() && epee::log_space::log_singletone::channel_enabled(log_channel))\
+#define LOG_PRINT_CHANNEL_COLOR2_CB(log_channel, log_name, x, y, color, cb) {if ( (y) <= epee::log_space::log_singletone::get_log_detalisation_level() && epee::log_space::log_singletone::channel_enabled(log_channel))\
{TRY_ENTRY();std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x << std::endl;epee::log_space::log_singletone::do_log_message(ss________.str(), y, color, false, log_name); cb(ss________.str());CATCH_ALL_DO_NOTHING();}}
-#define LOG_PRINT_CHANNEL_2_JORNAL(log_channel, log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() && epee::log_space::log_singletone::channel_enabled(log_channel))\
+#define LOG_PRINT_CHANNEL_2_JORNAL(log_channel, log_name, x, y) {if ( (y) <= epee::log_space::log_singletone::get_log_detalisation_level() && epee::log_space::log_singletone::channel_enabled(log_channel))\
{TRY_ENTRY();std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x << std::endl;epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, true, log_name);CATCH_ALL_DO_NOTHING();}}
#define LOG_ERROR2_CB(log_name, x, cb) { \
@@ -556,8 +556,11 @@ namespace log_space
else
m_have_to_kill_console = false;
- ::AllocConsole();
- freopen("CONOUT$", "w", stdout);
+ if (m_have_to_kill_console)
+ {
+ ::AllocConsole();
+ freopen("CONOUT$", "w", stdout);
+ }
std::cout.clear();
#endif
}
@@ -1236,21 +1239,24 @@ namespace log_space
return genabled_channels.find(ch_name) != genabled_channels.end();
}
- static void enable_channels(const std::string& channels_set)
+ static void enable_channels(const std::string& channels_set, bool verbose = true)
{
std::set& genabled_channels = get_enabled_channels();
std::list list_of_channels;
boost::split(list_of_channels, channels_set, boost::is_any_of(",;: "), boost::token_compress_on);
- std::cout << "log channels: ";
+ if (verbose)
+ std::cout << "log channels: ";
for (const auto& ch : list_of_channels)
{
genabled_channels.insert(ch);
- std::cout << ch << " ";
+ if (verbose)
+ std::cout << ch << " ";
}
- std::cout << " enabled" << std::endl;
+ if (verbose)
+ std::cout << " enabled" << std::endl;
}
- static void enable_channel(const std::string& ch_name)
+ static void enable_channel(const std::string& ch_name, bool verbose = true)
{
std::set& genabled_channels = get_enabled_channels();
//lazy synchronization: just replace with modified copy of whole set
@@ -1258,7 +1264,8 @@ namespace log_space
enabled_channels_local.insert(ch_name);
genabled_channels.swap(enabled_channels_local);
#ifndef ANDROID_BUILD
- std::cout << "log channel '" << ch_name << "' enabled" << std::endl;
+ if (verbose)
+ std::cout << "log channel '" << ch_name << "' enabled" << std::endl;
#endif
}
diff --git a/contrib/epee/include/net/http_client.h b/contrib/epee/include/net/http_client.h
index c0b57596..b0c8838a 100644
--- a/contrib/epee/include/net/http_client.h
+++ b/contrib/epee/include/net/http_client.h
@@ -57,987 +57,1048 @@ extern epee::critical_section gregexp_lock;
namespace epee
{
-namespace net_utils
-{
-
-using namespace std;
-
- /*struct url
- {
- public:
- void parse(const std::string& url_s)
- {
- const string prot_end("://");
- string::const_iterator prot_i = search(url_s.begin(), url_s.end(),
- prot_end.begin(), prot_end.end());
- protocol_.reserve(distance(url_s.begin(), prot_i));
- transform(url_s.begin(), prot_i,
- back_inserter(protocol_),
- ptr_fun(tolower)); // protocol is icase
- if( prot_i == url_s.end() )
- return;
- advance(prot_i, prot_end.length());
- string::const_iterator path_i = find(prot_i, url_s.end(), '/');
- host_.reserve(distance(prot_i, path_i));
- transform(prot_i, path_i,
- back_inserter(host_),
- ptr_fun(tolower)); // host is icase
- string::const_iterator query_i = find(path_i, url_s.end(), '?');
- path_.assign(path_i, query_i);
- if( query_i != url_s.end() )
- ++query_i;
- query_.assign(query_i, url_s.end());
- }
-
- std::string protocol_;
- std::string host_;
- std::string path_;
- std::string query_;
- };*/
-
-
-
-
- //---------------------------------------------------------------------------
- static inline const char* get_hex_vals()
- {
- static char hexVals[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
- return hexVals;
- }
-
- static inline const char* get_unsave_chars()
- {
- //static char unsave_chars[] = "\"<>%\\^[]`+$,@:;/!#?=&";
- static char unsave_chars[] = "\"<>%\\^[]`+$,@:;!#&";
- return unsave_chars;
- }
-
- static inline bool is_unsafe(unsigned char compare_char)
- {
- if(compare_char <= 32 || compare_char >= 123)
- return true;
-
- const char* punsave = get_unsave_chars();
-
- for(int ichar_pos = 0; 0!=punsave[ichar_pos] ;ichar_pos++)
- if(compare_char == punsave[ichar_pos])
- return true;
-
- return false;
- }
-
- static inline
- std::string dec_to_hex(char num, int radix)
- {
- int temp=0;
- std::string csTmp;
- int num_char;
-
- num_char = (int) num;
- if (num_char < 0)
- num_char = 256 + num_char;
-
- while (num_char >= radix)
- {
- temp = num_char % radix;
- num_char = (int)floor((float)num_char / (float)radix);
- csTmp = get_hex_vals()[temp];
- }
-
- csTmp += get_hex_vals()[num_char];
-
- if(csTmp.size() < 2)
- {
- csTmp += '0';
- }
-
- std::reverse(csTmp.begin(), csTmp.end());
- //_mbsrev((unsigned char*)csTmp.data());
-
- return csTmp;
- }
-
- static inline std::string convert(char val)
- {
- std::string csRet;
- csRet += "%";
- csRet += dec_to_hex(val, 16);
- return csRet;
- }
- static inline std::string conver_to_url_format(const std::string& uri)
- {
-
- std::string result;
-
- for(size_t i = 0; i!= uri.size(); i++)
- {
- if(is_unsafe(uri[i]))
- result += convert(uri[i]);
- else
- result += uri[i];
-
- }
-
- return result;
- }
-
- static inline std::string convert_to_url_format_force_all(const std::string& uri)
+ namespace net_utils
{
- std::string result;
+ using namespace std;
- for(size_t i = 0; i!= uri.size(); i++)
+ /*struct url
{
- result += convert(uri[i]);
+ public:
+ void parse(const std::string& url_s)
+ {
+ const string prot_end("://");
+ string::const_iterator prot_i = search(url_s.begin(), url_s.end(),
+ prot_end.begin(), prot_end.end());
+ protocol_.reserve(distance(url_s.begin(), prot_i));
+ transform(url_s.begin(), prot_i,
+ back_inserter(protocol_),
+ ptr_fun(tolower)); // protocol is icase
+ if( prot_i == url_s.end() )
+ return;
+ advance(prot_i, prot_end.length());
+ string::const_iterator path_i = find(prot_i, url_s.end(), '/');
+ host_.reserve(distance(prot_i, path_i));
+ transform(prot_i, path_i,
+ back_inserter(host_),
+ ptr_fun(tolower)); // host is icase
+ string::const_iterator query_i = find(path_i, url_s.end(), '?');
+ path_.assign(path_i, query_i);
+ if( query_i != url_s.end() )
+ ++query_i;
+ query_.assign(query_i, url_s.end());
}
- return result;
- }
+ std::string protocol_;
+ std::string host_;
+ std::string path_;
+ std::string query_;
+ };*/
+ //---------------------------------------------------------------------------
+ static inline const char* get_hex_vals()
+ {
+ static char hexVals[16] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
+ return hexVals;
+ }
- namespace http
- {
+ static inline const char* get_unsave_chars()
+ {
+ //static char unsave_chars[] = "\"<>%\\^[]`+$,@:;/!#?=&";
+ static char unsave_chars[] = "\"<>%\\^[]`+$,@:;!#&";
+ return unsave_chars;
+ }
- class http_simple_client: public i_target_handler
- {
- public:
-
-
- private:
- enum reciev_machine_state
- {
- reciev_machine_state_header,
- reciev_machine_state_body_content_len,
- reciev_machine_state_body_connection_close,
- reciev_machine_state_body_chunked,
- reciev_machine_state_done,
- reciev_machine_state_error
- };
-
-
-
- enum chunked_state{
- http_chunked_state_chunk_head,
- http_chunked_state_chunk_body,
- http_chunked_state_done,
- http_chunked_state_undefined
- };
-
-
- blocked_mode_client m_net_client;
- std::string m_host_buff;
- std::string m_port;
- //unsigned int m_timeout;
- unsigned int m_connection_timeout;
- unsigned int m_recv_timeout;
- std::string m_header_cache;
- http_response_info m_response_info;
- //std::string* m_ptarget_buffer;
- boost::shared_ptr m_pcontent_encoding_handler;
- reciev_machine_state m_state;
- chunked_state m_chunked_state;
- std::string m_chunked_cache;
- critical_section m_lock;
-
- protected:
- uint64_t m_len_in_summary;
- uint64_t m_len_in_remain;
-
- public:
- void set_host_name(const std::string& name)
- {
- CRITICAL_REGION_LOCAL(m_lock);
- m_host_buff = name;
- }
-
- boost::asio::ip::tcp::socket& get_socket()
- {
- return m_net_client.get_socket();
- }
-
-
- bool connect(const std::string& host, int port, unsigned int timeout)
- {
- return connect(host, std::to_string(port), timeout);
- }
-
- bool set_timeouts(unsigned int connection_timeout, unsigned int recv_timeout)
- {
- m_connection_timeout = connection_timeout;
- m_recv_timeout = recv_timeout;
+ static inline bool is_unsafe(unsigned char compare_char)
+ {
+ if (compare_char <= 32 || compare_char >= 123)
return true;
- }
- bool connect(const std::string& host, std::string port)
+ const char* punsave = get_unsave_chars();
+
+ for (int ichar_pos = 0; 0 != punsave[ichar_pos]; ichar_pos++)
+ if (compare_char == punsave[ichar_pos])
+ return true;
+
+ return false;
+ }
+
+ static inline
+ std::string dec_to_hex(char num, int radix)
+ {
+ int temp = 0;
+ std::string csTmp;
+ int num_char;
+
+ num_char = (int)num;
+ if (num_char < 0)
+ num_char = 256 + num_char;
+
+ while (num_char >= radix)
{
- CRITICAL_REGION_LOCAL(m_lock);
- m_host_buff = host;
- m_port = port;
-
- return m_net_client.connect(host, port, m_connection_timeout, m_recv_timeout);
+ temp = num_char % radix;
+ num_char = (int)floor((float)num_char / (float)radix);
+ csTmp = get_hex_vals()[temp];
}
- bool connect(const std::string& host, const std::string& port, unsigned int timeout)
+ csTmp += get_hex_vals()[num_char];
+
+ if (csTmp.size() < 2)
{
- m_connection_timeout = m_recv_timeout = timeout;
- return connect(host, port);
+ csTmp += '0';
}
- //---------------------------------------------------------------------------
- bool disconnect()
- {
- CRITICAL_REGION_LOCAL(m_lock);
- return m_net_client.disconnect();
- }
- //---------------------------------------------------------------------------
- bool is_connected()
- {
- CRITICAL_REGION_LOCAL(m_lock);
- return m_net_client.is_connected();
- }
- //---------------------------------------------------------------------------
- virtual bool handle_target_data(std::string& piece_of_transfer)
- {
- CRITICAL_REGION_LOCAL(m_lock);
- m_response_info.m_body += piece_of_transfer;
- piece_of_transfer.clear();
- return true;
- }
- //---------------------------------------------------------------------------
- inline
- bool invoke_get(const std::string& uri, const std::string& body = std::string(), const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list())
- {
- CRITICAL_REGION_LOCAL(m_lock);
- return invoke(uri, "GET", body, ppresponse_info, additional_params);
- }
- //---------------------------------------------------------------------------
- inline bool invoke(const std::string& uri, const std::string& method, const std::string& body, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list())
- {
- CRITICAL_REGION_LOCAL(m_lock);
- if(!is_connected())
- {
- LOG_PRINT("Reconnecting...", LOG_LEVEL_3);
- if(!connect(m_host_buff, m_port))
- {
- LOG_PRINT("Failed to connect to " << m_host_buff << ":" << m_port, LOG_LEVEL_3);
- return false;
- }
- }
- m_response_info.clear();
- std::string req_buff = method + " ";
- req_buff += uri + " HTTP/1.1\r\n" +
- "Host: "+ m_host_buff +"\r\n" + "Content-Length: " + boost::lexical_cast(body.size()) + "\r\n";
+ std::reverse(csTmp.begin(), csTmp.end());
+ //_mbsrev((unsigned char*)csTmp.data());
+
+ return csTmp;
+ }
+
+ static inline std::string convert(char val)
+ {
+ std::string csRet;
+ csRet += "%";
+ csRet += dec_to_hex(val, 16);
+ return csRet;
+ }
+ static inline std::string conver_to_url_format(const std::string& uri)
+ {
+
+ std::string result;
+
+ for (size_t i = 0; i != uri.size(); i++)
+ {
+ if (is_unsafe(uri[i]))
+ result += convert(uri[i]);
+ else
+ result += uri[i];
+
+ }
+
+ return result;
+ }
+
+ static inline std::string convert_to_url_format_force_all(const std::string& uri)
+ {
+
+ std::string result;
+
+ for (size_t i = 0; i != uri.size(); i++)
+ {
+ result += convert(uri[i]);
+ }
+
+ return result;
+ }
- //handle "additional_params"
- for(fields_list::const_iterator it = additional_params.begin(); it!=additional_params.end(); it++)
- req_buff += it->first + ": " + it->second + "\r\n";
- req_buff += "\r\n";
- //--
- bool res = m_net_client.send(req_buff);
- CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND");
- if(body.size())
- res = m_net_client.send(body);
- CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND");
- if(ppresponse_info)
- *ppresponse_info = &m_response_info;
- m_state = reciev_machine_state_header;
- return handle_reciev();
- }
- //---------------------------------------------------------------------------
- inline bool invoke_post(const std::string& uri, const std::string& body, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list())
- {
- CRITICAL_REGION_LOCAL(m_lock);
- return invoke(uri, "POST", body, ppresponse_info, additional_params);
- }
- private:
- //---------------------------------------------------------------------------
- inline bool handle_reciev()
- {
- CRITICAL_REGION_LOCAL(m_lock);
- bool keep_handling = true;
- bool need_more_data = true;
- std::string recv_buffer;
- while(keep_handling)
- {
- if(need_more_data)
- {
- if(!m_net_client.recv(recv_buffer))
- {
- LOG_PRINT("Unexpected reciec fail", LOG_LEVEL_3);
- m_state = reciev_machine_state_error;
- }
- if(!recv_buffer.size())
+ namespace http
+ {
+ template
+ class http_simple_client_t : public i_target_handler
+ {
+ public:
+
+
+ private:
+ enum reciev_machine_state
+ {
+ reciev_machine_state_header,
+ reciev_machine_state_body_content_len,
+ reciev_machine_state_body_connection_close,
+ reciev_machine_state_body_chunked,
+ reciev_machine_state_done,
+ reciev_machine_state_error
+ };
+
+
+
+ enum chunked_state {
+ http_chunked_state_chunk_head,
+ http_chunked_state_chunk_body,
+ http_chunked_state_done,
+ http_chunked_state_undefined
+ };
+
+
+ blocked_mode_client_t m_net_client;
+ std::string m_host_buff;
+ std::string m_port;
+ //unsigned int m_timeout;
+ unsigned int m_connection_timeout;
+ unsigned int m_recv_timeout;
+ std::string m_header_cache;
+ http_response_info m_response_info;
+ //std::string* m_ptarget_buffer;
+ boost::shared_ptr m_pcontent_encoding_handler;
+ reciev_machine_state m_state;
+ chunked_state m_chunked_state;
+ std::string m_chunked_cache;
+ critical_section m_lock;
+
+ protected:
+ uint64_t m_len_in_summary;
+ uint64_t m_len_in_remain;
+
+ public:
+ void set_host_name(const std::string& name)
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ m_host_buff = name;
+ }
+
+ boost::asio::ip::tcp::socket& get_socket()
+ {
+ return m_net_client.get_socket();
+ }
+
+
+ bool connect(const std::string& host, int port, unsigned int timeout)
+ {
+ return connect(host, std::to_string(port), timeout);
+ }
+
+ bool set_timeouts(unsigned int connection_timeout, unsigned int recv_timeout)
+ {
+ m_connection_timeout = connection_timeout;
+ m_recv_timeout = recv_timeout;
+ return true;
+ }
+
+ bool connect(const std::string& host, std::string port)
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ m_host_buff = host;
+ m_port = port;
+
+ return m_net_client.connect(host, port, m_connection_timeout, m_recv_timeout);
+ }
+
+ bool connect(const std::string& host, const std::string& port, unsigned int timeout)
+ {
+ m_connection_timeout = m_recv_timeout = timeout;
+ return connect(host, port);
+ }
+ //---------------------------------------------------------------------------
+ bool disconnect()
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ return m_net_client.disconnect();
+ }
+ //---------------------------------------------------------------------------
+ bool is_connected()
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ return m_net_client.is_connected();
+ }
+ //---------------------------------------------------------------------------
+ virtual bool handle_target_data(std::string& piece_of_transfer)
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ m_response_info.m_body += piece_of_transfer;
+ piece_of_transfer.clear();
+ return true;
+ }
+ //---------------------------------------------------------------------------
+ inline
+ bool invoke_get(const std::string& uri, const std::string& body = std::string(), const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list())
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ return invoke(uri, "GET", body, ppresponse_info, additional_params);
+ }
+
+ //---------------------------------------------------------------------------
+ inline bool invoke(const std::string& uri, const std::string& method, const std::string& body, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list())
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ if (!is_connected())
+ {
+ LOG_PRINT("Reconnecting...", LOG_LEVEL_3);
+ if (!connect(m_host_buff, m_port))
{
- //connection is going to be closed
- if(reciev_machine_state_body_connection_close != m_state)
+ LOG_PRINT("Failed to connect to " << m_host_buff << ":" << m_port, LOG_LEVEL_3);
+ return false;
+ }
+ }
+ m_response_info.clear();
+ std::string req_buff = method + " ";
+ req_buff += uri + " HTTP/1.1\r\n" +
+ "Host: " + m_host_buff + "\r\n" + "Content-Length: " + boost::lexical_cast(body.size()) + "\r\n";
+
+
+ //handle "additional_params"
+ for (fields_list::const_iterator it = additional_params.begin(); it != additional_params.end(); it++)
+ req_buff += it->first + ": " + it->second + "\r\n";
+ req_buff += "\r\n";
+ //--
+
+ bool res = m_net_client.send(req_buff);
+ CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND");
+ if (body.size())
+ res = m_net_client.send(body);
+ CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND");
+
+ if (ppresponse_info)
+ *ppresponse_info = &m_response_info;
+
+ m_state = reciev_machine_state_header;
+ return handle_reciev();
+ }
+ //---------------------------------------------------------------------------
+ inline bool invoke_post(const std::string& uri, const std::string& body, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list())
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ return invoke(uri, "POST", body, ppresponse_info, additional_params);
+ }
+ private:
+ //---------------------------------------------------------------------------
+ inline bool handle_reciev()
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ bool keep_handling = true;
+ bool need_more_data = true;
+ std::string recv_buffer;
+ while (keep_handling)
+ {
+ if (need_more_data)
+ {
+ if (!m_net_client.recv(recv_buffer))
{
+ LOG_PRINT("Unexpected reciec fail", LOG_LEVEL_3);
m_state = reciev_machine_state_error;
}
- }
- need_more_data = false;
- }
- switch(m_state)
- {
- case reciev_machine_state_header:
- keep_handling = handle_header(recv_buffer, need_more_data);
- break;
- case reciev_machine_state_body_content_len:
- keep_handling = handle_body_content_len(recv_buffer, need_more_data);
- break;
- case reciev_machine_state_body_connection_close:
- keep_handling = handle_body_connection_close(recv_buffer, need_more_data);
- break;
- case reciev_machine_state_body_chunked:
- keep_handling = handle_body_body_chunked(recv_buffer, need_more_data);
- break;
- case reciev_machine_state_done:
- keep_handling = false;
- break;
- case reciev_machine_state_error:
- keep_handling = false;
- break;
- }
-
- }
- m_header_cache.clear();
- if(m_state != reciev_machine_state_error)
- {
- if(m_response_info.m_header_info.m_connection.size() && !string_tools::compare_no_case("close", m_response_info.m_header_info.m_connection))
- disconnect();
-
- return true;
- }
- else
+ if (!recv_buffer.size())
+ {
+ //connection is going to be closed
+ if (reciev_machine_state_body_connection_close != m_state)
{
- LOG_PRINT_L3("Returning false because of wrong state machine. state: " << m_state);
- return false;
+ m_state = reciev_machine_state_error;
}
- }
- //---------------------------------------------------------------------------
- inline
- bool handle_header(std::string& recv_buff, bool& need_more_data)
- {
-
- CRITICAL_REGION_LOCAL(m_lock);
- if(!recv_buff.size())
- {
- LOG_ERROR("Connection closed at handle_header");
- m_state = reciev_machine_state_error;
- return false;
- }
+ }
+ need_more_data = false;
+ }
+ switch (m_state)
+ {
+ case reciev_machine_state_header:
+ keep_handling = handle_header(recv_buffer, need_more_data);
+ break;
+ case reciev_machine_state_body_content_len:
+ keep_handling = handle_body_content_len(recv_buffer, need_more_data);
+ break;
+ case reciev_machine_state_body_connection_close:
+ keep_handling = handle_body_connection_close(recv_buffer, need_more_data);
+ break;
+ case reciev_machine_state_body_chunked:
+ keep_handling = handle_body_body_chunked(recv_buffer, need_more_data);
+ break;
+ case reciev_machine_state_done:
+ keep_handling = false;
+ break;
+ case reciev_machine_state_error:
+ keep_handling = false;
+ break;
+ }
- m_header_cache += recv_buff;
- recv_buff.clear();
- std::string::size_type pos = m_header_cache.find("\r\n\r\n");
- if(pos != std::string::npos)
- {
- recv_buff.assign(m_header_cache.begin()+pos+4, m_header_cache.end());
- m_header_cache.erase(m_header_cache.begin()+pos+4, m_header_cache.end());
-
- analize_cached_header_and_invoke_state();
- m_header_cache.clear();
- if(!recv_buff.size() && (m_state != reciev_machine_state_error && m_state != reciev_machine_state_done))
- need_more_data = true;
-
- return true;
- }else
- need_more_data = true;
- return true;
- }
- //---------------------------------------------------------------------------
- inline
- bool handle_body_content_len(std::string& recv_buff, bool& need_more_data)
- {
- CRITICAL_REGION_LOCAL(m_lock);
- if(!recv_buff.size())
- {
- LOG_PRINT("Warning: Content-Len mode, but connection unexpectedly closed", LOG_LEVEL_3);
- m_state = reciev_machine_state_done;
- return true;
- }
- CHECK_AND_ASSERT_MES(m_len_in_remain >= recv_buff.size(), false, "m_len_in_remain >= recv_buff.size()");
- m_len_in_remain -= recv_buff.size();
- bool r = m_pcontent_encoding_handler->update_in(recv_buff);
- //CHECK_AND_ASSERT_MES(m_len_in_remain >= recv_buff.size(), false, "m_pcontent_encoding_handler->update_in returned false");
- if (!r)
- {
- m_state = reciev_machine_state_error;
- disconnect();
- return false;
- }
-
- if(m_len_in_remain == 0)
- m_state = reciev_machine_state_done;
- else
- need_more_data = true;
-
- return true;
- }
- //---------------------------------------------------------------------------
- inline
- bool handle_body_connection_close(std::string& recv_buff, bool& need_more_data)
- {
- CRITICAL_REGION_LOCAL(m_lock);
- if(!recv_buff.size())
- {
- m_state = reciev_machine_state_done;
- return true;
- }
- need_more_data = true;
- m_pcontent_encoding_handler->update_in(recv_buff);
-
-
- return true;
- }
- //---------------------------------------------------------------------------
- inline bool is_hex_symbol(char ch)
- {
-
- if( (ch >= '0' && ch <='9')||(ch >= 'A' && ch <='F')||(ch >= 'a' && ch <='f'))
- return true;
- else
- return false;
- }
- //---------------------------------------------------------------------------
- inline
- bool get_len_from_chunk_head(const std::string &chunk_head, uint64_t& result_size)
- {
- std::stringstream str_stream;
- str_stream << std::hex;
- if(!(str_stream << chunk_head && str_stream >> result_size))
- return false;
-
- return true;
- }
- //---------------------------------------------------------------------------
- inline
- bool get_chunk_head(std::string& buff, uint64_t& chunk_size, bool& is_matched)
- {
- is_matched = false;
- size_t offset = 0;
- for(std::string::iterator it = buff.begin(); it!= buff.end(); it++, offset++)
- {
- if(!is_hex_symbol(*it))
- {
- if(*it == '\r' || *it == ' ' )
- {
- offset--;
- continue;
- }
- else if(*it == '\n')
- {
- std::string chunk_head = buff.substr(0, offset);
- if(!get_len_from_chunk_head(chunk_head, chunk_size))
- return false;
-
- if(0 == chunk_size)
- {
- //Here is a small confusion
- //In breif - if the chunk is the last one we need to get terminating sequence
- //along with the cipher, generally in the "ddd\r\n\r\n" form
-
- for(it++;it != buff.end(); it++)
- {
- if('\r' == *it)
- continue;
- else if('\n' == *it)
- break;
- else
- {
- LOG_ERROR("http_stream_filter: Wrong last chunk terminator");
- return false;
- }
- }
-
- if(it == buff.end())
- return true;
- }
-
- buff.erase(buff.begin(), ++it);
-
- is_matched = true;
- return true;
- }
- else
- return false;
- }
- }
-
- return true;
- }
- //---------------------------------------------------------------------------
- inline
- bool handle_body_body_chunked(std::string& recv_buff, bool& need_more_data)
- {
- CRITICAL_REGION_LOCAL(m_lock);
- if(!recv_buff.size())
- {
- LOG_PRINT("Warning: CHUNKED mode, but connection unexpectedly closed", LOG_LEVEL_3);
- m_state = reciev_machine_state_done;
- return true;
- }
- m_chunked_cache += recv_buff;
- recv_buff.clear();
- bool is_matched = false;
-
- while(true)
- {
- if(!m_chunked_cache.size())
- {
- need_more_data = true;
- break;
- }
-
- switch(m_chunked_state)
- {
- case http_chunked_state_chunk_head:
- if(m_chunked_cache[0] == '\n' || m_chunked_cache[0] == '\r')
- {
- //optimize a bit
- if(m_chunked_cache[0] == '\r' && m_chunked_cache.size()>1 && m_chunked_cache[1] == '\n')
- m_chunked_cache.erase(0, 2);
- else
- m_chunked_cache.erase(0, 1);
- break;
- }
- if(!get_chunk_head(m_chunked_cache, m_len_in_remain, is_matched))
- {
- LOG_ERROR("http_stream_filter::handle_chunked(*) Failed to get length from chunked head:" << m_chunked_cache);
- m_state = reciev_machine_state_error;
- return false;
- }
-
- if(!is_matched)
- {
- need_more_data = true;
- return true;
- }else
- {
- m_chunked_state = http_chunked_state_chunk_body;
- if(m_len_in_remain == 0)
- {//last chunk, let stop the stream and fix the chunk queue.
- m_state = reciev_machine_state_done;
- return true;
- }
- m_chunked_state = http_chunked_state_chunk_body;
- break;
- }
- break;
- case http_chunked_state_chunk_body:
- {
- std::string chunk_body;
- if(m_len_in_remain >= m_chunked_cache.size())
- {
- m_len_in_remain -= m_chunked_cache.size();
- chunk_body.swap(m_chunked_cache);
- }else
- {
- chunk_body.assign(m_chunked_cache, 0, m_len_in_remain);
- m_chunked_cache.erase(0, m_len_in_remain);
- m_len_in_remain = 0;
- }
-
- m_pcontent_encoding_handler->update_in(chunk_body);
-
- if(!m_len_in_remain)
- m_chunked_state = http_chunked_state_chunk_head;
- }
- break;
- case http_chunked_state_done:
- m_state = reciev_machine_state_done;
- return true;
- case http_chunked_state_undefined:
- default:
- LOG_ERROR("http_stream_filter::handle_chunked(): Wrong state" << m_chunked_state);
- return false;
- }
- }
-
- return true;
- }
- //---------------------------------------------------------------------------
- inline
- bool parse_header(http_header_info& body_info, const std::string& m_cache_to_process)
- {
- LOG_FRAME("http_stream_filter::parse_cached_header(*)", LOG_LEVEL_4);
-
- STATIC_REGEXP_EXPR_1(rexp_mach_field,
- "\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)"
- // 12 3 4 5 6 7 8 9
- "|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]",
- //10 1112 13
- boost::regex::icase | boost::regex::normal);
-
- boost::smatch result;
- std::string::const_iterator it_current_bound = m_cache_to_process.begin();
- std::string::const_iterator it_end_bound = m_cache_to_process.end();
-
-
-
- //lookup all fields and fill well-known fields
- while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched)
- {
- const size_t field_val = 12;
- //const size_t field_etc_name = 10;
-
- int i = 2; //start position = 2
- if(result[i++].matched)//"Connection"
- body_info.m_connection = result[field_val];
- else if(result[i++].matched)//"Referrer"
- body_info.m_referer = result[field_val];
- else if(result[i++].matched)//"Content-Length"
- body_info.m_content_length = result[field_val];
- else if(result[i++].matched)//"Content-Type"
- body_info.m_content_type = result[field_val];
- else if(result[i++].matched)//"Transfer-Encoding"
- body_info.m_transfer_encoding = result[field_val];
- else if(result[i++].matched)//"Content-Encoding"
- body_info.m_content_encoding = result[field_val];
- else if(result[i++].matched)//"Host"
- { body_info.m_host = result[field_val];
- string_tools::trim(body_info.m_host);
- }
- else if(result[i++].matched)//"Cookie"
- body_info.m_cookie = result[field_val];
- else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!)
- {;}
- else
- {CHECK_AND_ASSERT_MES(false, false, "http_stream_filter::parse_cached_header() not matched last entry in:"<(result[1]);
- m_response_info.m_http_ver_lo = boost::lexical_cast(result[2]);
- m_response_info.m_response_code = boost::lexical_cast(result[3]);
-
- m_header_cache.erase(to_nonsonst_iterator(m_header_cache, result[0].first), to_nonsonst_iterator(m_header_cache, result[0].second));
- return true;
- }else
- {
- LOG_ERROR("http_stream_filter::handle_invoke_reply_line(): Failed to match first response line:" << m_header_cache);
- return false;
- }
-
- }
- inline
- bool set_reply_content_encoder()
- {
- STATIC_REGEXP_EXPR_1(rexp_match_gzip, "^.*?((gzip)|(deflate))", boost::regex::icase | boost::regex::normal);
- boost::smatch result; // 12 3
- if(boost::regex_search( m_response_info.m_header_info.m_content_encoding, result, rexp_match_gzip, boost::match_default) && result[0].matched)
- {
-#ifdef HTTP_ENABLE_GZIP
- m_pcontent_encoding_handler.reset(new content_encoding_gzip(this, result[3].matched));
-#else
- m_pcontent_encoding_handler.reset(new do_nothing_sub_handler(this));
- LOG_ERROR("GZIP encoding not supported in this build, please add zlib to your project and define HTTP_ENABLE_GZIP");
- return false;
-#endif
- }
- else
- {
- m_pcontent_encoding_handler.reset(new do_nothing_sub_handler(this));
- }
-
- return true;
- }
- inline
- bool analize_cached_header_and_invoke_state()
- {
- m_response_info.clear();
- analize_first_response_line();
- std::string fake_str; //gcc error workaround
-
- bool res = parse_header(m_response_info.m_header_info, m_header_cache);
- CHECK_AND_ASSERT_MES(res, false, "http_stream_filter::analize_cached_reply_header_and_invoke_state(): failed to anilize reply header: " << m_header_cache);
-
- set_reply_content_encoder();
-
- m_len_in_summary = 0;
- bool content_len_valid = false;
- if(m_response_info.m_header_info.m_content_length.size())
- content_len_valid = string_tools::get_xtype_from_string(m_len_in_summary, m_response_info.m_header_info.m_content_length);
-
-
-
- if(!m_len_in_summary && ((m_response_info.m_response_code>=100&&m_response_info.m_response_code<200)
- || 204 == m_response_info.m_response_code
- || 304 == m_response_info.m_response_code) )
- {//There will be no response body, server will display the local page with error
- m_state = reciev_machine_state_done;
- return true;
- }else if(m_response_info.m_header_info.m_transfer_encoding.size())
- {
- string_tools::trim(m_response_info.m_header_info.m_transfer_encoding);
- if(string_tools::compare_no_case(m_response_info.m_header_info.m_transfer_encoding, "chunked"))
- {
- LOG_ERROR("Wrong Transfer-Encoding:" << m_response_info.m_header_info.m_transfer_encoding);
- m_state = reciev_machine_state_error;
- return false;
- }
- m_state = reciev_machine_state_body_chunked;
- m_chunked_state = http_chunked_state_chunk_head;
- return true;
- }
- else if(!m_response_info.m_header_info.m_content_length.empty())
- {
- //In the response header the length was specified
- if(!content_len_valid)
- {
- LOG_ERROR("http_stream_filter::analize_cached_reply_header_and_invoke_state(): Failed to get_len_from_content_lenght();, m_query_info.m_content_length="<(u_c.port), timeout))
- {
- LOG_PRINT_L2("invoke_request: cannot connect to " << u_c.host << ":" << u_c.port);
- return false;
- }
- }
-
- return tr.invoke(u_c.uri, method, body, ppresponse_info, additional_params);
- }
-
- struct idle_handler_base
- {
- virtual bool do_call(const std::string& piece_of_data, uint64_t total_bytes, uint64_t received_bytes) = 0;
- virtual ~idle_handler_base() {}
- };
-
- template
- struct idle_handler : public idle_handler_base
- {
- callback_t m_cb;
-
- idle_handler(callback_t cb) : m_cb(cb) {}
- virtual bool do_call(const std::string& piece_of_data, uint64_t total_bytes, uint64_t received_bytes)
- {
- return m_cb(piece_of_data, total_bytes, received_bytes);
- }
- };
-
- class interruptible_http_client : public http_simple_client
- {
- std::shared_ptr m_pcb;
- bool m_permanent_error = false;
-
- virtual bool handle_target_data(std::string& piece_of_transfer)
- {
- bool r = m_pcb->do_call(piece_of_transfer, m_len_in_summary, m_len_in_summary - m_len_in_remain);
- piece_of_transfer.clear();
- return r;
- }
-
- public:
- template
- bool invoke_cb(callback_t cb, const std::string& url, uint64_t timeout, const std::string& method = "GET", const std::string& body = std::string(), const fields_list& additional_params = fields_list())
- {
- m_pcb.reset(new idle_handler(cb));
- const http_response_info* p_hri = nullptr;
- bool r = invoke_request(url, *this, timeout, &p_hri, method, body, additional_params);
- if (p_hri && !(p_hri->m_response_code >= 200 && p_hri->m_response_code < 300))
- {
- LOG_PRINT_L0("HTTP request to " << url << " failed with code: " << p_hri->m_response_code);
- m_permanent_error = true;
- return false;
- }
- return r;
- }
-
- template
- bool download(callback_t cb, const std::string& path_for_file, const std::string& url, uint64_t timeout, const std::string& method = "GET", const std::string& body = std::string(), const fields_list& additional_params = fields_list())
- {
- std::ofstream fs;
- fs.open(path_for_file, std::ios::binary | std::ios::out | std::ios::trunc);
- if (!fs.is_open())
- {
- LOG_ERROR("Fsiled to open " << path_for_file);
- return false;
- }
- auto local_cb = [&](const std::string& piece_of_data, uint64_t total_bytes, uint64_t received_bytes)
- {
- fs.write(piece_of_data.data(), piece_of_data.size());
- return cb(total_bytes, received_bytes);
- };
- bool r = this->invoke_cb(local_cb, url, timeout, method, body, additional_params);
- fs.close();
- return r;
- }
-
- //
- template
- bool download_and_unzip(callback_t cb, const std::string& path_for_file, const std::string& url, uint64_t timeout, const std::string& method = "GET", const std::string& body = std::string(), uint64_t fails_count = 1000, const fields_list& additional_params = fields_list())
- {
- std::ofstream fs;
- fs.open(path_for_file, std::ios::binary | std::ios::out | std::ios::trunc);
- if (!fs.is_open())
- {
- LOG_ERROR("Fsiled to open " << path_for_file);
- return false;
- }
- std::string buff;
- gzip_decoder_lambda zip_decoder;
- uint64_t state_total_bytes = 0;
- uint64_t state_received_bytes_base = 0;
- uint64_t state_received_bytes_current = 0;
- bool stopped = false;
- auto local_cb = [&](const std::string& piece_of_data, uint64_t total_bytes, uint64_t received_bytes)
- {
- //remember total_bytes only for first attempt, where fetched full lenght of the file
- if (!state_total_bytes)
- state_total_bytes = total_bytes;
-
- buff += piece_of_data;
- return zip_decoder.update_in(buff, [&](const std::string& unpacked_buff)
+ }
+ m_header_cache.clear();
+ if (m_state != reciev_machine_state_error)
{
- state_received_bytes_current = received_bytes;
- fs.write(unpacked_buff.data(), unpacked_buff.size());
- stopped = !cb(unpacked_buff, state_total_bytes, state_received_bytes_base + received_bytes);
- return !stopped;
- });
- };
- uint64_t current_err_count = 0;
- bool r = false;
- m_permanent_error = false;
- while (!r && current_err_count < fails_count)
+ if (m_response_info.m_header_info.m_connection.size() && !string_tools::compare_no_case("close", m_response_info.m_header_info.m_connection))
+ disconnect();
+
+ return true;
+ }
+ else
+ {
+ LOG_PRINT_L3("Returning false because of wrong state machine. state: " << m_state);
+ return false;
+ }
+ }
+ //---------------------------------------------------------------------------
+ inline
+ bool handle_header(std::string& recv_buff, bool& need_more_data)
{
- LOG_PRINT_L0("Attempt " << current_err_count + 1 << "/" << fails_count << " to get " << url << " (offset:" << state_received_bytes_base << ")");
- fields_list additional_params_local = additional_params;
- additional_params_local.push_back(std::make_pair("Range", std::string("bytes=") + std::to_string(state_received_bytes_base) + "-"));
- r = this->invoke_cb(local_cb, url, timeout, method, body, additional_params_local);
+
+ CRITICAL_REGION_LOCAL(m_lock);
+ if (!recv_buff.size())
+ {
+ LOG_ERROR("Connection closed at handle_header");
+ m_state = reciev_machine_state_error;
+ return false;
+ }
+
+ m_header_cache += recv_buff;
+ recv_buff.clear();
+ std::string::size_type pos = m_header_cache.find("\r\n\r\n");
+ if (pos != std::string::npos)
+ {
+ recv_buff.assign(m_header_cache.begin() + pos + 4, m_header_cache.end());
+ m_header_cache.erase(m_header_cache.begin() + pos + 4, m_header_cache.end());
+
+ analize_cached_header_and_invoke_state();
+ m_header_cache.clear();
+ if (!recv_buff.size() && (m_state != reciev_machine_state_error && m_state != reciev_machine_state_done))
+ need_more_data = true;
+
+ return true;
+ }
+ else
+ need_more_data = true;
+ return true;
+ }
+ //---------------------------------------------------------------------------
+ inline
+ bool handle_body_content_len(std::string& recv_buff, bool& need_more_data)
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ if (!recv_buff.size())
+ {
+ LOG_PRINT("Warning: Content-Len mode, but connection unexpectedly closed", LOG_LEVEL_3);
+ m_state = reciev_machine_state_done;
+ return true;
+ }
+ CHECK_AND_ASSERT_MES(m_len_in_remain >= recv_buff.size(), false, "m_len_in_remain >= recv_buff.size()");
+ m_len_in_remain -= recv_buff.size();
+ bool r = m_pcontent_encoding_handler->update_in(recv_buff);
+ //CHECK_AND_ASSERT_MES(m_len_in_remain >= recv_buff.size(), false, "m_pcontent_encoding_handler->update_in returned false");
if (!r)
{
- if (stopped || m_permanent_error)
+ m_state = reciev_machine_state_error;
+ disconnect();
+ return false;
+ }
+
+ if (m_len_in_remain == 0)
+ m_state = reciev_machine_state_done;
+ else
+ need_more_data = true;
+
+ return true;
+ }
+ //---------------------------------------------------------------------------
+ inline
+ bool handle_body_connection_close(std::string& recv_buff, bool& need_more_data)
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ if (!recv_buff.size())
+ {
+ m_state = reciev_machine_state_done;
+ return true;
+ }
+ need_more_data = true;
+ m_pcontent_encoding_handler->update_in(recv_buff);
+
+
+ return true;
+ }
+ //---------------------------------------------------------------------------
+ inline bool is_hex_symbol(char ch)
+ {
+
+ if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f'))
+ return true;
+ else
+ return false;
+ }
+ //---------------------------------------------------------------------------
+ inline
+ bool get_len_from_chunk_head(const std::string &chunk_head, uint64_t& result_size)
+ {
+ std::stringstream str_stream;
+ str_stream << std::hex;
+ if (!(str_stream << chunk_head && str_stream >> result_size))
+ return false;
+
+ return true;
+ }
+ //---------------------------------------------------------------------------
+ inline
+ bool get_chunk_head(std::string& buff, uint64_t& chunk_size, bool& is_matched)
+ {
+ is_matched = false;
+ size_t offset = 0;
+ for (std::string::iterator it = buff.begin(); it != buff.end(); it++, offset++)
+ {
+ if (!is_hex_symbol(*it))
+ {
+ if (*it == '\r' || *it == ' ')
+ {
+ offset--;
+ continue;
+ }
+ else if (*it == '\n')
+ {
+ std::string chunk_head = buff.substr(0, offset);
+ if (!get_len_from_chunk_head(chunk_head, chunk_size))
+ return false;
+
+ if (0 == chunk_size)
+ {
+ //Here is a small confusion
+ //In breif - if the chunk is the last one we need to get terminating sequence
+ //along with the cipher, generally in the "ddd\r\n\r\n" form
+
+ for (it++; it != buff.end(); it++)
+ {
+ if ('\r' == *it)
+ continue;
+ else if ('\n' == *it)
+ break;
+ else
+ {
+ LOG_ERROR("http_stream_filter: Wrong last chunk terminator");
+ return false;
+ }
+ }
+
+ if (it == buff.end())
+ return true;
+ }
+
+ buff.erase(buff.begin(), ++it);
+
+ is_matched = true;
+ return true;
+ }
+ else
+ return false;
+ }
+ }
+
+ return true;
+ }
+ //---------------------------------------------------------------------------
+ inline
+ bool handle_body_body_chunked(std::string& recv_buff, bool& need_more_data)
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ if (!recv_buff.size())
+ {
+ LOG_PRINT("Warning: CHUNKED mode, but connection unexpectedly closed", LOG_LEVEL_3);
+ m_state = reciev_machine_state_done;
+ return true;
+ }
+ m_chunked_cache += recv_buff;
+ recv_buff.clear();
+ bool is_matched = false;
+
+ while (true)
+ {
+ if (!m_chunked_cache.size())
+ {
+ need_more_data = true;
break;
- current_err_count++;
- state_received_bytes_base += state_received_bytes_current;
- state_received_bytes_current = 0;
- boost::this_thread::sleep_for(boost::chrono::milliseconds(2000));
+ }
+
+ switch (m_chunked_state)
+ {
+ case http_chunked_state_chunk_head:
+ if (m_chunked_cache[0] == '\n' || m_chunked_cache[0] == '\r')
+ {
+ //optimize a bit
+ if (m_chunked_cache[0] == '\r' && m_chunked_cache.size()>1 && m_chunked_cache[1] == '\n')
+ m_chunked_cache.erase(0, 2);
+ else
+ m_chunked_cache.erase(0, 1);
+ break;
+ }
+ if (!get_chunk_head(m_chunked_cache, m_len_in_remain, is_matched))
+ {
+ LOG_ERROR("http_stream_filter::handle_chunked(*) Failed to get length from chunked head:" << m_chunked_cache);
+ m_state = reciev_machine_state_error;
+ return false;
+ }
+
+ if (!is_matched)
+ {
+ need_more_data = true;
+ return true;
+ }
+ else
+ {
+ m_chunked_state = http_chunked_state_chunk_body;
+ if (m_len_in_remain == 0)
+ {//last chunk, let stop the stream and fix the chunk queue.
+ m_state = reciev_machine_state_done;
+ return true;
+ }
+ m_chunked_state = http_chunked_state_chunk_body;
+ break;
+ }
+ break;
+ case http_chunked_state_chunk_body:
+ {
+ std::string chunk_body;
+ if (m_len_in_remain >= m_chunked_cache.size())
+ {
+ m_len_in_remain -= m_chunked_cache.size();
+ chunk_body.swap(m_chunked_cache);
+ }
+ else
+ {
+ chunk_body.assign(m_chunked_cache, 0, m_len_in_remain);
+ m_chunked_cache.erase(0, m_len_in_remain);
+ m_len_in_remain = 0;
+ }
+
+ m_pcontent_encoding_handler->update_in(chunk_body);
+
+ if (!m_len_in_remain)
+ m_chunked_state = http_chunked_state_chunk_head;
+ }
+ break;
+ case http_chunked_state_done:
+ m_state = reciev_machine_state_done;
+ return true;
+ case http_chunked_state_undefined:
+ default:
+ LOG_ERROR("http_stream_filter::handle_chunked(): Wrong state" << m_chunked_state);
+ return false;
+ }
+ }
+
+ return true;
+ }
+ //---------------------------------------------------------------------------
+ inline
+ bool parse_header(http_header_info& body_info, const std::string& m_cache_to_process)
+ {
+ LOG_FRAME("http_stream_filter::parse_cached_header(*)", LOG_LEVEL_4);
+
+ STATIC_REGEXP_EXPR_1(rexp_mach_field,
+ "\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)"
+ // 12 3 4 5 6 7 8 9
+ "|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]",
+ //10 1112 13
+ boost::regex::icase | boost::regex::normal);
+
+ boost::smatch result;
+ std::string::const_iterator it_current_bound = m_cache_to_process.begin();
+ std::string::const_iterator it_end_bound = m_cache_to_process.end();
+
+
+
+ //lookup all fields and fill well-known fields
+ while (boost::regex_search(it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched)
+ {
+ const size_t field_val = 12;
+ //const size_t field_etc_name = 10;
+
+ int i = 2; //start position = 2
+ if (result[i++].matched)//"Connection"
+ body_info.m_connection = result[field_val];
+ else if (result[i++].matched)//"Referrer"
+ body_info.m_referer = result[field_val];
+ else if (result[i++].matched)//"Content-Length"
+ body_info.m_content_length = result[field_val];
+ else if (result[i++].matched)//"Content-Type"
+ body_info.m_content_type = result[field_val];
+ else if (result[i++].matched)//"Transfer-Encoding"
+ body_info.m_transfer_encoding = result[field_val];
+ else if (result[i++].matched)//"Content-Encoding"
+ body_info.m_content_encoding = result[field_val];
+ else if (result[i++].matched)//"Host"
+ {
+ body_info.m_host = result[field_val];
+ string_tools::trim(body_info.m_host);
+ }
+ else if (result[i++].matched)//"Cookie"
+ body_info.m_cookie = result[field_val];
+ else if (result[i++].matched)//e.t.c (HAVE TO BE MATCHED!)
+ {
+ ;
+ }
+ else
+ {
+ CHECK_AND_ASSERT_MES(false, false, "http_stream_filter::parse_cached_header() not matched last entry in:" << m_cache_to_process);
+ }
+
+ it_current_bound = result[(int)result.size() - 1].first;
+ }
+ return true;
+
+ }
+ inline
+ bool analize_first_response_line()
+ {
+
+ //First line response, look like this: "HTTP/1.1 200 OK"
+ STATIC_REGEXP_EXPR_1(rexp_match_first_response_line, "^HTTP/(\\d+).(\\d+) ((\\d)\\d{2})( [^\n]*)?\r?\n", boost::regex::icase | boost::regex::normal);
+ // 1 2 34 5
+ //size_t match_len = 0;
+ boost::smatch result;
+ if (boost::regex_search(m_header_cache, result, rexp_match_first_response_line, boost::match_default) && result[0].matched)
+ {
+ CHECK_AND_ASSERT_MES(result[1].matched&&result[2].matched, false, "http_stream_filter::handle_invoke_reply_line() assert failed...");
+ m_response_info.m_http_ver_hi = boost::lexical_cast(result[1]);
+ m_response_info.m_http_ver_lo = boost::lexical_cast(result[2]);
+ m_response_info.m_response_code = boost::lexical_cast(result[3]);
+
+ m_header_cache.erase(to_nonsonst_iterator(m_header_cache, result[0].first), to_nonsonst_iterator(m_header_cache, result[0].second));
+ return true;
+ }
+ else
+ {
+ LOG_ERROR("http_stream_filter::handle_invoke_reply_line(): Failed to match first response line:" << m_header_cache);
+ return false;
+ }
+
+ }
+ inline
+ bool set_reply_content_encoder()
+ {
+ STATIC_REGEXP_EXPR_1(rexp_match_gzip, "^.*?((gzip)|(deflate))", boost::regex::icase | boost::regex::normal);
+ boost::smatch result; // 12 3
+ if (boost::regex_search(m_response_info.m_header_info.m_content_encoding, result, rexp_match_gzip, boost::match_default) && result[0].matched)
+ {
+#ifdef HTTP_ENABLE_GZIP
+ m_pcontent_encoding_handler.reset(new content_encoding_gzip(this, result[3].matched));
+#else
+ m_pcontent_encoding_handler.reset(new do_nothing_sub_handler(this));
+ LOG_ERROR("GZIP encoding not supported in this build, please add zlib to your project and define HTTP_ENABLE_GZIP");
+ return false;
+#endif
+ }
+ else
+ {
+ m_pcontent_encoding_handler.reset(new do_nothing_sub_handler(this));
+ }
+
+ return true;
+ }
+ inline
+ bool analize_cached_header_and_invoke_state()
+ {
+ m_response_info.clear();
+ analize_first_response_line();
+ std::string fake_str; //gcc error workaround
+
+ bool res = parse_header(m_response_info.m_header_info, m_header_cache);
+ CHECK_AND_ASSERT_MES(res, false, "http_stream_filter::analize_cached_reply_header_and_invoke_state(): failed to anilize reply header: " << m_header_cache);
+
+ set_reply_content_encoder();
+
+ m_len_in_summary = 0;
+ bool content_len_valid = false;
+ if (m_response_info.m_header_info.m_content_length.size())
+ content_len_valid = string_tools::get_xtype_from_string(m_len_in_summary, m_response_info.m_header_info.m_content_length);
+
+
+
+ if (!m_len_in_summary && ((m_response_info.m_response_code >= 100 && m_response_info.m_response_code<200)
+ || 204 == m_response_info.m_response_code
+ || 304 == m_response_info.m_response_code))
+ {//There will be no response body, server will display the local page with error
+ m_state = reciev_machine_state_done;
+ return true;
+ }
+ else if (m_response_info.m_header_info.m_transfer_encoding.size())
+ {
+ string_tools::trim(m_response_info.m_header_info.m_transfer_encoding);
+ if (string_tools::compare_no_case(m_response_info.m_header_info.m_transfer_encoding, "chunked"))
+ {
+ LOG_ERROR("Wrong Transfer-Encoding:" << m_response_info.m_header_info.m_transfer_encoding);
+ m_state = reciev_machine_state_error;
+ return false;
+ }
+ m_state = reciev_machine_state_body_chunked;
+ m_chunked_state = http_chunked_state_chunk_head;
+ return true;
+ }
+ else if (!m_response_info.m_header_info.m_content_length.empty())
+ {
+ //In the response header the length was specified
+ if (!content_len_valid)
+ {
+ LOG_ERROR("http_stream_filter::analize_cached_reply_header_and_invoke_state(): Failed to get_len_from_content_lenght();, m_query_info.m_content_length=" << m_response_info.m_header_info.m_content_length);
+ m_state = reciev_machine_state_error;
+ return false;
+ }
+ if (!m_len_in_summary)
+ {
+ m_state = reciev_machine_state_done;
+ return true;
+ }
+ else
+ {
+ m_len_in_remain = m_len_in_summary;
+ m_state = reciev_machine_state_body_content_len;
+ return true;
+ }
+ }
+ else if (!m_response_info.m_header_info.m_connection.empty() && is_connection_close_field(m_response_info.m_header_info.m_connection))
+ { //By indirect signs we suspect that data transfer will end with a connection break
+ m_state = reciev_machine_state_body_connection_close;
+ }
+ else if (is_multipart_body(m_response_info.m_header_info, fake_str))
+ {
+ m_state = reciev_machine_state_error;
+ LOG_ERROR("Unsupported MULTIPART BODY.");
+ return false;
+ }
+ else
+ { //Apparently there are no signs of the form of transfer, will receive data until the connection is closed
+ m_state = reciev_machine_state_body_connection_close;
+ LOG_PRINT("Undefinded transfer type, consider http_body_transfer_connection_close method. header: " << m_header_cache, LOG_LEVEL_2);
+ return false;
+ }
+ return false;
+ }
+ inline
+ bool is_connection_close_field(const std::string& str)
+ {
+ STATIC_REGEXP_EXPR_1(rexp_match_close, "^\\s*close", boost::regex::icase | boost::regex::normal);
+ boost::smatch result;
+ if (boost::regex_search(str, result, rexp_match_close, boost::match_default) && result[0].matched)
+ return true;
+ else
+ return false;
+ }
+ inline
+ bool is_multipart_body(const http_header_info& head_info, OUT std::string& boundary)
+ {
+ //Check whether this is multi part - if yes, capture boundary immediately
+ STATIC_REGEXP_EXPR_1(rexp_match_multipart_type, "^\\s*multipart/([\\w\\-]+); boundary=((\"(.*?)\")|(\\\\\"(.*?)\\\\\")|([^\\s;]*))", boost::regex::icase | boost::regex::normal);
+ boost::smatch result;
+ if (boost::regex_search(head_info.m_content_type, result, rexp_match_multipart_type, boost::match_default) && result[0].matched)
+ {
+ if (result[4].matched)
+ boundary = result[4];
+ else if (result[6].matched)
+ boundary = result[6];
+ else if (result[7].matched)
+ boundary = result[7];
+ else
+ {
+ LOG_ERROR("Failed to match boundary in content-type=" << head_info.m_content_type);
+ return false;
+ }
+ return true;
+ }
+ else
+ return false;
+
+ return true;
+ }
+ };
+ // class http_simple_client
+
+ typedef http_simple_client_t http_simple_client;
+ typedef http_simple_client_t https_simple_client;
+
+
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+ template
+ bool invoke_request(const std::string& url, t_transport& tr, unsigned int timeout, const http_response_info** ppresponse_info, const std::string& method = "GET", const std::string& body = std::string(), const fields_list& additional_params = fields_list())
+ {
+ http::url_content u_c;
+ bool res = parse_url(url, u_c);
+
+ if (!tr.is_connected() && !u_c.host.empty())
+ {
+ CHECK_AND_ASSERT_MES(res, false, "failed to parse url: " << url);
+
+ if (!u_c.port)
+ u_c.port = 80;//default for http
+
+ if (!tr.connect(u_c.host, static_cast(u_c.port), timeout))
+ {
+ LOG_PRINT_L2("invoke_request: cannot connect to " << u_c.host << ":" << u_c.port);
+ return false;
}
}
- if (current_err_count >= fails_count)
- {
- LOG_PRINT_YELLOW("Downloading from " << url << " FAILED as it's reached maximum (" << fails_count << ") number of attempts. Downloaded " << state_received_bytes_base << " bytes.", LOG_LEVEL_0);
- }
- else if (m_permanent_error)
- {
- LOG_PRINT_YELLOW("Downloading from " << url << " FAILED due to permanent HTTP error. Downloaded " << state_received_bytes_base << " bytes.", LOG_LEVEL_0);
- }
-
- fs.close();
- return r;
+ return tr.invoke(u_c.uri, method, body, ppresponse_info, additional_params);
}
- };
+ struct idle_handler_base
+ {
+ virtual bool do_call(const std::string& piece_of_data, uint64_t total_bytes, uint64_t received_bytes) = 0;
+ virtual ~idle_handler_base() {}
+ };
- } // namespace http
+ template
+ struct idle_handler : public idle_handler_base
+ {
+ callback_t m_cb;
-} // namespace net_utils
-} // namespace epee
+ idle_handler(callback_t cb) : m_cb(cb) {}
+ virtual bool do_call(const std::string& piece_of_data, uint64_t total_bytes, uint64_t received_bytes)
+ {
+ return m_cb(piece_of_data, total_bytes, received_bytes);
+ }
+ };
+
+ class interruptible_http_client : public http_simple_client
+ {
+ std::shared_ptr m_pcb;
+ bool m_permanent_error = false;
+
+ virtual bool handle_target_data(std::string& piece_of_transfer)
+ {
+ bool r = m_pcb->do_call(piece_of_transfer, m_len_in_summary, m_len_in_summary - m_len_in_remain);
+ piece_of_transfer.clear();
+ return r;
+ }
+
+ public:
+ template
+ bool invoke_cb(callback_t cb, const std::string& url, uint64_t timeout, const std::string& method = "GET", const std::string& body = std::string(), const fields_list& additional_params = fields_list())
+ {
+ m_pcb.reset(new idle_handler(cb));
+ const http_response_info* p_hri = nullptr;
+ bool r = invoke_request(url, *this, timeout, &p_hri, method, body, additional_params);
+ if (p_hri && !(p_hri->m_response_code >= 200 && p_hri->m_response_code < 300))
+ {
+ LOG_PRINT_L0("HTTP request to " << url << " failed with code: " << p_hri->m_response_code);
+ m_permanent_error = true;
+ return false;
+ }
+ return r;
+ }
+
+ template
+ bool download(callback_t cb, const std::string& path_for_file, const std::string& url, uint64_t timeout, const std::string& method = "GET", const std::string& body = std::string(), const fields_list& additional_params = fields_list())
+ {
+ std::ofstream fs;
+ fs.open(path_for_file, std::ios::binary | std::ios::out | std::ios::trunc);
+ if (!fs.is_open())
+ {
+ LOG_ERROR("Fsiled to open " << path_for_file);
+ return false;
+ }
+ auto local_cb = [&](const std::string& piece_of_data, uint64_t total_bytes, uint64_t received_bytes)
+ {
+ fs.write(piece_of_data.data(), piece_of_data.size());
+ return cb(total_bytes, received_bytes);
+ };
+ bool r = this->invoke_cb(local_cb, url, timeout, method, body, additional_params);
+ fs.close();
+ return r;
+ }
+
+ //
+ template
+ bool download_and_unzip(callback_t cb, const std::string& path_for_file, const std::string& url, uint64_t timeout, const std::string& method = "GET", const std::string& body = std::string(), uint64_t fails_count = 1000, const fields_list& additional_params = fields_list())
+ {
+ std::ofstream fs;
+ fs.open(path_for_file, std::ios::binary | std::ios::out | std::ios::trunc);
+ if (!fs.is_open())
+ {
+ LOG_ERROR("Fsiled to open " << path_for_file);
+ return false;
+ }
+ std::string buff;
+ gzip_decoder_lambda zip_decoder;
+ uint64_t state_total_bytes = 0;
+ uint64_t state_received_bytes_base = 0;
+ uint64_t state_received_bytes_current = 0;
+ bool stopped = false;
+ auto local_cb = [&](const std::string& piece_of_data, uint64_t total_bytes, uint64_t received_bytes)
+ {
+ //remember total_bytes only for first attempt, where fetched full lenght of the file
+ if (!state_total_bytes)
+ state_total_bytes = total_bytes;
+
+ buff += piece_of_data;
+ return zip_decoder.update_in(buff, [&](const std::string& unpacked_buff)
+ {
+ state_received_bytes_current = received_bytes;
+ fs.write(unpacked_buff.data(), unpacked_buff.size());
+ stopped = !cb(unpacked_buff, state_total_bytes, state_received_bytes_base + received_bytes);
+ return !stopped;
+ });
+ };
+ uint64_t current_err_count = 0;
+ bool r = false;
+ m_permanent_error = false;
+ while (!r && current_err_count < fails_count)
+ {
+ LOG_PRINT_L0("Attempt " << current_err_count + 1 << "/" << fails_count << " to get " << url << " (offset:" << state_received_bytes_base << ")");
+ fields_list additional_params_local = additional_params;
+ additional_params_local.push_back(std::make_pair("Range", std::string("bytes=") + std::to_string(state_received_bytes_base) + "-"));
+ r = this->invoke_cb(local_cb, url, timeout, method, body, additional_params_local);
+ if (!r)
+ {
+ if (stopped || m_permanent_error)
+ break;
+ current_err_count++;
+ state_received_bytes_base += state_received_bytes_current;
+ state_received_bytes_current = 0;
+ boost::this_thread::sleep_for(boost::chrono::milliseconds(2000));
+ }
+ }
+
+ if (current_err_count >= fails_count)
+ {
+ LOG_PRINT_YELLOW("Downloading from " << url << " FAILED as it's reached maximum (" << fails_count << ") number of attempts. Downloaded " << state_received_bytes_base << " bytes.", LOG_LEVEL_0);
+ }
+ else if (m_permanent_error)
+ {
+ LOG_PRINT_YELLOW("Downloading from " << url << " FAILED due to permanent HTTP error. Downloaded " << state_received_bytes_base << " bytes.", LOG_LEVEL_0);
+ }
+
+ fs.close();
+ return r;
+ }
+ };
+
+ template
+ bool fetch_url_t(transport_t& tr, const url_content& u_c, std::string& response_body, const std::string& method = "GET", const std::string& request_body = "", unsigned int timeout = 1000)
+ {
+ fields_list additional_params;
+ if (!tr.is_connected() && !u_c.host.empty())
+ {
+ if (!tr.connect(u_c.host, static_cast(u_c.port), timeout))
+ {
+ LOG_PRINT_L2("invoke_request: cannot connect to " << u_c.host << ":" << u_c.port);
+ return false;
+ }
+ }
+ const http_response_info* ppresponse_info = nullptr;
+ if (tr.invoke(u_c.uri, "GET", request_body, &ppresponse_info, additional_params) && ppresponse_info)
+ {
+ response_body = ppresponse_info->m_body;
+ return true;
+ }
+ return false;
+ }
+
+ inline
+ bool fetch_url(const std::string& url, std::string& response_body, const std::string& method = "GET", const std::string& request_body = "", unsigned int timeout = 1000)
+ {
+ try
+ {
+ url_content u_c = AUTO_VAL_INIT(u_c);
+ bool res = epee::net_utils::parse_url(url, u_c);
+ CHECK_AND_ASSERT_MES(res, false, "failed to parse url: " << url);
+ if (u_c.schema == "https")
+ {
+ https_simple_client tr;
+ return fetch_url_t(tr, u_c, response_body, method, request_body, timeout);
+ }
+ else
+ {
+ http_simple_client tr;
+ return fetch_url_t(tr, u_c, response_body, method, request_body, timeout);
+ }
+ }
+ catch (...)
+ {
+ return false;
+ }
+
+ }
+
+ } // namespace http
+
+ } // namespace net_utils
+} // namespace epee
\ No newline at end of file
diff --git a/contrib/epee/include/net/http_server_handlers_map2.h b/contrib/epee/include/net/http_server_handlers_map2.h
index 6b45950a..3102cb2d 100644
--- a/contrib/epee/include/net/http_server_handlers_map2.h
+++ b/contrib/epee/include/net/http_server_handlers_map2.h
@@ -29,13 +29,22 @@
#include "serialization/keyvalue_serialization.h"
#include "storages/portable_storage_template_helper.h"
#include "http_base.h"
+#include "net/net_utils_base.h"
+
+
+
+template
+typename_t get_documentation_json_struct()
+{
+ return AUTO_VAL_INIT_T(typename_t);
+}
template
bool auto_doc_t(const std::string& prefix_name, std::string& generate_reference)
{
if (!generate_reference.size()) return true;
- request_t req = AUTO_VAL_INIT(req);
- response_t res = AUTO_VAL_INIT(res);
+ request_t req = get_documentation_json_struct();
+ response_t res = get_documentation_json_struct();
std::stringstream ss;
ss << prefix_name << ENDL
<< "REQUEST: " << ENDL << epee::serialization::store_t_to_json(req) << ENDL << "--------------------------------" << ENDL
@@ -51,6 +60,20 @@ bool auto_doc(const std::string& prefix_name, std::string& generate_reference)
return auto_doc_t(prefix_name, generate_reference);
}
+namespace epee {
+ namespace net_utils {
+ namespace http {
+ struct i_chain_handler
+ {
+ virtual bool handle_http_request_map(const epee::net_utils::http::http_request_info& query_info, epee::net_utils::http::http_response_info& response_info,
+ epee::net_utils::connection_context_base& m_conn_context, bool& call_found, std::string& generate_reference) = 0;
+ };
+ }
+ }
+}
+
+
+
#define CHAIN_HTTP_TO_MAP2(context_type) bool handle_http_request(const epee::net_utils::http::http_request_info& query_info, \
epee::net_utils::http::http_response_info& response, \
@@ -73,6 +96,13 @@ bool auto_doc(const std::string& prefix_name, std::string& generate_reference)
call_found = false; \
if(false) return true; //just a stub to have "else if"
+#define BEGIN_URI_MAP2_VIRTUAL() virtual bool handle_http_request_map(const epee::net_utils::http::http_request_info& query_info, \
+ epee::net_utils::http::http_response_info& response_info, \
+ epee::net_utils::connection_context_base& m_conn_context, bool& call_found, std::string& generate_reference) { \
+ call_found = false; \
+ if(false) return true; //just a stub to have "else if"
+
+
#define MAP_URI2(pattern, callback) else if(std::string::npos != query_info.m_URI.find(pattern)) return callback(query_info, response_info, m_conn_context);
#define MAP_URI_AUTO_XML2(s_pattern, callback_f, command_type) //TODO: don't think i ever again will use xml - ambiguous and "overtagged" format
@@ -117,6 +147,8 @@ bool auto_doc(const std::string& prefix_name, std::string& generate_reference)
LOG_PRINT( "[HTTP/BIN][" << epee::string_tools::get_ip_string_from_int32(m_conn_context.m_remote_ip ) << "][" << query_info.m_URI << "] processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \
}
+#define CHAIN_TO_PHANDLER(pi_chain_handler) else if (pi_chain_handler && pi_chain_handler->handle_http_request_map(query_info, response_info, m_conn_context, call_found, generate_reference) && call_found) { return true;}
+
#define CHAIN_URI_MAP2(callback) else {callback(query_info, response_info, m_conn_context);call_found = true;}
#define END_URI_MAP2() return true;}
@@ -287,6 +319,7 @@ struct json_command_type_t
#define MAP_JON_RPC_WE(method_name, callback_f, command_type) \
else if(auto_doc>("[" method_name "]", generate_reference) && callback_name == method_name) \
{ \
+ call_found = true; \
PREPARE_OBJECTS_FROM_JSON(command_type) \
epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \
fail_resp.jsonrpc = "2.0"; \
@@ -304,6 +337,7 @@ struct json_command_type_t
#define MAP_JON_RPC_WERI(method_name, callback_f, command_type) \
else if(auto_doc>("[" method_name "]", generate_reference) && callback_name == method_name) \
{ \
+ call_found = true; \
PREPARE_OBJECTS_FROM_JSON(command_type) \
epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \
fail_resp.jsonrpc = "2.0"; \
@@ -321,6 +355,7 @@ struct json_command_type_t
#define MAP_JON_RPC(method_name, callback_f, command_type) \
else if(auto_doc>(std::string("[") + method_name + "]", generate_reference) && callback_name == method_name) \
{ \
+ call_found = true; \
PREPARE_OBJECTS_FROM_JSON(command_type) \
if(!callback_f(req.params, resp.result, m_conn_context)) \
{ \
diff --git a/contrib/epee/include/net/http_server_impl_base.h b/contrib/epee/include/net/http_server_impl_base.h
index c02475c3..4b083804 100644
--- a/contrib/epee/include/net/http_server_impl_base.h
+++ b/contrib/epee/include/net/http_server_impl_base.h
@@ -31,7 +31,7 @@
#include
-#include
+#include
#include "net/http_server_cp2.h"
#include "net/http_server_handlers_map2.h"
@@ -65,7 +65,7 @@ namespace epee
bool res = m_net_server.init_server(bind_port, bind_ip);
if(!res)
{
- LOG_ERROR("Failed to bind server");
+ LOG_ERROR("Failed to bind to " << bind_ip << ":" << bind_port);
return false;
}
return true;
@@ -74,14 +74,14 @@ namespace epee
bool run(size_t threads_count, bool wait = true)
{
//go to loop
- LOG_PRINT("Run net_service loop( " << threads_count << " threads)...", LOG_LEVEL_0);
+ LOG_PRINT("Run net_service loop (" << threads_count << " threads)...", LOG_LEVEL_1);
if(!m_net_server.run_server(threads_count, wait))
{
LOG_ERROR("Failed to run net tcp server!");
}
if(wait)
- LOG_PRINT("net_service loop stopped.", LOG_LEVEL_0);
+ LOG_PRINT("net_service loop stopped.", LOG_LEVEL_1);
return true;
}
diff --git a/contrib/epee/include/net/net_helper.h b/contrib/epee/include/net/net_helper.h
index 72ff4e75..4a42e679 100644
--- a/contrib/epee/include/net/net_helper.h
+++ b/contrib/epee/include/net/net_helper.h
@@ -38,12 +38,14 @@
#include
#include
#include
+#include
#include
#include
#include
#include
#include "net/net_utils_base.h"
#include "misc_language.h"
+#include "misc_helpers.h"
//#include "profile_tools.h"
#include "../string_tools.h"
@@ -54,637 +56,744 @@
namespace epee
{
-namespace net_utils
-{
+ namespace net_utils
+ {
- class blocked_mode_client
- {
-
-
- struct handler_obj
- {
- handler_obj(boost::system::error_code& error, size_t& bytes_transferred):ref_error(error), ref_bytes_transferred(bytes_transferred)
- {}
- handler_obj(const handler_obj& other_obj):ref_error(other_obj.ref_error), ref_bytes_transferred(other_obj.ref_bytes_transferred)
- {}
-
- boost::system::error_code& ref_error;
- size_t& ref_bytes_transferred;
-
- void operator()(const boost::system::error_code& error, // Result of operation.
- std::size_t bytes_transferred // Number of bytes read.
- )
- {
- ref_error = error;
- ref_bytes_transferred = bytes_transferred;
- }
- };
-
- public:
- inline
- blocked_mode_client():m_socket(m_io_service),
- m_initialized(false),
- m_connected(false),
- m_deadline(m_io_service),
- m_shutdowned(0),
- m_connect_timeout{},
- m_reciev_timeout{}
- {
-
-
- m_initialized = true;
+ template
+ struct socket_backend;
- // No deadline is required until the first socket operation is started. We
- // set the deadline to positive infinity so that the actor takes no action
- // until a specific deadline is set.
- m_deadline.expires_at(boost::posix_time::pos_infin);
-
- // Start the persistent actor that checks for deadline expiry.
- check_deadline();
-
- }
- inline
- ~blocked_mode_client()
- {
- NESTED_TRY_ENTRY();
-
- //profile_tools::local_coast lc("~blocked_mode_client()", 3);
- shutdown();
-
- NESTED_CATCH_ENTRY(__func__);
- }
-
- inline void set_recv_timeout(int reciev_timeout)
- {
- m_reciev_timeout = reciev_timeout;
- }
-
- inline
- bool connect(const std::string& addr, int port, unsigned int connect_timeout, unsigned int reciev_timeout, const std::string& bind_ip = "0.0.0.0")
+ template<>
+ struct socket_backend
{
- return connect(addr, std::to_string(port), connect_timeout, reciev_timeout, bind_ip);
- }
+ socket_backend(boost::asio::io_service& _io_service): m_ssl_context(boost::asio::ssl::context::sslv23), m_socket(_io_service, m_ssl_context)
+ {
+ // Create a context that uses the default paths for
+ // finding CA certificates.
+ m_ssl_context.set_default_verify_paths();
+ /*m_socket.set_verify_mode(boost::asio::ssl::verify_peer);
+ m_socket.set_verify_callback(
+ boost::bind(&socket_backend::verify_certificate, this, _1, _2));*/
- inline
- bool connect(const std::string& addr, const std::string& port, unsigned int connect_timeout, unsigned int reciev_timeout, const std::string& bind_ip = "0.0.0.0")
- {
- m_connect_timeout = connect_timeout;
- m_reciev_timeout = reciev_timeout;
- m_connected = false;
- if(!m_reciev_timeout)
- m_reciev_timeout = m_connect_timeout;
+ }
- try
- {
- m_socket.close();
- // Get a list of endpoints corresponding to the server name.
-
+ /*
+ bool verify_certificate(bool preverified,
+ boost::asio::ssl::verify_context& ctx)
+ {
+ std::cout << "verify_certificate (preverified " << preverified << " ) ...\n";
+ // The verify callback can be used to check whether the certificate that is
+ // being presented is valid for the peer. For example, RFC 2818 describes
+ // the steps involved in doing this for HTTPS. Consult the OpenSSL
+ // documentation for more details. Note that the callback is called once
+ // for each certificate in the certificate chain, starting from the root
+ // certificate authority.
- //////////////////////////////////////////////////////////////////////////
+ // In this example we will simply print the certificate's subject name.
+ char subject_name[256];
+ X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
+ X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
+ std::cout << "Verifying " << subject_name << "\n";
- boost::asio::ip::tcp::resolver resolver(m_io_service);
- boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), addr, port);
- boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
- boost::asio::ip::tcp::resolver::iterator end;
- if(iterator == end)
- {
- LOG_ERROR("Failed to resolve " << addr);
- return false;
- }
+ // dummy verification
+ return true;
+ }*/
- //////////////////////////////////////////////////////////////////////////
+ void set_domain(const std::string& domain_name)
+ {
+ SSL_set_tlsext_host_name(m_socket.native_handle(), domain_name.c_str());
+ }
+
+ boost::asio::ip::tcp::socket& get_socket()
+ {
+ return m_socket.next_layer();
+ }
+
+ auto& get_stream()
+ {
+ return m_socket;
+ }
+
+ void on_after_connect()
+ {
+ m_socket.handshake(boost::asio::ssl::stream_base::client);
+ }
+
+ private:
+ boost::asio::ssl::context m_ssl_context;
+ boost::asio::ssl::stream m_socket;
+ };
+
+ template<>
+ struct socket_backend
+ {
+ socket_backend(boost::asio::io_service& _io_service): m_socket(_io_service)
+ {}
+
+ boost::asio::ip::tcp::socket& get_socket()
+ {
+ return m_socket;
+ }
+
+ void set_domain(const std::string& domain_name)
+ {
+
+ }
+
+ boost::asio::ip::tcp::socket& get_stream()
+ {
+ return m_socket;
+ }
+
+ void on_after_connect()
+ {
+
+ }
+ private:
+ boost::asio::ip::tcp::socket m_socket;
+ };
- //boost::asio::ip::tcp::endpoint remote_endpoint(boost::asio::ip::address::from_string(addr.c_str()), port);
- boost::asio::ip::tcp::endpoint remote_endpoint(*iterator);
- m_socket.open(remote_endpoint.protocol());
- if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" )
- {
- boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(addr.c_str()), 0);
- m_socket.bind(local_endpoint);
- }
+ template
+ class blocked_mode_client_t
+ {
+ struct handler_obj
+ {
+ handler_obj(boost::system::error_code& error, size_t& bytes_transferred) :ref_error(error), ref_bytes_transferred(bytes_transferred)
+ {}
+ handler_obj(const handler_obj& other_obj) :ref_error(other_obj.ref_error), ref_bytes_transferred(other_obj.ref_bytes_transferred)
+ {}
-
- m_deadline.expires_from_now(boost::posix_time::milliseconds(m_connect_timeout));
+ boost::system::error_code& ref_error;
+ size_t& ref_bytes_transferred;
+
+ void operator()(const boost::system::error_code& error, // Result of operation.
+ std::size_t bytes_transferred // Number of bytes read.
+ )
+ {
+ ref_error = error;
+ ref_bytes_transferred = bytes_transferred;
+ }
+ };
+
+ public:
+ inline
+ blocked_mode_client_t() :m_sct_back(m_io_service),
+ m_initialized(false),
+ m_connected(false),
+ m_deadline(m_io_service),
+ m_shutdowned(0),
+ m_connect_timeout{},
+ m_reciev_timeout{}
+ {
- boost::system::error_code ec = boost::asio::error::would_block;
-
- //m_socket.connect(remote_endpoint);
- m_socket.async_connect(remote_endpoint, boost::lambda::var(ec) = boost::lambda::_1);
- while (ec == boost::asio::error::would_block)
- {
- m_io_service.run_one();
- }
-
- if (!ec && m_socket.is_open())
- {
- m_connected = true;
- m_deadline.expires_at(boost::posix_time::pos_infin);
- return true;
- }else
- {
- LOG_PRINT("Some problems at connect, message: " << ec.message(), LOG_LEVEL_3);
- return false;
- }
-
- }
- catch(const boost::system::system_error& er)
- {
- LOG_PRINT("Some problems at connect, message: " << er.what(), LOG_LEVEL_4);
- return false;
- }
- catch(...)
- {
- LOG_PRINT("Some fatal problems.", LOG_LEVEL_4);
- return false;
- }
-
- return true;
- }
+ m_initialized = true;
- inline
- bool disconnect()
- {
- try
- {
- if(m_connected)
- {
- m_connected = false;
- m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
-
- }
- }
-
- catch(const boost::system::system_error& /*er*/)
- {
- //LOG_ERROR("Some problems at disconnect, message: " << er.what());
- return false;
- }
- catch(...)
- {
- //LOG_ERROR("Some fatal problems.");
- return false;
- }
- return true;
- }
+ // No deadline is required until the first socket operation is started. We
+ // set the deadline to positive infinity so that the actor takes no action
+ // until a specific deadline is set.
+ m_deadline.expires_at(boost::posix_time::pos_infin);
+
+ // Start the persistent actor that checks for deadline expiry.
+ check_deadline();
+
+ }
+ inline
+ ~blocked_mode_client_t()
+ {
+ NESTED_TRY_ENTRY();
+
+ //profile_tools::local_coast lc("~blocked_mode_client()", 3);
+ shutdown();
+
+ NESTED_CATCH_ENTRY(__func__);
+ }
+
+ inline void set_recv_timeout(int reciev_timeout)
+ {
+ m_reciev_timeout = reciev_timeout;
+ }
+
+ inline
+ bool connect(const std::string& addr, int port, unsigned int connect_timeout, unsigned int reciev_timeout, const std::string& bind_ip = "0.0.0.0")
+ {
+ return connect(addr, std::to_string(port), connect_timeout, reciev_timeout, bind_ip);
+ }
+
+ inline
+ bool connect(const std::string& addr, const std::string& port, unsigned int connect_timeout, unsigned int reciev_timeout, const std::string& bind_ip = "0.0.0.0")
+ {
+ m_connect_timeout = connect_timeout;
+ m_reciev_timeout = reciev_timeout;
+ m_connected = false;
+ if (!m_reciev_timeout)
+ m_reciev_timeout = m_connect_timeout;
+
+ try
+ {
+ m_sct_back.get_socket().close();
+ // Get a list of endpoints corresponding to the server name.
- inline
- bool send(const std::string& buff)
- {
+ //////////////////////////////////////////////////////////////////////////
- try
- {
- m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout));
+ boost::asio::ip::tcp::resolver resolver(m_io_service);
+ boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), addr, port);
+ boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
+ boost::asio::ip::tcp::resolver::iterator end;
+ if (iterator == end)
+ {
+ LOG_ERROR("Failed to resolve " << addr);
+ return false;
+ }
- // Set up the variable that receives the result of the asynchronous
- // operation. The error code is set to would_block to signal that the
- // operation is incomplete. Asio guarantees that its asynchronous
- // operations will never fail with would_block, so any other value in
- // ec indicates completion.
- boost::system::error_code ec = boost::asio::error::would_block;
+ //////////////////////////////////////////////////////////////////////////
+ m_sct_back.set_domain(addr);
- // Start the asynchronous operation itself. The boost::lambda function
- // object is used as a callback and will update the ec variable when the
- // operation completes. The blocking_udp_client.cpp example shows how you
- // can use boost::bind rather than boost::lambda.
- boost::asio::async_write(m_socket, boost::asio::buffer(buff), boost::lambda::var(ec) = boost::lambda::_1);
+ //boost::asio::ip::tcp::endpoint remote_endpoint(boost::asio::ip::address::from_string(addr.c_str()), port);
+ boost::asio::ip::tcp::endpoint remote_endpoint(*iterator);
- // Block until the asynchronous operation has completed.
- while (ec == boost::asio::error::would_block)
- {
- m_io_service.run_one();
- }
- if (ec)
- {
- LOG_PRINT_L3("Problems at write: " << ec.message());
+ m_sct_back.get_socket().open(remote_endpoint.protocol());
+ if (bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "")
+ {
+ boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(addr.c_str()), 0);
+ m_sct_back.get_socket().bind(local_endpoint);
+ }
+
+
+ m_deadline.expires_from_now(boost::posix_time::milliseconds(m_connect_timeout));
+
+
+ boost::system::error_code ec = boost::asio::error::would_block;
+
+ //m_sct_back.get_socket().connect(remote_endpoint);
+ m_sct_back.get_socket().async_connect(remote_endpoint, boost::lambda::var(ec) = boost::lambda::_1);
+ while (ec == boost::asio::error::would_block)
+ {
+ m_io_service.run_one();
+ }
+
+ if (!ec && m_sct_back.get_socket().is_open())
+ {
+ m_sct_back.on_after_connect();
+ m_connected = true;
+ m_deadline.expires_at(boost::posix_time::pos_infin);
+ return true;
+ }
+ else
+ {
+ LOG_PRINT("Some problems at connect, message: " << ec.message(), LOG_LEVEL_3);
+ return false;
+ }
+
+ }
+ catch (const boost::system::system_error& er)
+ {
+ LOG_PRINT("Some problems at connect, message: " << er.what(), LOG_LEVEL_4);
+ return false;
+ }
+ catch (...)
+ {
+ LOG_PRINT("Some fatal problems.", LOG_LEVEL_4);
+ return false;
+ }
+
+ return true;
+ }
+
+
+ inline
+ bool disconnect()
+ {
+ try
+ {
+ if (m_connected)
+ {
+ m_connected = false;
+ m_sct_back.get_socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both);
+
+ }
+ }
+
+ catch (const boost::system::system_error& /*er*/)
+ {
+ //LOG_ERROR("Some problems at disconnect, message: " << er.what());
+ return false;
+ }
+ catch (...)
+ {
+ //LOG_ERROR("Some fatal problems.");
+ return false;
+ }
+ return true;
+ }
+
+
+ inline
+ bool send(const std::string& buff)
+ {
+
+ try
+ {
+ m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout));
+
+ // Set up the variable that receives the result of the asynchronous
+ // operation. The error code is set to would_block to signal that the
+ // operation is incomplete. Asio guarantees that its asynchronous
+ // operations will never fail with would_block, so any other value in
+ // ec indicates completion.
+ boost::system::error_code ec = boost::asio::error::would_block;
+
+ // Start the asynchronous operation itself. The boost::lambda function
+ // object is used as a callback and will update the ec variable when the
+ // operation completes. The blocking_udp_client.cpp example shows how you
+ // can use boost::bind rather than boost::lambda.
+ boost::asio::async_write(m_sct_back.get_stream(), boost::asio::buffer(buff), boost::lambda::var(ec) = boost::lambda::_1);
+
+ // Block until the asynchronous operation has completed.
+ while (ec == boost::asio::error::would_block)
+ {
+ m_io_service.run_one();
+ }
+
+ if (ec)
+ {
+ LOG_PRINT_L3("Problems at write: " << ec.message());
+ m_connected = false;
+ return false;
+ }
+ else
+ {
+ m_deadline.expires_at(boost::posix_time::pos_infin);
+ }
+ }
+
+ catch (const boost::system::system_error& er)
+ {
+ LOG_ERROR("Some problems at connect, message: " << er.what());
+ return false;
+ }
+ catch (...)
+ {
+ LOG_ERROR("Some fatal problems.");
+ return false;
+ }
+
+ return true;
+ }
+
+ inline
+ bool send(const void* data, size_t sz)
+ {
+ try
+ {
+ /*
+ m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout));
+
+ // Set up the variable that receives the result of the asynchronous
+ // operation. The error code is set to would_block to signal that the
+ // operation is incomplete. Asio guarantees that its asynchronous
+ // operations will never fail with would_block, so any other value in
+ // ec indicates completion.
+ boost::system::error_code ec = boost::asio::error::would_block;
+
+ // Start the asynchronous operation itself. The boost::lambda function
+ // object is used as a callback and will update the ec variable when the
+ // operation completes. The blocking_udp_client.cpp example shows how you
+ // can use boost::bind rather than boost::lambda.
+ boost::asio::async_write(m_sct_back.get_socket(), boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1);
+
+ // Block until the asynchronous operation has completed.
+ while (ec == boost::asio::error::would_block)
+ {
+ m_io_service.run_one();
+ }
+ */
+ boost::system::error_code ec;
+
+ size_t writen = m_sct_back.get_stream().write_some(boost::asio::buffer(data, sz), ec);
+
+
+
+ if (!writen || ec)
+ {
+ LOG_PRINT_L3("Problems at write: " << ec.message());
+ m_connected = false;
+ return false;
+ }
+ else
+ {
+ m_deadline.expires_at(boost::posix_time::pos_infin);
+ }
+ }
+
+ catch (const boost::system::system_error& er)
+ {
+ LOG_ERROR("Some problems at send, message: " << er.what());
m_connected = false;
- return false;
- }else
- {
- m_deadline.expires_at(boost::posix_time::pos_infin);
- }
- }
+ return false;
+ }
+ catch (...)
+ {
+ LOG_ERROR("Some fatal problems.");
+ return false;
+ }
- catch(const boost::system::system_error& er)
- {
- LOG_ERROR("Some problems at connect, message: " << er.what());
- return false;
- }
- catch(...)
- {
- LOG_ERROR("Some fatal problems.");
- return false;
- }
+ return true;
+ }
- return true;
- }
+ bool is_connected()
+ {
+ return m_connected && m_sct_back.get_socket().is_open();
+ //TRY_ENTRY()
+ //return m_socket.is_open();
+ //CATCH_ENTRY_L0("is_connected", false)
+ }
- inline
- bool send(const void* data, size_t sz)
- {
- try
- {
- /*
- m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout));
+ inline
+ bool recv(std::string& buff)
+ {
- // Set up the variable that receives the result of the asynchronous
- // operation. The error code is set to would_block to signal that the
- // operation is incomplete. Asio guarantees that its asynchronous
- // operations will never fail with would_block, so any other value in
- // ec indicates completion.
- boost::system::error_code ec = boost::asio::error::would_block;
+ try
+ {
+ // Set a deadline for the asynchronous operation. Since this function uses
+ // a composed operation (async_read_until), the deadline applies to the
+ // entire operation, rather than individual reads from the socket.
+ m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout));
- // Start the asynchronous operation itself. The boost::lambda function
- // object is used as a callback and will update the ec variable when the
- // operation completes. The blocking_udp_client.cpp example shows how you
- // can use boost::bind rather than boost::lambda.
- boost::asio::async_write(m_socket, boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1);
+ // Set up the variable that receives the result of the asynchronous
+ // operation. The error code is set to would_block to signal that the
+ // operation is incomplete. Asio guarantees that its asynchronous
+ // operations will never fail with would_block, so any other value in
+ // ec indicates completion.
+ //boost::system::error_code ec = boost::asio::error::would_block;
- // Block until the asynchronous operation has completed.
- while (ec == boost::asio::error::would_block)
- {
- m_io_service.run_one();
- }
- */
- boost::system::error_code ec;
+ // Start the asynchronous operation itself. The boost::lambda function
+ // object is used as a callback and will update the ec variable when the
+ // operation completes. The blocking_udp_client.cpp example shows how you
+ // can use boost::bind rather than boost::lambda.
- size_t writen = m_socket.write_some(boost::asio::buffer(data, sz), ec);
-
+ boost::system::error_code ec = boost::asio::error::would_block;
+ size_t bytes_transfered = 0;
+
+ handler_obj hndlr(ec, bytes_transfered);
+
+ char local_buff[10000] = { 0 };
+ //m_socket.async_read_some(boost::asio::buffer(local_buff, sizeof(local_buff)), hndlr);
+ boost::asio::async_read(m_sct_back.get_stream(), boost::asio::buffer(local_buff, sizeof(local_buff)), boost::asio::transfer_at_least(1), hndlr);
+
+ // Block until the asynchronous operation has completed.
+ while (ec == boost::asio::error::would_block && !boost::interprocess::ipcdetail::atomic_read32(&m_shutdowned))
+ {
+ m_io_service.run_one();
+ }
- if (!writen || ec)
- {
- LOG_PRINT_L3("Problems at write: " << ec.message());
+ if (ec)
+ {
+ LOG_PRINT_L4("READ ENDS: Connection err_code " << ec.value());
+ if (ec == boost::asio::error::eof)
+ {
+ LOG_PRINT_L4("Connection err_code eof.");
+ //connection closed there, empty
+ return true;
+ }
+
+ LOG_PRINT_L3("Problems at read: " << ec.message());
+ m_connected = false;
+ return false;
+ }
+ else
+ {
+ LOG_PRINT_L4("READ ENDS: Success. bytes_tr: " << bytes_transfered);
+ m_deadline.expires_at(boost::posix_time::pos_infin);
+ }
+
+ /*if(!bytes_transfered)
+ return false;*/
+
+ buff.assign(local_buff, bytes_transfered);
+ return true;
+ }
+
+ catch (const boost::system::system_error& er)
+ {
+ LOG_ERROR("Some problems at read, message: " << er.what());
m_connected = false;
- return false;
- }else
- {
- m_deadline.expires_at(boost::posix_time::pos_infin);
- }
- }
-
- catch(const boost::system::system_error& er)
- {
- LOG_ERROR("Some problems at send, message: " << er.what());
- m_connected = false;
- return false;
- }
- catch(...)
- {
- LOG_ERROR("Some fatal problems.");
- return false;
- }
-
- return true;
- }
-
- bool is_connected()
- {
- return m_connected && m_socket.is_open();
- //TRY_ENTRY()
- //return m_socket.is_open();
- //CATCH_ENTRY_L0("is_connected", false)
- }
-
- inline
- bool recv(std::string& buff)
- {
-
- try
- {
- // Set a deadline for the asynchronous operation. Since this function uses
- // a composed operation (async_read_until), the deadline applies to the
- // entire operation, rather than individual reads from the socket.
- m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout));
-
- // Set up the variable that receives the result of the asynchronous
- // operation. The error code is set to would_block to signal that the
- // operation is incomplete. Asio guarantees that its asynchronous
- // operations will never fail with would_block, so any other value in
- // ec indicates completion.
- //boost::system::error_code ec = boost::asio::error::would_block;
-
- // Start the asynchronous operation itself. The boost::lambda function
- // object is used as a callback and will update the ec variable when the
- // operation completes. The blocking_udp_client.cpp example shows how you
- // can use boost::bind rather than boost::lambda.
-
- boost::system::error_code ec = boost::asio::error::would_block;
- size_t bytes_transfered = 0;
-
- handler_obj hndlr(ec, bytes_transfered);
-
- char local_buff[10000] = {0};
- //m_socket.async_read_some(boost::asio::buffer(local_buff, sizeof(local_buff)), hndlr);
- boost::asio::async_read(m_socket, boost::asio::buffer(local_buff, sizeof(local_buff)), boost::asio::transfer_at_least(1), hndlr);
-
- // Block until the asynchronous operation has completed.
- while (ec == boost::asio::error::would_block && !boost::interprocess::ipcdetail::atomic_read32(&m_shutdowned))
- {
- m_io_service.run_one();
- }
-
-
- if (ec)
- {
- LOG_PRINT_L4("READ ENDS: Connection err_code " << ec.value());
- if(ec == boost::asio::error::eof)
- {
- LOG_PRINT_L4("Connection err_code eof.");
- //connection closed there, empty
- return true;
- }
-
- LOG_PRINT_L3("Problems at read: " << ec.message());
- m_connected = false;
- return false;
- }else
- {
- LOG_PRINT_L4("READ ENDS: Success. bytes_tr: " << bytes_transfered);
- m_deadline.expires_at(boost::posix_time::pos_infin);
- }
-
- /*if(!bytes_transfered)
- return false;*/
-
- buff.assign(local_buff, bytes_transfered);
- return true;
- }
-
- catch(const boost::system::system_error& er)
- {
- LOG_ERROR("Some problems at read, message: " << er.what());
- m_connected = false;
- return false;
- }
- catch(...)
- {
- LOG_ERROR("Some fatal problems at read.");
- return false;
- }
+ return false;
+ }
+ catch (...)
+ {
+ LOG_ERROR("Some fatal problems at read.");
+ return false;
+ }
- return false;
+ return false;
- }
+ }
- inline bool recv_n(std::string& buff, int64_t sz)
- {
+ inline bool recv_n(std::string& buff, int64_t sz)
+ {
- try
- {
- // Set a deadline for the asynchronous operation. Since this function uses
- // a composed operation (async_read_until), the deadline applies to the
- // entire operation, rather than individual reads from the socket.
- m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout));
+ try
+ {
+ // Set a deadline for the asynchronous operation. Since this function uses
+ // a composed operation (async_read_until), the deadline applies to the
+ // entire operation, rather than individual reads from the socket.
+ m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout));
- // Set up the variable that receives the result of the asynchronous
- // operation. The error code is set to would_block to signal that the
- // operation is incomplete. Asio guarantees that its asynchronous
- // operations will never fail with would_block, so any other value in
- // ec indicates completion.
- //boost::system::error_code ec = boost::asio::error::would_block;
+ // Set up the variable that receives the result of the asynchronous
+ // operation. The error code is set to would_block to signal that the
+ // operation is incomplete. Asio guarantees that its asynchronous
+ // operations will never fail with would_block, so any other value in
+ // ec indicates completion.
+ //boost::system::error_code ec = boost::asio::error::would_block;
- // Start the asynchronous operation itself. The boost::lambda function
- // object is used as a callback and will update the ec variable when the
- // operation completes. The blocking_udp_client.cpp example shows how you
- // can use boost::bind rather than boost::lambda.
+ // Start the asynchronous operation itself. The boost::lambda function
+ // object is used as a callback and will update the ec variable when the
+ // operation completes. The blocking_udp_client.cpp example shows how you
+ // can use boost::bind rather than boost::lambda.
- buff.resize(static_cast(sz));
- boost::system::error_code ec = boost::asio::error::would_block;
- size_t bytes_transfered = 0;
+ buff.resize(static_cast(sz));
+ boost::system::error_code ec = boost::asio::error::would_block;
+ size_t bytes_transfered = 0;
-
- handler_obj hndlr(ec, bytes_transfered);
- //char local_buff[10000] = {0};
- boost::asio::async_read(m_socket, boost::asio::buffer((char*)buff.data(), buff.size()), boost::asio::transfer_at_least(buff.size()), hndlr);
+ handler_obj hndlr(ec, bytes_transfered);
- // Block until the asynchronous operation has completed.
- while (ec == boost::asio::error::would_block && !boost::interprocess::ipcdetail::atomic_read32(&m_shutdowned))
- {
- m_io_service.run_one();
- }
+ //char local_buff[10000] = {0};
+ boost::asio::async_read(m_sct_back.get_stream(), boost::asio::buffer((char*)buff.data(), buff.size()), boost::asio::transfer_at_least(buff.size()), hndlr);
- if (ec)
- {
- LOG_PRINT_L3("Problems at read: " << ec.message());
+ // Block until the asynchronous operation has completed.
+ while (ec == boost::asio::error::would_block && !boost::interprocess::ipcdetail::atomic_read32(&m_shutdowned))
+ {
+ m_io_service.run_one();
+ }
+
+ if (ec)
+ {
+ LOG_PRINT_L3("Problems at read: " << ec.message());
+ m_connected = false;
+ return false;
+ }
+ else
+ {
+ m_deadline.expires_at(boost::posix_time::pos_infin);
+ }
+
+ if (bytes_transfered != buff.size())
+ {
+ LOG_ERROR("Transferred missmatch with transfer_at_least value: m_bytes_transferred=" << bytes_transfered << " at_least value=" << buff.size());
+ return false;
+ }
+
+ return true;
+ }
+
+ catch (const boost::system::system_error& er)
+ {
+ LOG_ERROR("Some problems at read, message: " << er.what());
m_connected = false;
- return false;
- }else
- {
- m_deadline.expires_at(boost::posix_time::pos_infin);
- }
+ return false;
+ }
+ catch (...)
+ {
+ LOG_ERROR("Some fatal problems at read.");
+ return false;
+ }
- if(bytes_transfered != buff.size())
- {
- LOG_ERROR("Transferred missmatch with transfer_at_least value: m_bytes_transferred=" << bytes_transfered << " at_least value=" << buff.size());
- return false;
- }
- return true;
- }
- catch(const boost::system::system_error& er)
- {
- LOG_ERROR("Some problems at read, message: " << er.what());
+ return false;
+ }
+
+ bool shutdown()
+ {
+ m_deadline.cancel();
+ boost::system::error_code ignored_ec;
+ m_sct_back.get_socket().cancel(ignored_ec);
+ m_sct_back.get_socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
+ m_sct_back.get_socket().close(ignored_ec);
+ boost::interprocess::ipcdetail::atomic_write32(&m_shutdowned, 1);
m_connected = false;
- return false;
- }
- catch(...)
- {
- LOG_ERROR("Some fatal problems at read.");
- return false;
- }
+ return true;
+ }
+
+ void set_connected(bool connected)
+ {
+ m_connected = connected;
+ }
+ boost::asio::io_service& get_io_service()
+ {
+ return m_io_service;
+ }
+
+ boost::asio::ip::tcp::socket& get_socket()
+ {
+ return m_sct_back.get_socket();
+ }
+
+ private:
+
+ void check_deadline()
+ {
+ // Check whether the deadline has passed. We compare the deadline against
+ // the current time since a new asynchronous operation may have moved the
+ // deadline before this actor had a chance to run.
+ if (m_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now())
+ {
+ // The deadline has passed. The socket is closed so that any outstanding
+ // asynchronous operations are cancelled. This allows the blocked
+ // connect(), read_line() or write_line() functions to return.
+ LOG_PRINT_L3("Timed out socket");
+ m_connected = false;
+ m_sct_back.get_socket().close();
+
+ // There is no longer an active deadline. The expiry is set to positive
+ // infinity so that the actor takes no action until a new deadline is set.
+ m_deadline.expires_at(boost::posix_time::pos_infin);
+ }
+
+ // Put the actor back to sleep.
+ m_deadline.async_wait(boost::bind(&blocked_mode_client_t::check_deadline, this));
+ }
- return false;
- }
-
- bool shutdown()
- {
- m_deadline.cancel();
- boost::system::error_code ignored_ec;
- m_socket.cancel(ignored_ec);
- m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
- m_socket.close(ignored_ec);
- boost::interprocess::ipcdetail::atomic_write32(&m_shutdowned, 1);
- m_connected = false;
- return true;
- }
-
- void set_connected(bool connected)
- {
- m_connected = connected;
- }
- boost::asio::io_service& get_io_service()
- {
- return m_io_service;
- }
-
- boost::asio::ip::tcp::socket& get_socket()
- {
- return m_socket;
- }
-
- private:
-
- void check_deadline()
- {
- // Check whether the deadline has passed. We compare the deadline against
- // the current time since a new asynchronous operation may have moved the
- // deadline before this actor had a chance to run.
- if (m_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now())
- {
- // The deadline has passed. The socket is closed so that any outstanding
- // asynchronous operations are cancelled. This allows the blocked
- // connect(), read_line() or write_line() functions to return.
- LOG_PRINT_L3("Timed out socket");
- m_connected = false;
- m_socket.close();
-
- // There is no longer an active deadline. The expiry is set to positive
- // infinity so that the actor takes no action until a new deadline is set.
- m_deadline.expires_at(boost::posix_time::pos_infin);
- }
-
- // Put the actor back to sleep.
- m_deadline.async_wait(boost::bind(&blocked_mode_client::check_deadline, this));
- }
-
-
-
- protected:
- boost::asio::io_service m_io_service;
- boost::asio::ip::tcp::socket m_socket;
- int m_connect_timeout;
- int m_reciev_timeout;
- bool m_initialized;
- bool m_connected;
- boost::asio::deadline_timer m_deadline;
- volatile uint32_t m_shutdowned;
- };
+ protected:
+ boost::asio::io_service m_io_service;
+ socket_backend m_sct_back;
+ int m_connect_timeout;
+ int m_reciev_timeout;
+ bool m_initialized;
+ bool m_connected;
+ boost::asio::deadline_timer m_deadline;
+ volatile uint32_t m_shutdowned;
+ };
- /************************************************************************/
- /* */
- /************************************************************************/
- class async_blocked_mode_client: public blocked_mode_client
- {
- public:
- async_blocked_mode_client():m_send_deadline(blocked_mode_client::m_io_service)
- {
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+ template
+ class async_blocked_mode_client_t : public blocked_mode_client_t
+ {
+ public:
+ async_blocked_mode_client_t() :m_send_deadline(blocked_mode_client_t::m_io_service)
+ {
- // No deadline is required until the first socket operation is started. We
- // set the deadline to positive infinity so that the actor takes no action
- // until a specific deadline is set.
- m_send_deadline.expires_at(boost::posix_time::pos_infin);
+ // No deadline is required until the first socket operation is started. We
+ // set the deadline to positive infinity so that the actor takes no action
+ // until a specific deadline is set.
+ m_send_deadline.expires_at(boost::posix_time::pos_infin);
- // Start the persistent actor that checks for deadline expiry.
- check_send_deadline();
- }
- ~async_blocked_mode_client()
- {
- m_send_deadline.cancel();
- }
-
- bool shutdown()
- {
- blocked_mode_client::shutdown();
- m_send_deadline.cancel();
- return true;
- }
+ // Start the persistent actor that checks for deadline expiry.
+ check_send_deadline();
+ }
+ ~async_blocked_mode_client_t()
+ {
+ m_send_deadline.cancel();
+ }
- inline
- bool send(const void* data, size_t sz)
- {
- try
- {
- /*
- m_send_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout));
+ bool shutdown()
+ {
+ blocked_mode_client_t::shutdown();
+ m_send_deadline.cancel();
+ return true;
+ }
- // Set up the variable that receives the result of the asynchronous
- // operation. The error code is set to would_block to signal that the
- // operation is incomplete. Asio guarantees that its asynchronous
- // operations will never fail with would_block, so any other value in
- // ec indicates completion.
- boost::system::error_code ec = boost::asio::error::would_block;
+ inline
+ bool send(const void* data, size_t sz)
+ {
+ try
+ {
+ /*
+ m_send_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout));
- // Start the asynchronous operation itself. The boost::lambda function
- // object is used as a callback and will update the ec variable when the
- // operation completes. The blocking_udp_client.cpp example shows how you
- // can use boost::bind rather than boost::lambda.
- boost::asio::async_write(m_socket, boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1);
+ // Set up the variable that receives the result of the asynchronous
+ // operation. The error code is set to would_block to signal that the
+ // operation is incomplete. Asio guarantees that its asynchronous
+ // operations will never fail with would_block, so any other value in
+ // ec indicates completion.
+ boost::system::error_code ec = boost::asio::error::would_block;
- // Block until the asynchronous operation has completed.
- while(ec == boost::asio::error::would_block)
- {
- m_io_service.run_one();
- }*/
-
- boost::system::error_code ec;
+ // Start the asynchronous operation itself. The boost::lambda function
+ // object is used as a callback and will update the ec variable when the
+ // operation completes. The blocking_udp_client.cpp example shows how you
+ // can use boost::bind rather than boost::lambda.
+ boost::asio::async_write(m_socket, boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1);
- size_t writen = m_socket.write_some(boost::asio::buffer(data, sz), ec);
+ // Block until the asynchronous operation has completed.
+ while(ec == boost::asio::error::would_block)
+ {
+ m_io_service.run_one();
+ }*/
- if (!writen || ec)
- {
- LOG_PRINT_L3("Problems at write: " << ec.message());
- return false;
- }else
- {
- m_send_deadline.expires_at(boost::posix_time::pos_infin);
- }
- }
+ boost::system::error_code ec;
- catch(const boost::system::system_error& er)
- {
- LOG_ERROR("Some problems at connect, message: " << er.what());
- return false;
- }
- catch(...)
- {
- LOG_ERROR("Some fatal problems.");
- return false;
- }
+ size_t writen = blocked_mode_client_t::m_sct_back.get_socket().write_some(boost::asio::buffer(data, sz), ec);
- return true;
- }
+ if (!writen || ec)
+ {
+ LOG_PRINT_L3("Problems at write: " << ec.message());
+ return false;
+ }
+ else
+ {
+ m_send_deadline.expires_at(boost::posix_time::pos_infin);
+ }
+ }
+
+ catch (const boost::system::system_error& er)
+ {
+ LOG_ERROR("Some problems at connect, message: " << er.what());
+ return false;
+ }
+ catch (...)
+ {
+ LOG_ERROR("Some fatal problems.");
+ return false;
+ }
+
+ return true;
+ }
- private:
+ private:
- boost::asio::deadline_timer m_send_deadline;
+ boost::asio::deadline_timer m_send_deadline;
- void check_send_deadline()
- {
- // Check whether the deadline has passed. We compare the deadline against
- // the current time since a new asynchronous operation may have moved the
- // deadline before this actor had a chance to run.
- if (m_send_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now())
- {
- // The deadline has passed. The socket is closed so that any outstanding
- // asynchronous operations are cancelled. This allows the blocked
- // connect(), read_line() or write_line() functions to return.
- LOG_PRINT_L3("Timed out socket");
- m_socket.close();
+ void check_send_deadline()
+ {
+ // Check whether the deadline has passed. We compare the deadline against
+ // the current time since a new asynchronous operation may have moved the
+ // deadline before this actor had a chance to run.
+ if (m_send_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now())
+ {
+ // The deadline has passed. The socket is closed so that any outstanding
+ // asynchronous operations are cancelled. This allows the blocked
+ // connect(), read_line() or write_line() functions to return.
+ LOG_PRINT_L3("Timed out socket");
+ blocked_mode_client_t::m_sct_back.get_socket().close();
- // There is no longer an active deadline. The expiry is set to positive
- // infinity so that the actor takes no action until a new deadline is set.
- m_send_deadline.expires_at(boost::posix_time::pos_infin);
- }
+ // There is no longer an active deadline. The expiry is set to positive
+ // infinity so that the actor takes no action until a new deadline is set.
+ m_send_deadline.expires_at(boost::posix_time::pos_infin);
+ }
- // Put the actor back to sleep.
- m_send_deadline.async_wait(boost::bind(&async_blocked_mode_client::check_send_deadline, this));
- }
- };
-}
+ // Put the actor back to sleep.
+ m_send_deadline.async_wait(boost::bind(&async_blocked_mode_client_t::check_send_deadline, this));
+ }
+ };
+
+ typedef blocked_mode_client_t blocked_mode_client;
+ typedef async_blocked_mode_client_t async_blocked_mode_client;
+ }
}
diff --git a/contrib/epee/include/net/net_parse_helpers.h b/contrib/epee/include/net/net_parse_helpers.h
index 586dac98..57f10753 100644
--- a/contrib/epee/include/net/net_parse_helpers.h
+++ b/contrib/epee/include/net/net_parse_helpers.h
@@ -155,6 +155,13 @@ namespace net_utils
{
content.port = boost::lexical_cast(result[6]);
}
+ else
+ {
+ if (content.schema == "http")
+ content.port = 80;
+ else if (content.schema == "https")
+ content.port = 443;
+ }
if(result[7].matched)
{
content.uri = result[7];
diff --git a/contrib/epee/include/net/net_utils_base.h b/contrib/epee/include/net/net_utils_base.h
index 1ef06777..8b474516 100644
--- a/contrib/epee/include/net/net_utils_base.h
+++ b/contrib/epee/include/net/net_utils_base.h
@@ -79,6 +79,8 @@ namespace net_utils
m_started(time(NULL))
{}
+ connection_context_base(const connection_context_base& a) = default;
+
connection_context_base& operator=(const connection_context_base& a)
{
set_details(a.m_connection_id, a.m_remote_ip, a.m_remote_port, a.m_is_income);
diff --git a/contrib/epee/include/serialization/keyvalue_enable_POD_serialize_as_string.h b/contrib/epee/include/serialization/keyvalue_enable_POD_serialize_as_string.h
new file mode 100644
index 00000000..9ae9b7f4
--- /dev/null
+++ b/contrib/epee/include/serialization/keyvalue_enable_POD_serialize_as_string.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2006-2022, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#pragma once
+#include "keyvalue_helpers.h"
+
+
+//should be done in global namespace
+#define KV_ENABLE_POD_SERIALIZATION_AS_HEX(type_name) \
+namespace epee \
+{ \
+ namespace serialization \
+ { \
+ template \
+ bool kv_serialize(const type_name& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) \
+ { \
+ std::string s = epee::transform_t_pod_to_str(d); \
+ return kv_serialize(s, stg, hparent_section, pname); \
+ } \
+ template \
+ bool kv_unserialize(type_name& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) \
+ { \
+ std::string s; \
+ bool r = kv_unserialize(s, stg, hparent_section, pname); \
+ if (r) \
+ { \
+ d = epee::transform_str_to_t_pod(s); \
+ } \
+ return r; \
+ } \
+ } \
+}
diff --git a/contrib/epee/include/serialization/keyvalue_helpers.h b/contrib/epee/include/serialization/keyvalue_helpers.h
index 0ae911b0..79a4886b 100644
--- a/contrib/epee/include/serialization/keyvalue_helpers.h
+++ b/contrib/epee/include/serialization/keyvalue_helpers.h
@@ -62,6 +62,8 @@ namespace epee
t_pod_type transform_str_to_t_pod(const std::string& a)
{
t_pod_type res = AUTO_VAL_INIT(res);
+ if (a.empty())
+ return res;
if (!epee::string_tools::hex_to_pod(a, res))
throw std::runtime_error(std::string("Unable to transform \"") + a + "\" to pod type " + typeid(t_pod_type).name());
return res;
diff --git a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h
index d33d5ad1..c0d46af3 100644
--- a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h
+++ b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h
@@ -429,6 +429,55 @@ namespace epee
bool kv_unserialize(std::deque& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
return kv_serialization_overloads_impl_is_base_serializable_types, typename std::remove_const::type>::value>::kv_unserialize(d, stg, hparent_section, pname);
- }
+ }
+ //-------------------------------------------------------------------------------------------------------------------
+ //boost::optional
+ template
+ bool kv_serialize(const boost::optional& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
+ {
+ if(d != boost::none)
+ {
+ return kv_serialize(*d, stg, hparent_section, pname);
+ }
+ return true;
+ }
+ //-------------------------------------------------------------------------------------------------------------------
+ template
+ bool kv_unserialize(boost::optional& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
+ {
+ d = t_type();
+ bool r = kv_unserialize(*d, stg, hparent_section, pname);
+ if (!r)
+ {
+ d = boost::none;
+ }
+ return r;
+ }
+ //-------------------------------------------------------------------------------------------------------------------
+ //boost::shared_ptr
+ template
+ bool kv_serialize(const boost::shared_ptr& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
+ {
+ if (d.get())
+ {
+ return kv_serialize(*d, stg, hparent_section, pname);
+ }
+ return true;
+ }
+ //-------------------------------------------------------------------------------------------------------------------
+ template
+ bool kv_unserialize(boost::shared_ptr& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
+ {
+ d.reset();
+ t_type* ptr = new t_type();
+ bool r = kv_unserialize(*ptr, stg, hparent_section, pname);
+ if (!r)
+ {
+ d.reset(ptr);
+ }
+ return r;
+ }
+
+
}
}
\ No newline at end of file
diff --git a/contrib/epee/include/storages/http_abstract_invoke.h b/contrib/epee/include/storages/http_abstract_invoke.h
index 0b6ef8e3..aa0af3d8 100644
--- a/contrib/epee/include/storages/http_abstract_invoke.h
+++ b/contrib/epee/include/storages/http_abstract_invoke.h
@@ -29,11 +29,25 @@
#include "portable_storage_template_helper.h"
#include "net/http_base.h"
#include "net/http_server_handlers_map2.h"
+#include "net/http_client.h"
namespace epee
{
namespace net_utils
{
+
+ template
+ bool get_http_json_t(const std::string& url, t_response& result_struct, unsigned int timeout = 5000, const std::string& method = "GET")
+ {
+ std::string body;
+ if (!http::fetch_url(url, body, method, "", timeout))
+ {
+ return false;
+ }
+ return serialization::load_t_from_json(result_struct, body);
+ }
+
+
template
bool invoke_http_json_remote_command2(const std::string& url, t_request& out_struct, t_response& result_struct, t_transport& transport, unsigned int timeout = 5000, const std::string& method = "GET")
{
@@ -72,6 +86,8 @@ namespace epee
if(!serialization::store_t_to_binary(out_struct, req_param))
return false;
+ LOG_PRINT_L3("[HTTP_BIN] ---> " << "[" << &req_param << "][" << method << "][" << url << "] REQUEST BODY BASE64: " << ENDL << epee::string_encoding::base64_encode(req_param));
+
const http::http_response_info* pri = NULL;
if(!invoke_request(url, transport, timeout, &pri, method, req_param))
{
@@ -85,6 +101,8 @@ namespace epee
return false;
}
+ LOG_PRINT_L3("[HTTP_BIN] <--- " << "[" << &req_param << "][" << method << "][" << url << "] RESPONSE(" << pri->m_response_code << ") BODY BASE64: " << ENDL << epee::string_encoding::base64_encode(pri->m_body));
+
if(pri->m_response_code != 200)
{
LOG_PRINT_L1("Failed to invoke http request to " << url << ", wrong response code: " << pri->m_response_code);
diff --git a/contrib/epee/include/storages/portable_storage.h b/contrib/epee/include/storages/portable_storage.h
index 4d36cc03..a5d46e19 100644
--- a/contrib/epee/include/storages/portable_storage.h
+++ b/contrib/epee/include/storages/portable_storage.h
@@ -77,8 +77,7 @@ namespace epee
bool insert_next_section(harray hSecArray, hsection& hinserted_childsection);
//------------------------------------------------------------------------
//delete entry (section, value or array)
- bool delete_entry(const std::string& pentry_name, hsection hparent_section = nullptr);
-
+ bool delete_entry(const std::string& pentry_name, hsection hparent_section = nullptr);
//-------------------------------------------------------------------------------
bool store_to_binary(binarybuffer& target);
bool load_from_binary(const binarybuffer& target);
@@ -87,6 +86,8 @@ namespace epee
bool dump_as_json(std::string& targetObj, size_t indent = 0, end_of_line_t eol = eol_crlf);
bool load_from_json(const std::string& source);
+ template
+ bool enum_entries(hsection hparent_section, cb_t cb);
private:
section m_root;
hsection get_root_section() {return &m_root;}
@@ -384,6 +385,20 @@ namespace epee
CATCH_ENTRY("portable_storage::insert_first_value", nullptr);
}
//---------------------------------------------------------------------------------------------------------------
+ template
+ bool portable_storage::enum_entries(hsection hparent_section, cb_t cb)
+ {
+ TRY_ENTRY();
+ if (!hparent_section) hparent_section = &m_root;
+ for (const auto& e : hparent_section->m_entries)
+ {
+ if (!cb(e.first, e.second))
+ break;
+ }
+ return true;
+ CATCH_ENTRY("portable_storage::enum_entries", false);
+ }
+
template
bool portable_storage::insert_next_value(harray hval_array, const t_value& target)
{
diff --git a/contrib/epee/include/storages/portable_storage_from_json.h b/contrib/epee/include/storages/portable_storage_from_json.h
index 4e74fb7a..fa76cc9b 100644
--- a/contrib/epee/include/storages/portable_storage_from_json.h
+++ b/contrib/epee/include/storages/portable_storage_from_json.h
@@ -30,7 +30,6 @@
namespace epee
{
- using namespace misc_utils::parse;
namespace serialization
{
namespace json
@@ -86,7 +85,7 @@ namespace epee
switch(*it)
{
case '"':
- match_string2(it, buf_end, name);
+ misc_utils::parse::match_string2(it, buf_end, name);
state = match_state_waiting_separator;
break;
case '}':
@@ -107,7 +106,7 @@ namespace epee
if(*it == '"')
{//just a named string value started
std::string val;
- match_string2(it, buf_end, val);
+ misc_utils::parse::match_string2(it, buf_end, val);
//insert text value
stg.set_value(name, val, current_section);
state = match_state_wonder_after_value;
@@ -115,7 +114,7 @@ namespace epee
{//just a named number value started
std::string val;
bool is_v_float = false;bool is_signed = false;
- match_number2(it, buf_end, val, is_v_float, is_signed);
+ misc_utils::parse::match_number2(it, buf_end, val, is_v_float, is_signed);
if(!is_v_float)
{
if(is_signed)
@@ -136,7 +135,7 @@ namespace epee
}else if(isalpha(*it) )
{// could be null, true or false
std::string word;
- match_word2(it, buf_end, word);
+ misc_utils::parse::match_word2(it, buf_end, word);
if(boost::iequals(word, "null"))
{
state = match_state_wonder_after_value;
@@ -191,7 +190,7 @@ namespace epee
{
//mean array of strings
std::string val;
- match_string2(it, buf_end, val);
+ misc_utils::parse::match_string2(it, buf_end, val);
h_array = stg.insert_first_value(name, val, current_section);
CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values entry");
state = match_state_array_after_value;
@@ -200,7 +199,7 @@ namespace epee
{//array of numbers value started
std::string val;
bool is_v_float = false;bool is_signed_val = false;
- match_number2(it, buf_end, val, is_v_float, is_signed_val);
+ misc_utils::parse::match_number2(it, buf_end, val, is_v_float, is_signed_val);
if(!is_v_float)
{
int64_t nval = boost::lexical_cast(val);//bool res = string_tools::string_to_num_fast(val, nval);
@@ -222,7 +221,7 @@ namespace epee
}else if(isalpha(*it) )
{// array of booleans
std::string word;
- match_word2(it, buf_end, word);
+ misc_utils::parse::match_word2(it, buf_end, word);
if(boost::iequals(word, "true"))
{
h_array = stg.insert_first_value(name, true, current_section);
@@ -266,7 +265,7 @@ namespace epee
if(*it == '"')
{
std::string val;
- match_string2(it, buf_end, val);
+ misc_utils::parse::match_string2(it, buf_end, val);
bool res = stg.insert_next_value(h_array, val);
CHECK_AND_ASSERT_THROW_MES(res, "failed to insert values");
state = match_state_array_after_value;
@@ -277,7 +276,7 @@ namespace epee
{//array of numbers value started
std::string val;
bool is_v_float = false;bool is_signed_val = false;
- match_number2(it, buf_end, val, is_v_float, is_signed_val);
+ misc_utils::parse::match_number2(it, buf_end, val, is_v_float, is_signed_val);
bool insert_res = false;
if(!is_v_float)
{
@@ -299,7 +298,7 @@ namespace epee
if(isalpha(*it) )
{// array of booleans
std::string word;
- match_word2(it, buf_end, word);
+ misc_utils::parse::match_word2(it, buf_end, word);
if(boost::iequals(word, "true"))
{
bool r = stg.insert_next_value(h_array, true);
diff --git a/contrib/epee/include/storages/portable_storage_val_converters.h b/contrib/epee/include/storages/portable_storage_val_converters.h
index caf7a322..8e38beeb 100644
--- a/contrib/epee/include/storages/portable_storage_val_converters.h
+++ b/contrib/epee/include/storages/portable_storage_val_converters.h
@@ -129,7 +129,20 @@ POP_VS_WARNINGS
template
struct convert_to_integral
{
- static void convert(const from_type& from, to_type& to)
+ static void convert(const std::string& from, uint64_t& to)
+ {
+ //attempt to convert string to unsigned int
+ try
+ {
+ to = std::stoull(from);
+ }catch(...)
+ {
+ ASSERT_AND_THROW_WRONG_CONVERSION();
+ }
+ }
+
+ template
+ static void convert(const from_type_t& from, to_type_t& to)
{
ASSERT_AND_THROW_WRONG_CONVERSION();
}
diff --git a/contrib/epee/include/string_coding.h b/contrib/epee/include/string_coding.h
index 0b125bbd..a034dec9 100644
--- a/contrib/epee/include/string_coding.h
+++ b/contrib/epee/include/string_coding.h
@@ -37,6 +37,7 @@
#endif
#include
#endif
+#include
#include "warnings.h"
@@ -310,7 +311,14 @@ namespace string_encoding
return get_md5_as_hexstring(src.data(), src.size());
}
#endif
-
+ inline
+ std::string toupper(std::string s)
+ {
+ std::transform(s.begin(), s.end(), s.begin(),
+ [](unsigned char c) { return std::toupper(c); } // correct
+ );
+ return s;
+ }
}
}
diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h
index 95a5d88e..24dbffee 100644
--- a/contrib/epee/include/string_tools.h
+++ b/contrib/epee/include/string_tools.h
@@ -23,16 +23,12 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
-
-
-
#ifndef _STRING_TOOLS_H_
#define _STRING_TOOLS_H_
-//#include
#include
#include
-//#include
+#include