moved all stuff to github
This commit is contained in:
parent
095fbeeb67
commit
296ae46ed8
|
@ -0,0 +1,2 @@
|
||||||
|
.git* export-ignore
|
||||||
|
/CMakeLists.txt export-subst
|
|
@ -0,0 +1,2 @@
|
||||||
|
/build
|
||||||
|
/tags
|
|
@ -0,0 +1,115 @@
|
||||||
|
cmake_minimum_required(VERSION 2.8.6)
|
||||||
|
|
||||||
|
set(VERSION "0.1")
|
||||||
|
# $Format:Packaged from commit %H%nset(COMMIT %h)%nset(REFS "%d")$
|
||||||
|
|
||||||
|
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||||
|
set(CMAKE_CONFIGURATION_TYPES "Debug;Release")
|
||||||
|
enable_testing()
|
||||||
|
|
||||||
|
include_directories(src contrib/epee/include "${CMAKE_BINARY_DIR}/version")
|
||||||
|
|
||||||
|
set(STATIC ${MSVC} CACHE BOOL "Link libraries statically")
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
add_definitions("/bigobj /MP /W3 /GS- /D_CRT_SECURE_NO_WARNINGS /wd4996 /wd4345 /D_WIN32_WINNT=0x0600 /DWIN32_LEAN_AND_MEAN /DGTEST_HAS_TR1_TUPLE=0 /FIinline_c.h /D__SSE4_1__")
|
||||||
|
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Dinline=__inline")
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:10485760")
|
||||||
|
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}}")
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
include_directories(SYSTEM src/platform/msc)
|
||||||
|
else()
|
||||||
|
set(ARCH native CACHE STRING "CPU to build for: -march value or default")
|
||||||
|
if("${ARCH}" STREQUAL "default")
|
||||||
|
set(ARCH_FLAG "")
|
||||||
|
else()
|
||||||
|
set(ARCH_FLAG "-march=${ARCH}")
|
||||||
|
endif()
|
||||||
|
set(WARNINGS "-Wall -Wextra -Wpointer-arith -Wundef -Wvla -Wwrite-strings -Werror -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-error=undef -Wno-error=uninitialized")
|
||||||
|
if(CMAKE_C_COMPILER_ID STREQUAL "Clang")
|
||||||
|
set(WARNINGS "${WARNINGS} -Wno-error=mismatched-tags -Wno-error=null-conversion -Wno-overloaded-shift-op-parentheses -Wno-error=shift-count-overflow -Wno-error=tautological-constant-out-of-range-compare -Wno-error=unused-private-field -Wno-error=unneeded-internal-declaration")
|
||||||
|
else()
|
||||||
|
set(WARNINGS "${WARNINGS} -Wlogical-op -Wno-error=maybe-uninitialized")
|
||||||
|
endif()
|
||||||
|
if(MINGW)
|
||||||
|
set(WARNINGS "${WARNINGS} -Wno-error=unused-value")
|
||||||
|
set(MINGW_FLAG "-DWIN32_LEAN_AND_MEAN")
|
||||||
|
include_directories(SYSTEM src/platform/mingw)
|
||||||
|
else()
|
||||||
|
set(MINGW_FLAG "")
|
||||||
|
endif()
|
||||||
|
set(C_WARNINGS "-Waggregate-return -Wnested-externs -Wold-style-definition -Wstrict-prototypes")
|
||||||
|
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")
|
||||||
|
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} -std=c++11 -D_GNU_SOURCE ${MINGW_FLAG} ${WARNINGS} ${CXX_WARNINGS} ${ARCH_FLAG}")
|
||||||
|
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND NOT (CMAKE_C_COMPILER_VERSION VERSION_LESS 4.8))
|
||||||
|
set(DEBUG_FLAGS "-g3 -Og")
|
||||||
|
else()
|
||||||
|
set(DEBUG_FLAGS "-g3 -O0")
|
||||||
|
endif()
|
||||||
|
set(RELEASE_FLAGS "-Ofast -DNDEBUG -Wno-unused-variable -flto")
|
||||||
|
#if(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND NOT MINGW)
|
||||||
|
# set(RELEASE_FLAGS "${RELEASE_FLAGS} -fno-fat-lto-objects")
|
||||||
|
#endif()
|
||||||
|
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${DEBUG_FLAGS}")
|
||||||
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${DEBUG_FLAGS}")
|
||||||
|
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${RELEASE_FLAGS}")
|
||||||
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${RELEASE_FLAGS}")
|
||||||
|
if(STATIC)
|
||||||
|
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
|
||||||
|
function(add_executable name)
|
||||||
|
_add_executable("${name}" ${ARGN})
|
||||||
|
set_target_properties("${name}" PROPERTIES LINK_SEARCH_START_STATIC ON LINK_SEARCH_END_STATIC ON)
|
||||||
|
endfunction()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(STATIC)
|
||||||
|
set(Boost_USE_STATIC_LIBS ON)
|
||||||
|
set(Boost_USE_STATIC_RUNTIME ON)
|
||||||
|
endif()
|
||||||
|
find_package(Boost 1.53 REQUIRED COMPONENTS system filesystem thread date_time chrono regex serialization atomic program_options)
|
||||||
|
if((${Boost_MAJOR_VERSION} EQUAL 1) AND (${Boost_MINOR_VERSION} EQUAL 54))
|
||||||
|
message(SEND_ERROR "Boost version 1.54 is unsupported, more details are available here http://goo.gl/RrCFmA")
|
||||||
|
endif()
|
||||||
|
include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
|
||||||
|
if(MINGW)
|
||||||
|
set(Boost_LIBRARIES "${Boost_LIBRARIES};ws2_32;mswsock")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(COMMIT_ID_IN_VERSION ON CACHE BOOL "Include commit ID in version")
|
||||||
|
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/version")
|
||||||
|
if (NOT COMMIT_ID_IN_VERSION)
|
||||||
|
set(VERSION "${VERSION}-unknown")
|
||||||
|
configure_file("src/version.h.in" "version/version.h")
|
||||||
|
add_custom_target(version ALL)
|
||||||
|
elseif(DEFINED COMMIT)
|
||||||
|
string(REPLACE "." "\\." VERSION_RE "${VERSION}")
|
||||||
|
if(NOT REFS MATCHES "(\\(|, )tag: v${VERSION_RE}(\\)|, )")
|
||||||
|
set(VERSION "${VERSION}-g${COMMIT}")
|
||||||
|
endif()
|
||||||
|
configure_file("src/version.h.in" "version/version.h")
|
||||||
|
add_custom_target(version ALL)
|
||||||
|
else()
|
||||||
|
find_package(Git QUIET)
|
||||||
|
if(Git_FOUND)
|
||||||
|
add_custom_target(version ALL "${CMAKE_COMMAND}" "-D" "VERSION=${VERSION}" "-D" "GIT=${GIT_EXECUTABLE}" "-D" "TO=${CMAKE_BINARY_DIR}/version/version.h" "-P" "src/version.cmake" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
|
||||||
|
else()
|
||||||
|
set(VERSION "${VERSION}-unknown")
|
||||||
|
configure_file("src/version.h.in" "version/version.h")
|
||||||
|
add_custom_target(version ALL)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_subdirectory(src)
|
||||||
|
add_subdirectory(tests)
|
20
LICENSE
20
LICENSE
|
@ -1,20 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2013 Antonio Juarez
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
|
||||||
the Software without restriction, including without limitation the rights to
|
|
||||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
||||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
all: all-release
|
||||||
|
|
||||||
|
cmake-debug:
|
||||||
|
mkdir -p build/debug
|
||||||
|
cd build/debug && cmake -D CMAKE_BUILD_TYPE=Debug ../..
|
||||||
|
|
||||||
|
build-debug: cmake-debug
|
||||||
|
cd build/debug && $(MAKE)
|
||||||
|
|
||||||
|
test-debug: build-debug
|
||||||
|
cd build/debug && $(MAKE) test
|
||||||
|
|
||||||
|
all-debug: build-debug
|
||||||
|
|
||||||
|
cmake-release:
|
||||||
|
mkdir -p build/release
|
||||||
|
cd build/release && cmake -D CMAKE_BUILD_TYPE=Release ../..
|
||||||
|
|
||||||
|
build-release: cmake-release
|
||||||
|
cd build/release && $(MAKE)
|
||||||
|
|
||||||
|
test-release: build-release
|
||||||
|
cd build/release && $(MAKE) test
|
||||||
|
|
||||||
|
all-release: build-release
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf build
|
||||||
|
|
||||||
|
tags:
|
||||||
|
ctags -R --sort=1 --c++-kinds=+p --fields=+iaS --extra=+q --language-force=C++ src contrib tests/gtest
|
||||||
|
|
||||||
|
.PHONY: all cmake-debug build-debug test-debug all-debug cmake-release build-release test-release all-release clean tags
|
|
@ -0,0 +1,32 @@
|
||||||
|
-= Building Cybernote =-
|
||||||
|
|
||||||
|
On *nix:
|
||||||
|
|
||||||
|
Dependencies: GCC 4.7 or later, CMake 2.8.6 or later, and Boost 1.53 or later (except 1.54, more details here: http://goo.gl/RrCFmA).
|
||||||
|
You may download them from:
|
||||||
|
http://gcc.gnu.org/
|
||||||
|
http://www.cmake.org/
|
||||||
|
http://www.boost.org/
|
||||||
|
Alternatively, it may be possible to install them using a package manager.
|
||||||
|
|
||||||
|
To build, change to a directory where this file is located, and run `make'. The resulting executables can be found in build/release/src.
|
||||||
|
|
||||||
|
Advanced options:
|
||||||
|
Parallel build: run `make -j<number of threads>' instead of `make'.
|
||||||
|
Debug build: run `make build-debug'.
|
||||||
|
Test suite: run `make test-release' to run tests in addition to building. Running `make test-debug' will do the same to the debug version.
|
||||||
|
Building with Clang: it may be possible to use Clang instead of GCC, but this may not work everywhere. To build, run `export CC=clang CXX=clang++' before running `make'.
|
||||||
|
|
||||||
|
On Windows:
|
||||||
|
Dependencies: MSVC 2012 or later, CMake 2.8.6 or later, and Boost 1.53 or later. You may download them from:
|
||||||
|
http://www.microsoft.com/
|
||||||
|
http://www.cmake.org/
|
||||||
|
http://www.boost.org/
|
||||||
|
|
||||||
|
To build, change to a directory where this file is located, and run this commands:
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -G "Visual Studio 11 Win64" ..
|
||||||
|
|
||||||
|
And then do Build.
|
||||||
|
Good luck!
|
|
@ -0,0 +1,25 @@
|
||||||
|
Copyright (c) 2006-2013, 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 Andrey N. Sabelnikov 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.
|
|
@ -0,0 +1 @@
|
||||||
|
epee - is a small library of helpers, wrappers, tools and and so on, used to make my life easier.
|
|
@ -0,0 +1 @@
|
||||||
|
/build/*
|
|
@ -0,0 +1,49 @@
|
||||||
|
cmake_minimum_required(VERSION 2.8)
|
||||||
|
set(Boost_USE_MULTITHREADED ON)
|
||||||
|
#set(Boost_DEBUG 1)
|
||||||
|
find_package(Boost COMPONENTS system filesystem thread date_time chrono regex )
|
||||||
|
|
||||||
|
include_directories( ${Boost_INCLUDE_DIRS} )
|
||||||
|
|
||||||
|
|
||||||
|
IF (MSVC)
|
||||||
|
add_definitions( "/W3 /D_CRT_SECURE_NO_WARNINGS /wd4996 /wd4345 /nologo /D_WIN32_WINNT=0x0600 /DWIN32_LEAN_AND_MEAN /bigobj" )
|
||||||
|
ELSE()
|
||||||
|
# set stuff for other systems
|
||||||
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wno-reorder -D_GNU_SOURCE")
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
|
||||||
|
include_directories(.)
|
||||||
|
include_directories(../include)
|
||||||
|
include_directories(iface)
|
||||||
|
|
||||||
|
|
||||||
|
# Add folders to filters
|
||||||
|
file(GLOB_RECURSE LEVIN_GENERAL_SECTION RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/demo_levin_server/*.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/demo_levin_server/*.inl
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/demo_levin_server/*.cpp)
|
||||||
|
|
||||||
|
file(GLOB_RECURSE HTTP_GENERAL_SECTION RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/demo_http_server/*.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/demo_http_server/*.inl
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/demo_http_server/*.cpp)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
source_group(general FILES ${LEVIN_GENERAL_SECTION} FILES ${HTTP_GENERAL_SECTION})
|
||||||
|
#source_group(general FILES ${HTTP_GENERAL_SECTION})
|
||||||
|
|
||||||
|
add_executable(demo_http_server ${HTTP_GENERAL_SECTION} )
|
||||||
|
add_executable(demo_levin_server ${LEVIN_GENERAL_SECTION} )
|
||||||
|
|
||||||
|
target_link_libraries( demo_http_server ${Boost_LIBRARIES} )
|
||||||
|
target_link_libraries( demo_levin_server ${Boost_LIBRARIES} )
|
||||||
|
|
||||||
|
IF (NOT WIN32)
|
||||||
|
target_link_libraries (demo_http_server rt)
|
||||||
|
target_link_libraries (demo_levin_server rt)
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,217 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "console_handler.h"
|
||||||
|
#include "demo_http_server.h"
|
||||||
|
#include "net/http_client.h"
|
||||||
|
#include "storages/http_abstract_invoke.h"
|
||||||
|
|
||||||
|
|
||||||
|
template<class t_request, class t_response>
|
||||||
|
bool communicate(const std::string url, t_request& req, t_response& rsp, const std::string& ip, const std::string& port, bool use_json, bool use_jrpc = false)
|
||||||
|
{
|
||||||
|
epee::net_utils::http::http_simple_client http_client;
|
||||||
|
bool r = http_client.connect(ip, port, 1000);
|
||||||
|
CHECK_AND_ASSERT_MES(r, false, "failed to connect");
|
||||||
|
if(use_json)
|
||||||
|
{
|
||||||
|
if(use_jrpc)
|
||||||
|
{
|
||||||
|
epee::json_rpc::request<t_request> req_t = AUTO_VAL_INIT(req_t);
|
||||||
|
req_t.params = req;
|
||||||
|
req_t.id = "10";
|
||||||
|
req_t.method = "command_example_1";
|
||||||
|
req_t.version = "1.1";
|
||||||
|
epee::json_rpc::response<t_response, std::string> resp_t = AUTO_VAL_INIT(resp_t);
|
||||||
|
if(!epee::net_utils::invoke_http_json_remote_command2("/request_json_rpc", req_t, resp_t, http_client))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
rsp = resp_t.result;
|
||||||
|
return true;
|
||||||
|
}else
|
||||||
|
return epee::net_utils::invoke_http_json_remote_command2(url, req, rsp, http_client);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return epee::net_utils::invoke_http_bin_remote_command2(url, req, rsp, http_client);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
string_tools::set_module_name_and_folder(argv[0]);
|
||||||
|
|
||||||
|
//set up logging options
|
||||||
|
log_space::get_set_log_detalisation_level(true, LOG_LEVEL_2);
|
||||||
|
log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL);
|
||||||
|
log_space::log_singletone::add_logger(LOGGER_FILE,
|
||||||
|
log_space::log_singletone::get_default_log_file().c_str(),
|
||||||
|
log_space::log_singletone::get_default_log_folder().c_str());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LOG_PRINT("Demo server starting ...", LOG_LEVEL_0);
|
||||||
|
|
||||||
|
|
||||||
|
demo::demo_http_server srv;
|
||||||
|
|
||||||
|
start_default_console(&srv);
|
||||||
|
|
||||||
|
std::string bind_param = "0.0.0.0";
|
||||||
|
std::string port = "83";
|
||||||
|
|
||||||
|
if(!srv.init(port, bind_param))
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to initialize srv!");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//log loop
|
||||||
|
srv.run();
|
||||||
|
size_t count = 0;
|
||||||
|
while (!srv.is_stop())
|
||||||
|
{
|
||||||
|
|
||||||
|
demo::COMMAND_EXAMPLE_1::request req;
|
||||||
|
req.sub = demo::get_test_data();
|
||||||
|
demo::COMMAND_EXAMPLE_1::response rsp;
|
||||||
|
bool r = false;
|
||||||
|
if(count%2)
|
||||||
|
{//invoke json
|
||||||
|
r = communicate("/request_api_json_1", req, rsp, "127.0.0.1", port, true, true);
|
||||||
|
}else{
|
||||||
|
r = communicate("/request_api_bin_1", req, rsp, "127.0.0.1", port, false);
|
||||||
|
}
|
||||||
|
CHECK_AND_ASSERT_MES(r, false, "failed to invoke http request");
|
||||||
|
CHECK_AND_ASSERT_MES(rsp.m_success, false, "wrong response");
|
||||||
|
CHECK_AND_ASSERT_MES(rsp.subs.size()==1, false, "wrong response");
|
||||||
|
CHECK_AND_ASSERT_MES(rsp.subs.front() == demo::get_test_data(), false, "wrong response");
|
||||||
|
//misc_utils::sleep_no_w(1000);
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
bool r = srv.wait_stop();
|
||||||
|
CHECK_AND_ASSERT_MES(r, 1, "failed to wait server stop");
|
||||||
|
srv.deinit();
|
||||||
|
|
||||||
|
LOG_PRINT("Demo server stoped.", LOG_LEVEL_0);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
CATCH_ENTRY_L0("main", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
namespace demo
|
||||||
|
{
|
||||||
|
bool demo_http_server::init(const std::string& bind_port, const std::string& bind_ip)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
//set self as callback handler
|
||||||
|
m_net_server.get_config_object().m_phandler = this;
|
||||||
|
|
||||||
|
//here set folder for hosting reqests
|
||||||
|
m_net_server.get_config_object().m_folder = "";
|
||||||
|
|
||||||
|
LOG_PRINT_L0("Binding on " << bind_ip << ":" << bind_port);
|
||||||
|
return m_net_server.init_server(bind_port, bind_ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool demo_http_server::run()
|
||||||
|
{
|
||||||
|
m_stop = false;
|
||||||
|
//here you can set worker threads count
|
||||||
|
int thrds_count = 4;
|
||||||
|
|
||||||
|
//go to loop
|
||||||
|
LOG_PRINT("Run net_service loop( " << thrds_count << " threads)...", LOG_LEVEL_0);
|
||||||
|
if(!m_net_server.run_server(thrds_count, false))
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to run net tcp server!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool demo_http_server::deinit()
|
||||||
|
{
|
||||||
|
return m_net_server.deinit_server();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool demo_http_server::send_stop_signal()
|
||||||
|
{
|
||||||
|
m_stop = true;
|
||||||
|
m_net_server.send_stop_signal();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool demo_http_server::on_requestr_uri_1(const net_utils::http::http_request_info& query_info,
|
||||||
|
net_utils::http::http_response_info& response,
|
||||||
|
const net_utils::connection_context_base& m_conn_context)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool demo_http_server::on_requestr_uri_2(const net_utils::http::http_request_info& query_info,
|
||||||
|
net_utils::http::http_response_info& response,
|
||||||
|
const net_utils::connection_context_base& m_conn_context)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool demo_http_server::on_hosting_request( const net_utils::http::http_request_info& query_info,
|
||||||
|
net_utils::http::http_response_info& response,
|
||||||
|
const net_utils::connection_context_base& m_conn_context)
|
||||||
|
{
|
||||||
|
//read file from filesystem here
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool demo_http_server::on_request_api_1(const COMMAND_EXAMPLE_1::request& req, COMMAND_EXAMPLE_1::response& res)
|
||||||
|
{
|
||||||
|
CHECK_AND_ASSERT_MES(req.sub == demo::get_test_data(), false, "wrong request");
|
||||||
|
res.m_success = true;
|
||||||
|
res.subs.push_back(req.sub);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool demo_http_server::on_request_api_1_with_error(const COMMAND_EXAMPLE_1::request& req, COMMAND_EXAMPLE_1::response& res, epee::json_rpc::error& error_resp)
|
||||||
|
{
|
||||||
|
error_resp.code = 232432;
|
||||||
|
error_resp.message = "bla bla bla";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool demo_http_server::on_request_api_2(const COMMAND_EXAMPLE_2::request& req, COMMAND_EXAMPLE_2::response& res)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 <boost/thread.hpp>
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
|
||||||
|
#include "net/http_server_cp2.h"
|
||||||
|
#include "transport_defs.h"
|
||||||
|
#include "net/http_server_handlers_map2.h"
|
||||||
|
|
||||||
|
using namespace epee;
|
||||||
|
|
||||||
|
namespace demo
|
||||||
|
{
|
||||||
|
|
||||||
|
class demo_http_server: public net_utils::http::i_http_server_handler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
demo_http_server():m_stop(false){}
|
||||||
|
bool run();
|
||||||
|
bool init(const std::string& bind_port = "11112", const std::string& bind_ip = "0.0.0.0");
|
||||||
|
bool deinit();
|
||||||
|
bool send_stop_signal();
|
||||||
|
bool is_stop(){return m_stop;}
|
||||||
|
bool wait_stop(){return m_net_server.timed_wait_server_stop(100000);}
|
||||||
|
private:
|
||||||
|
|
||||||
|
|
||||||
|
CHAIN_HTTP_TO_MAP2(); //forward http requests to uri map
|
||||||
|
|
||||||
|
BEGIN_URI_MAP2()
|
||||||
|
MAP_URI2("/requestr_uri_1", on_requestr_uri_1)
|
||||||
|
MAP_URI2("/requestr_uri_2", on_requestr_uri_1)
|
||||||
|
//MAP_URI_AUTO_XML2("/request_api_xml_1", on_request_api_1, COMMAND_EXAMPLE_1)
|
||||||
|
//MAP_URI_AUTO_XML2("/request_api_xml_2", on_request_api_2, COMMAND_EXAMPLE_2)
|
||||||
|
MAP_URI_AUTO_JON2("/request_api_json_1", on_request_api_1, COMMAND_EXAMPLE_1)
|
||||||
|
MAP_URI_AUTO_JON2("/request_api_json_2", on_request_api_2, COMMAND_EXAMPLE_2)
|
||||||
|
MAP_URI_AUTO_BIN2("/request_api_bin_1", on_request_api_1, COMMAND_EXAMPLE_1)
|
||||||
|
MAP_URI_AUTO_BIN2("/request_api_bin_2", on_request_api_2, COMMAND_EXAMPLE_2)
|
||||||
|
BEGIN_JSON_RPC_MAP("/request_json_rpc")
|
||||||
|
MAP_JON_RPC("command_example_1", on_request_api_1, COMMAND_EXAMPLE_1)
|
||||||
|
MAP_JON_RPC("command_example_2", on_request_api_2, COMMAND_EXAMPLE_2)
|
||||||
|
MAP_JON_RPC_WE("command_example_1_we", on_request_api_1_with_error, COMMAND_EXAMPLE_1)
|
||||||
|
END_JSON_RPC_MAP()
|
||||||
|
CHAIN_URI_MAP2(on_hosting_request)
|
||||||
|
END_URI_MAP2()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool on_requestr_uri_1(const net_utils::http::http_request_info& query_info,
|
||||||
|
net_utils::http::http_response_info& response,
|
||||||
|
const net_utils::connection_context_base& m_conn_context);
|
||||||
|
|
||||||
|
|
||||||
|
bool on_requestr_uri_2(const net_utils::http::http_request_info& query_info,
|
||||||
|
net_utils::http::http_response_info& response,
|
||||||
|
const net_utils::connection_context_base& m_conn_context);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool on_hosting_request( const net_utils::http::http_request_info& query_info,
|
||||||
|
net_utils::http::http_response_info& response,
|
||||||
|
const net_utils::connection_context_base& m_conn_context);
|
||||||
|
|
||||||
|
bool on_request_api_1(const COMMAND_EXAMPLE_1::request& req, COMMAND_EXAMPLE_1::response& res);
|
||||||
|
bool on_request_api_2(const COMMAND_EXAMPLE_2::request& req, COMMAND_EXAMPLE_2::response& res);
|
||||||
|
|
||||||
|
bool on_request_api_1_with_error(const COMMAND_EXAMPLE_1::request& req, COMMAND_EXAMPLE_1::response& res, epee::json_rpc::error& error_resp);
|
||||||
|
|
||||||
|
net_utils::boosted_http_server_custum_handling m_net_server;
|
||||||
|
std::atomic<bool> m_stop;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
// stdafx.cpp : source file that includes just the standard includes
|
||||||
|
// demo_http_server.pch will be the pre-compiled header
|
||||||
|
// stdafx.obj will contain the pre-compiled type information
|
||||||
|
|
||||||
|
#include "stdafx.h"
|
||||||
|
|
||||||
|
// TODO: reference any additional headers you need in STDAFX.H
|
||||||
|
// and not in this file
|
|
@ -0,0 +1,40 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 "targetver.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define BOOST_FILESYSTEM_VERSION 3
|
||||||
|
#define ENABLE_RELEASE_LOGGING
|
||||||
|
#include "misc_log_ex.h"
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// The following macros define the minimum required platform. The minimum required platform
|
||||||
|
// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run
|
||||||
|
// your application. The macros work by enabling all features available on platform versions up to and
|
||||||
|
// including the version specified.
|
||||||
|
|
||||||
|
// Modify the following defines if you have to target a platform prior to the ones specified below.
|
||||||
|
// Refer to MSDN for the latest info on corresponding values for different platforms.
|
||||||
|
#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista.
|
||||||
|
#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows.
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,200 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "demo_levin_server.h"
|
||||||
|
#include "console_handler.h"
|
||||||
|
|
||||||
|
|
||||||
|
template<class t_request>
|
||||||
|
bool communicate(net_utils::boosted_levin_async_server& transport, int id, t_request& req, const std::string& ip, const std::string& port, bool use_async)
|
||||||
|
{
|
||||||
|
if(use_async)
|
||||||
|
{
|
||||||
|
//IMPORTANT: do not pass local parameters from stack by reference! connect_async returns immediately, and callback will call in any thread later
|
||||||
|
transport.connect_async(ip, port, 10000, [&transport, id, req, ip, port](net_utils::connection_context_base& ctx, const boost::system::error_code& ec_)
|
||||||
|
{
|
||||||
|
if(!!ec_)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to connect to " << ip << ":" << port);
|
||||||
|
}else
|
||||||
|
{//connected ok!
|
||||||
|
|
||||||
|
epee::net_utils::async_invoke_remote_command2<demo::COMMAND_EXAMPLE_1::response>(ctx.m_connection_id, id, req, transport.get_config_object(), [&transport, ip, port](int res_code, demo::COMMAND_EXAMPLE_1::response& rsp, net_utils::connection_context_base& ctx)
|
||||||
|
{
|
||||||
|
if(res_code < 0)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to invoke to " << ip << ":" << port);
|
||||||
|
}else
|
||||||
|
{//invoked ok
|
||||||
|
CHECK_AND_ASSERT_MES(rsp.m_success, false, "wrong response");
|
||||||
|
CHECK_AND_ASSERT_MES(rsp.subs.size()==1, false, "wrong response");
|
||||||
|
CHECK_AND_ASSERT_MES(rsp.subs.front() == demo::get_test_data(), false, "wrong response");
|
||||||
|
LOG_PRINT_GREEN("Client COMMAND_EXAMPLE_1 async invoked ok", LOG_LEVEL_0);
|
||||||
|
}
|
||||||
|
transport.get_config_object().close(ctx.m_connection_id);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
LOG_PRINT_GREEN("Client COMMAND_EXAMPLE_1 async invoke requested", LOG_LEVEL_0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
net_utils::connection_context_base ctx = AUTO_VAL_INIT(ctx);
|
||||||
|
bool r = transport.connect(ip, port, 10000, ctx);
|
||||||
|
CHECK_AND_ASSERT_MES(r, false, "failed to connect to " << ip << ":" << port);
|
||||||
|
demo::COMMAND_EXAMPLE_1::response rsp = AUTO_VAL_INIT(rsp);
|
||||||
|
LOG_PRINT_GREEN("Client COMMAND_EXAMPLE_1 sync invoke requested", LOG_LEVEL_0);
|
||||||
|
r = epee::net_utils::invoke_remote_command2(ctx.m_connection_id, id, req, rsp, transport.get_config_object());
|
||||||
|
CHECK_AND_ASSERT_MES(r, false, "failed to invoke levin request");
|
||||||
|
CHECK_AND_ASSERT_MES(rsp.m_success, false, "wrong response");
|
||||||
|
CHECK_AND_ASSERT_MES(rsp.subs.size()==1, false, "wrong response");
|
||||||
|
CHECK_AND_ASSERT_MES(rsp.subs.front() == demo::get_test_data(), false, "wrong response");
|
||||||
|
transport.get_config_object().close(ctx.m_connection_id);
|
||||||
|
LOG_PRINT_GREEN("Client COMMAND_EXAMPLE_1 sync invoked ok", LOG_LEVEL_0);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
string_tools::set_module_name_and_folder(argv[0]);
|
||||||
|
|
||||||
|
//set up logging options
|
||||||
|
log_space::get_set_log_detalisation_level(true, LOG_LEVEL_2);
|
||||||
|
log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL);
|
||||||
|
log_space::log_singletone::add_logger(LOGGER_FILE,
|
||||||
|
log_space::log_singletone::get_default_log_file().c_str(),
|
||||||
|
log_space::log_singletone::get_default_log_folder().c_str());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LOG_PRINT("Demo server starting ...", LOG_LEVEL_0);
|
||||||
|
|
||||||
|
|
||||||
|
demo::demo_levin_server srv;
|
||||||
|
|
||||||
|
start_default_console(&srv);
|
||||||
|
|
||||||
|
std::string bind_param = "0.0.0.0";
|
||||||
|
std::string port = "12345";
|
||||||
|
|
||||||
|
if(!srv.init(port, bind_param))
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to initialize srv!");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
srv.run();
|
||||||
|
|
||||||
|
size_t c = 1;
|
||||||
|
while (!srv.is_stop())
|
||||||
|
{
|
||||||
|
|
||||||
|
demo::COMMAND_EXAMPLE_1::request req;
|
||||||
|
req.sub = demo::get_test_data();
|
||||||
|
bool r = communicate(srv.get_server(), demo::COMMAND_EXAMPLE_1::ID, req, "127.0.0.1", port, (c%2 == 0));
|
||||||
|
misc_utils::sleep_no_w(1000);
|
||||||
|
++c;
|
||||||
|
}
|
||||||
|
bool r = srv.wait_stop();
|
||||||
|
CHECK_AND_ASSERT_MES(r, 1, "failed to wait server stop");
|
||||||
|
|
||||||
|
|
||||||
|
srv.deinit();
|
||||||
|
|
||||||
|
LOG_PRINT("Demo server stoped.", LOG_LEVEL_0);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
CATCH_ENTRY_L0("main", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
namespace demo
|
||||||
|
{
|
||||||
|
bool demo_levin_server::init(const std::string& bind_port, const std::string& bind_ip)
|
||||||
|
{
|
||||||
|
m_net_server.get_config_object().m_pcommands_handler = this;
|
||||||
|
LOG_PRINT_L0("Binding on " << bind_ip << ":" << bind_port);
|
||||||
|
return m_net_server.init_server(bind_port, bind_ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool demo_levin_server::run()
|
||||||
|
{
|
||||||
|
m_stop = false;
|
||||||
|
//here you can set worker threads count
|
||||||
|
int thrds_count = 4;
|
||||||
|
m_net_server.get_config_object().m_invoke_timeout = 10000;
|
||||||
|
m_net_server.get_config_object().m_pcommands_handler = this;
|
||||||
|
|
||||||
|
//go to loop
|
||||||
|
LOG_PRINT("Run net_service loop( " << thrds_count << " threads)...", LOG_LEVEL_0);
|
||||||
|
if(!m_net_server.run_server(thrds_count, false))
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to run net tcp server!");
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_PRINT("net_service loop stopped.", LOG_LEVEL_0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool demo_levin_server::deinit()
|
||||||
|
{
|
||||||
|
return m_net_server.deinit_server();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool demo_levin_server::send_stop_signal()
|
||||||
|
{
|
||||||
|
m_net_server.send_stop_signal();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
int demo_levin_server::handle_command_1(int command, COMMAND_EXAMPLE_1::request& arg, COMMAND_EXAMPLE_1::response& rsp, const net_utils::connection_context_base& context)
|
||||||
|
{
|
||||||
|
CHECK_AND_ASSERT_MES(arg.sub == demo::get_test_data(), false, "wrong request");
|
||||||
|
rsp.m_success = true;
|
||||||
|
rsp.subs.push_back(arg.sub);
|
||||||
|
LOG_PRINT_GREEN("Server COMMAND_EXAMPLE_1 ok", LOG_LEVEL_0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
int demo_levin_server::handle_command_2(int command, COMMAND_EXAMPLE_2::request& arg, COMMAND_EXAMPLE_2::response& rsp, const net_utils::connection_context_base& context)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
int demo_levin_server::handle_notify_1(int command, COMMAND_EXAMPLE_1::request& arg, const net_utils::connection_context_base& context)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
int demo_levin_server::handle_notify_2(int command, COMMAND_EXAMPLE_2::request& arg, const net_utils::connection_context_base& context)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 <boost/thread.hpp>
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
|
||||||
|
#include "net/levin_server_cp2.h"
|
||||||
|
#include "transport_defs.h"
|
||||||
|
#include "storages/levin_abstract_invoke2.h"
|
||||||
|
|
||||||
|
using namespace epee;
|
||||||
|
|
||||||
|
namespace demo
|
||||||
|
{
|
||||||
|
|
||||||
|
class demo_levin_server: public levin::levin_commands_handler<>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool run();
|
||||||
|
bool init(const std::string& bind_port = "11112", const std::string& bind_ip = "0.0.0.0");
|
||||||
|
bool deinit();
|
||||||
|
bool send_stop_signal();
|
||||||
|
bool is_stop(){return m_stop;}
|
||||||
|
bool wait_stop(){return m_net_server.timed_wait_server_stop(100000);}
|
||||||
|
net_utils::boosted_levin_async_server& get_server(){return m_net_server;}
|
||||||
|
private:
|
||||||
|
|
||||||
|
|
||||||
|
CHAIN_LEVIN_INVOKE_MAP(); //move levin_commands_handler interface invoke(...) callbacks into invoke map
|
||||||
|
CHAIN_LEVIN_NOTIFY_STUB(); //move levin_commands_handler interface notify(...) callbacks into nothing
|
||||||
|
|
||||||
|
BEGIN_INVOKE_MAP2(demo_levin_server)
|
||||||
|
HANDLE_INVOKE_T2(COMMAND_EXAMPLE_1, &demo_levin_server::handle_command_1)
|
||||||
|
HANDLE_INVOKE_T2(COMMAND_EXAMPLE_2, &demo_levin_server::handle_command_2)
|
||||||
|
HANDLE_NOTIFY_T2(COMMAND_EXAMPLE_1, &demo_levin_server::handle_notify_1)
|
||||||
|
HANDLE_NOTIFY_T2(COMMAND_EXAMPLE_2, &demo_levin_server::handle_notify_2)
|
||||||
|
END_INVOKE_MAP2()
|
||||||
|
|
||||||
|
//----------------- commands handlers ----------------------------------------------
|
||||||
|
int handle_command_1(int command, COMMAND_EXAMPLE_1::request& arg, COMMAND_EXAMPLE_1::response& rsp, const net_utils::connection_context_base& context);
|
||||||
|
int handle_command_2(int command, COMMAND_EXAMPLE_2::request& arg, COMMAND_EXAMPLE_2::response& rsp, const net_utils::connection_context_base& context);
|
||||||
|
int handle_notify_1(int command, COMMAND_EXAMPLE_1::request& arg, const net_utils::connection_context_base& context);
|
||||||
|
int handle_notify_2(int command, COMMAND_EXAMPLE_2::request& arg, const net_utils::connection_context_base& context);
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
net_utils::boosted_levin_async_server m_net_server;
|
||||||
|
std::atomic<bool> m_stop;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "stdafx.h"
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 "targetver.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define BOOST_FILESYSTEM_VERSION 3
|
||||||
|
#define ENABLE_RELEASE_LOGGING
|
||||||
|
#include "log_opt_defs.h"
|
||||||
|
#include "misc_log_ex.h"
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// The following macros define the minimum required platform. The minimum required platform
|
||||||
|
// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run
|
||||||
|
// your application. The macros work by enabling all features available on platform versions up to and
|
||||||
|
// including the version specified.
|
||||||
|
|
||||||
|
// Modify the following defines if you have to target a platform prior to the ones specified below.
|
||||||
|
// Refer to MSDN for the latest info on corresponding values for different platforms.
|
||||||
|
#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista.
|
||||||
|
#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows.
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
#cmake -DBOOST_ROOT=/usr/local/proj/boost_1_49_0 -DBOOST_LIBRARYDIR=/usr/local/proj/boost_1_49_0/stage/lib ..
|
|
@ -0,0 +1,7 @@
|
||||||
|
mkdir build
|
||||||
|
|
||||||
|
cd build
|
||||||
|
|
||||||
|
cmake "-DBoost_USE_STATIC_LIBS=TRUE" -G "Visual Studio 11 Win64" ..
|
||||||
|
cd ..
|
||||||
|
pause
|
|
@ -0,0 +1,213 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "serialization/keyvalue_serialization.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace demo
|
||||||
|
{
|
||||||
|
|
||||||
|
struct some_test_subdata
|
||||||
|
{
|
||||||
|
std::string m_str;
|
||||||
|
|
||||||
|
BEGIN_KV_SERIALIZE_MAP()
|
||||||
|
KV_SERIALIZE(m_str)
|
||||||
|
END_KV_SERIALIZE_MAP()
|
||||||
|
};
|
||||||
|
|
||||||
|
struct some_test_data
|
||||||
|
{
|
||||||
|
std::string m_str;
|
||||||
|
uint64_t m_uint64;
|
||||||
|
uint32_t m_uint32;
|
||||||
|
uint16_t m_uint16;
|
||||||
|
uint8_t m_uint8;
|
||||||
|
int64_t m_int64;
|
||||||
|
int32_t m_int32;
|
||||||
|
int16_t m_int16;
|
||||||
|
int8_t m_int8;
|
||||||
|
double m_double;
|
||||||
|
bool m_bool;
|
||||||
|
std::list<std::string> m_list_of_str;
|
||||||
|
std::list<uint64_t> m_list_of_uint64_t;
|
||||||
|
std::list<uint32_t> m_list_of_uint32_t;
|
||||||
|
std::list<uint16_t> m_list_of_uint16_t;
|
||||||
|
std::list<uint8_t> m_list_of_uint8_t;
|
||||||
|
std::list<int64_t> m_list_of_int64_t;
|
||||||
|
std::list<int32_t> m_list_of_int32_t;
|
||||||
|
std::list<int16_t> m_list_of_int16_t;
|
||||||
|
std::list<int8_t> m_list_of_int8_t;
|
||||||
|
std::list<double> m_list_of_double;
|
||||||
|
std::list<bool> m_list_of_bool;
|
||||||
|
some_test_subdata m_subobj;
|
||||||
|
std::list<some_test_data> m_list_of_self;
|
||||||
|
|
||||||
|
BEGIN_KV_SERIALIZE_MAP()
|
||||||
|
KV_SERIALIZE(m_str)
|
||||||
|
KV_SERIALIZE(m_uint64)
|
||||||
|
KV_SERIALIZE(m_uint32)
|
||||||
|
KV_SERIALIZE(m_uint16)
|
||||||
|
KV_SERIALIZE(m_uint8)
|
||||||
|
KV_SERIALIZE(m_int64)
|
||||||
|
KV_SERIALIZE(m_int32)
|
||||||
|
KV_SERIALIZE(m_int16)
|
||||||
|
KV_SERIALIZE(m_int8)
|
||||||
|
KV_SERIALIZE(m_double)
|
||||||
|
KV_SERIALIZE(m_bool)
|
||||||
|
KV_SERIALIZE(m_subobj)
|
||||||
|
KV_SERIALIZE(m_list_of_str)
|
||||||
|
KV_SERIALIZE(m_list_of_uint64_t)
|
||||||
|
KV_SERIALIZE(m_list_of_uint32_t)
|
||||||
|
KV_SERIALIZE(m_list_of_uint16_t)
|
||||||
|
KV_SERIALIZE(m_list_of_uint8_t)
|
||||||
|
KV_SERIALIZE(m_list_of_int64_t)
|
||||||
|
KV_SERIALIZE(m_list_of_int32_t)
|
||||||
|
KV_SERIALIZE(m_list_of_int16_t)
|
||||||
|
KV_SERIALIZE(m_list_of_int8_t)
|
||||||
|
KV_SERIALIZE(m_list_of_double)
|
||||||
|
KV_SERIALIZE(m_list_of_bool)
|
||||||
|
KV_SERIALIZE(m_list_of_self)
|
||||||
|
END_KV_SERIALIZE_MAP()
|
||||||
|
};
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
struct COMMAND_EXAMPLE_1
|
||||||
|
{
|
||||||
|
const static int ID = 1000;
|
||||||
|
|
||||||
|
struct request
|
||||||
|
{
|
||||||
|
std::string example_string_data;
|
||||||
|
some_test_data sub;
|
||||||
|
|
||||||
|
BEGIN_KV_SERIALIZE_MAP()
|
||||||
|
KV_SERIALIZE(example_string_data)
|
||||||
|
KV_SERIALIZE(sub)
|
||||||
|
END_KV_SERIALIZE_MAP()
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct response
|
||||||
|
{
|
||||||
|
bool m_success;
|
||||||
|
std::list<some_test_data> subs;
|
||||||
|
|
||||||
|
BEGIN_KV_SERIALIZE_MAP()
|
||||||
|
KV_SERIALIZE(m_success)
|
||||||
|
KV_SERIALIZE(subs)
|
||||||
|
END_KV_SERIALIZE_MAP()
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct COMMAND_EXAMPLE_2
|
||||||
|
{
|
||||||
|
const static int ID = 1001;
|
||||||
|
|
||||||
|
struct request
|
||||||
|
{
|
||||||
|
std::string example_string_data2;
|
||||||
|
|
||||||
|
BEGIN_KV_SERIALIZE_MAP()
|
||||||
|
KV_SERIALIZE(example_string_data2)
|
||||||
|
END_KV_SERIALIZE_MAP()
|
||||||
|
};
|
||||||
|
|
||||||
|
struct response
|
||||||
|
{
|
||||||
|
bool m_success;
|
||||||
|
|
||||||
|
|
||||||
|
BEGIN_KV_SERIALIZE_MAP()
|
||||||
|
KV_SERIALIZE(m_success)
|
||||||
|
END_KV_SERIALIZE_MAP()
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------------------
|
||||||
|
//-------------------------------------------------------------------------------------
|
||||||
|
//in debug purpose
|
||||||
|
bool operator != (const some_test_subdata& a, const some_test_subdata& b)
|
||||||
|
{
|
||||||
|
return b.m_str != a.m_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator == (const some_test_data& a, const some_test_data& b)
|
||||||
|
{
|
||||||
|
if( b.m_str != a.m_str
|
||||||
|
|| b.m_uint64 != a.m_uint64
|
||||||
|
|| b.m_uint32 != a.m_uint32
|
||||||
|
|| b.m_uint16 != a.m_uint16
|
||||||
|
|| b.m_uint8 != a.m_uint8
|
||||||
|
|| b.m_int64 != a.m_int64
|
||||||
|
|| b.m_int32 != a.m_int32
|
||||||
|
|| b.m_int16 != a.m_int16
|
||||||
|
|| b.m_int8 != a.m_int8
|
||||||
|
|| b.m_double != a.m_double
|
||||||
|
|| b.m_bool != a.m_bool
|
||||||
|
|| b.m_list_of_str != a.m_list_of_str
|
||||||
|
|| b.m_list_of_uint64_t != a.m_list_of_uint64_t
|
||||||
|
|| b.m_list_of_uint32_t != a.m_list_of_uint32_t
|
||||||
|
|| b.m_list_of_uint16_t != a.m_list_of_uint16_t
|
||||||
|
|| b.m_list_of_uint8_t != a.m_list_of_uint8_t
|
||||||
|
|| b.m_list_of_int64_t != a.m_list_of_int64_t
|
||||||
|
|| b.m_list_of_int32_t != a.m_list_of_int32_t
|
||||||
|
|| b.m_list_of_int16_t != a.m_list_of_int16_t
|
||||||
|
|| b.m_list_of_int8_t != a.m_list_of_int8_t
|
||||||
|
|| b.m_list_of_double != a.m_list_of_double
|
||||||
|
|| b.m_list_of_bool != a.m_list_of_bool
|
||||||
|
|| b.m_subobj != a.m_subobj
|
||||||
|
|| b.m_list_of_self != a.m_list_of_self
|
||||||
|
)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline some_test_data get_test_data()
|
||||||
|
{
|
||||||
|
some_test_data s;
|
||||||
|
s.m_str = "zuzuzuzuzuz";
|
||||||
|
s.m_uint64 = 111111111111111;
|
||||||
|
s.m_uint32 = 2222222;
|
||||||
|
s.m_uint16 = 2222;
|
||||||
|
s.m_uint8 = 22;
|
||||||
|
s.m_int64 = -111111111111111;
|
||||||
|
s.m_int32 = -2222222;
|
||||||
|
s.m_int16 = -2222;
|
||||||
|
s.m_int8 = -24;
|
||||||
|
s.m_double = 0.11111;
|
||||||
|
s.m_bool = true;
|
||||||
|
s.m_list_of_str.push_back("1112121");
|
||||||
|
s.m_list_of_uint64_t.push_back(1111111111);
|
||||||
|
s.m_list_of_uint64_t.push_back(2222222222);
|
||||||
|
s.m_list_of_uint32_t.push_back(1111111);
|
||||||
|
s.m_list_of_uint32_t.push_back(2222222);
|
||||||
|
s.m_list_of_uint16_t.push_back(1111);
|
||||||
|
s.m_list_of_uint16_t.push_back(2222);
|
||||||
|
s.m_list_of_uint8_t.push_back(11);
|
||||||
|
s.m_list_of_uint8_t.push_back(22);
|
||||||
|
|
||||||
|
|
||||||
|
s.m_list_of_int64_t.push_back(-1111111111);
|
||||||
|
s.m_list_of_int64_t.push_back(-222222222);
|
||||||
|
s.m_list_of_int32_t.push_back(-1111111);
|
||||||
|
s.m_list_of_int32_t.push_back(-2222222);
|
||||||
|
s.m_list_of_int16_t.push_back(-1111);
|
||||||
|
s.m_list_of_int16_t.push_back(-2222);
|
||||||
|
s.m_list_of_int8_t.push_back(-11);
|
||||||
|
s.m_list_of_int8_t.push_back(-22);
|
||||||
|
|
||||||
|
s.m_list_of_double.push_back(0.11111);
|
||||||
|
s.m_list_of_double.push_back(0.22222);
|
||||||
|
s.m_list_of_bool.push_back(true);
|
||||||
|
s.m_list_of_bool.push_back(false);
|
||||||
|
|
||||||
|
s.m_subobj.m_str = "subszzzzzzzz";
|
||||||
|
s.m_list_of_self.push_back(s);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,310 @@
|
||||||
|
// Copyright (c) 2006-2013, 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
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template<class t_server>
|
||||||
|
bool empty_commands_handler(t_server* psrv, const std::string& command)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class t_server, class chain_handler>
|
||||||
|
bool default_console_handler(t_server* psrv, chain_handler ch_handler, const std::string usage = "")
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
bool continue_handle = true;
|
||||||
|
while(continue_handle)
|
||||||
|
{
|
||||||
|
char command_buff[400] = {0};
|
||||||
|
std::string command;
|
||||||
|
std::cin.getline(command_buff, 399);
|
||||||
|
if(std::cin.eof() || std::cin.fail())
|
||||||
|
{
|
||||||
|
LOG_PRINT("std::cin.eof() or std::cin.fail(), stopping...", LOG_LEVEL_0);
|
||||||
|
continue_handle = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
command = command_buff;
|
||||||
|
|
||||||
|
if(!command.compare("exit") || !command.compare("q") )
|
||||||
|
{
|
||||||
|
psrv->send_stop_signal();
|
||||||
|
continue_handle = false;
|
||||||
|
}else if ( !command.compare(0, 7, "set_log"))
|
||||||
|
{
|
||||||
|
//parse set_log command
|
||||||
|
if(command.size() != 9)
|
||||||
|
{
|
||||||
|
std::cout << "wrong syntax: " << command << std::endl << "use set_log n" << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int n = 0;
|
||||||
|
if(!string_tools::get_xtype_from_string(n, command.substr(8, 1)))
|
||||||
|
{
|
||||||
|
std::cout << "wrong syntax: " << command << std::endl << "use set_log n" << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
log_space::get_set_log_detalisation_level(true, n);
|
||||||
|
LOG_PRINT_L0("New log level set " << n);
|
||||||
|
}
|
||||||
|
else if(ch_handler(psrv, command))
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << "unknown command: " << command << std::endl;
|
||||||
|
std::cout << usage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
CATCH_ENTRY_L0("console_handler", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class chain_handler>
|
||||||
|
bool default_console_handler2(chain_handler ch_handler, const std::string usage)
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
bool continue_handle = true;
|
||||||
|
while(continue_handle)
|
||||||
|
{
|
||||||
|
char command_buff[400] = {0};
|
||||||
|
std::string command;
|
||||||
|
std::cin.getline(command_buff, 399);
|
||||||
|
if(std::cin.eof() || std::cin.fail())
|
||||||
|
{
|
||||||
|
|
||||||
|
LOG_PRINT("std::cin.eof() or std::cin.fail(), stopping...", LOG_LEVEL_0);
|
||||||
|
continue_handle = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
command = command_buff;
|
||||||
|
|
||||||
|
if(!command.compare("exit") || !command.compare("q") )
|
||||||
|
{
|
||||||
|
continue_handle = false;
|
||||||
|
}else if ( !command.compare(0, 7, "set_log"))
|
||||||
|
{
|
||||||
|
//parse set_log command
|
||||||
|
if(command.size() != 9)
|
||||||
|
{
|
||||||
|
std::cout << "wrong syntax: " << command << std::endl << "use set_log n" << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int n = 0;
|
||||||
|
if(!string_tools::get_xtype_from_string(n, command.substr(8, 1)))
|
||||||
|
{
|
||||||
|
std::cout << "wrong syntax: " << command << std::endl << "use set_log n" << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
log_space::get_set_log_detalisation_level(true, n);
|
||||||
|
LOG_PRINT_L0("New log level set " << n);
|
||||||
|
}
|
||||||
|
else if(ch_handler(command))
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << "unknown command: " << command << std::endl;
|
||||||
|
std::cout << usage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
CATCH_ENTRY_L0("console_handler", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template<class t_server, class t_handler>
|
||||||
|
bool start_default_console(t_server* ptsrv, t_handler handlr, const std::string& usage = "")
|
||||||
|
{
|
||||||
|
boost::thread( boost::bind(default_console_handler<t_server, t_handler>, ptsrv, handlr, usage) );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class t_server>
|
||||||
|
bool start_default_console(t_server* ptsrv, const std::string& usage = "")
|
||||||
|
{
|
||||||
|
return start_default_console(ptsrv, empty_commands_handler<t_server>, usage);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class t_server, class t_handler>
|
||||||
|
bool no_srv_param_adapter(t_server* ptsrv, const std::string& cmd, t_handler handlr)
|
||||||
|
{
|
||||||
|
return handlr(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class t_server, class t_handler>
|
||||||
|
bool run_default_console_handler_no_srv_param(t_server* ptsrv, t_handler handlr, const std::string& usage = "")
|
||||||
|
{
|
||||||
|
return default_console_handler(ptsrv, boost::bind<bool>(no_srv_param_adapter<t_server, t_handler>, _1, _2, handlr), usage);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class t_server, class t_handler>
|
||||||
|
bool start_default_console_handler_no_srv_param(t_server* ptsrv, t_handler handlr, const std::string& usage = "")
|
||||||
|
{
|
||||||
|
boost::thread( boost::bind(run_default_console_handler_no_srv_param<t_server, t_handler>, ptsrv, handlr, usage) );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*template<class a>
|
||||||
|
bool f(int i, a l)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}*/
|
||||||
|
/*
|
||||||
|
template<class chain_handler>
|
||||||
|
bool default_console_handler2(chain_handler ch_handler, const std::string usage)
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*template<class t_handler>
|
||||||
|
bool start_default_console2(t_handler handlr, const std::string& usage = "")
|
||||||
|
{
|
||||||
|
//std::string usage_local = usage;
|
||||||
|
boost::thread( boost::bind(default_console_handler2<t_handler>, handlr, usage) );
|
||||||
|
//boost::function<bool ()> p__ = boost::bind(f<t_handler>, 1, handlr);
|
||||||
|
//boost::function<bool ()> p__ = boost::bind(default_console_handler2<t_handler>, handlr, usage);
|
||||||
|
//boost::thread tr(p__);
|
||||||
|
return true;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
class console_handlers_binder
|
||||||
|
{
|
||||||
|
typedef boost::function<bool (const std::vector<std::string> &)> console_command_handler;
|
||||||
|
typedef std::map<std::string, std::pair<console_command_handler, std::string> > command_handlers_map;
|
||||||
|
std::unique_ptr<boost::thread> m_console_thread;
|
||||||
|
command_handlers_map m_command_handlers;
|
||||||
|
public:
|
||||||
|
std::string get_usage()
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
size_t max_command_len = 0;
|
||||||
|
for(auto& x:m_command_handlers)
|
||||||
|
if(x.first.size() > max_command_len)
|
||||||
|
max_command_len = x.first.size();
|
||||||
|
|
||||||
|
for(auto& x:m_command_handlers)
|
||||||
|
{
|
||||||
|
ss.width(max_command_len + 3);
|
||||||
|
ss << std::left << x.first << x.second.second << ENDL;
|
||||||
|
}
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
void set_handler(const std::string& cmd, const console_command_handler& hndlr, const std::string& usage = "")
|
||||||
|
{
|
||||||
|
command_handlers_map::mapped_type & vt = m_command_handlers[cmd];
|
||||||
|
vt.first = hndlr;
|
||||||
|
vt.second = usage;
|
||||||
|
}
|
||||||
|
bool process_command_vec(const std::vector<std::string>& cmd)
|
||||||
|
{
|
||||||
|
if(!cmd.size())
|
||||||
|
return false;
|
||||||
|
auto it = m_command_handlers.find(cmd.front());
|
||||||
|
if(it == m_command_handlers.end())
|
||||||
|
return false;
|
||||||
|
std::vector<std::string> cmd_local(cmd.begin()+1, cmd.end());
|
||||||
|
return it->second.first(cmd_local);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool process_command_str(const std::string& cmd)
|
||||||
|
{
|
||||||
|
std::vector<std::string> cmd_v;
|
||||||
|
boost::split(cmd_v,cmd,boost::is_any_of(" "), boost::token_compress_on);
|
||||||
|
return process_command_vec(cmd_v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*template<class t_srv>
|
||||||
|
bool start_handling(t_srv& srv, const std::string& usage_string = "")
|
||||||
|
{
|
||||||
|
start_default_console_handler_no_srv_param(&srv, boost::bind(&console_handlers_binder::process_command_str, this, _1));
|
||||||
|
return true;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
bool start_handling(const std::string& usage_string = "")
|
||||||
|
{
|
||||||
|
m_console_thread.reset(new boost::thread(boost::bind(&console_handlers_binder::run_handling, this, usage_string) ));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool stop_handling()
|
||||||
|
{
|
||||||
|
if(m_console_thread.get())
|
||||||
|
m_console_thread->interrupt();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool run_handling(const std::string usage_string)
|
||||||
|
{
|
||||||
|
return default_console_handler2(boost::bind(&console_handlers_binder::process_command_str, this, _1), usage_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*template<class t_srv>
|
||||||
|
bool run_handling(t_srv& srv, const std::string& usage_string)
|
||||||
|
{
|
||||||
|
return run_default_console_handler_no_srv_param(&srv, boost::bind<bool>(&console_handlers_binder::process_command_str, this, _1), usage_string);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/* work around because of broken boost bind */
|
||||||
|
template<class t_server>
|
||||||
|
class srv_console_handlers_binder: public console_handlers_binder
|
||||||
|
{
|
||||||
|
bool process_command_str(t_server* /*psrv*/, const std::string& cmd)
|
||||||
|
{
|
||||||
|
return console_handlers_binder::process_command_str(cmd);
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
bool start_handling(t_server* psrv, const std::string& usage_string = "")
|
||||||
|
{
|
||||||
|
boost::thread(boost::bind(&srv_console_handlers_binder<t_server>::run_handling, this, psrv, usage_string) );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool run_handling(t_server* psrv, const std::string usage_string)
|
||||||
|
{
|
||||||
|
return default_console_handler(psrv, boost::bind(&srv_console_handlers_binder<t_server>::process_command_str, this, _1, _2), usage_string);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,455 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _FILE_IO_UTILS_H_
|
||||||
|
#define _FILE_IO_UTILS_H_
|
||||||
|
|
||||||
|
|
||||||
|
//#include <sys/types.h>
|
||||||
|
//#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef MAKE64
|
||||||
|
#define MAKE64(low,high) ((__int64)(((DWORD)(low)) | ((__int64)((DWORD)(high))) << 32))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WINDOWS_PLATFORM
|
||||||
|
#include <psapi.h>
|
||||||
|
#include <strsafe.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <mbstring.h>
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace file_io_utils
|
||||||
|
{
|
||||||
|
#ifdef WINDOWS_PLATFORM
|
||||||
|
|
||||||
|
inline
|
||||||
|
std::string get_temp_file_name_a()
|
||||||
|
{
|
||||||
|
std::string str_result;
|
||||||
|
char sz_temp[MAX_PATH*2] = {0};
|
||||||
|
if(!::GetTempPathA( sizeof( sz_temp ), sz_temp ))
|
||||||
|
return str_result;
|
||||||
|
|
||||||
|
char sz_temp_file[MAX_PATH*2] = {0};
|
||||||
|
if(!::GetTempFileNameA( sz_temp, "mail", 0, sz_temp_file))
|
||||||
|
return str_result;
|
||||||
|
sz_temp_file[sizeof(sz_temp_file)-1] = 0; //be happy!
|
||||||
|
str_result = sz_temp_file;
|
||||||
|
return str_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef BOOST_LEXICAL_CAST_INCLUDED
|
||||||
|
inline
|
||||||
|
bool get_not_used_filename(const std::string& folder, OUT std::string& result_name)
|
||||||
|
{
|
||||||
|
DWORD folder_attr = ::GetFileAttributesA(folder.c_str());
|
||||||
|
if(folder_attr == INVALID_FILE_ATTRIBUTES)
|
||||||
|
return false;
|
||||||
|
if(!(folder_attr&FILE_ATTRIBUTE_DIRECTORY))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
std::string base_name = folder + "\\tmp";
|
||||||
|
std::string tmp_name;
|
||||||
|
bool name_found = false;
|
||||||
|
int current_index = 0;
|
||||||
|
tmp_name = base_name + boost::lexical_cast<std::string>(current_index) + ".tmp";
|
||||||
|
while(!name_found)
|
||||||
|
{
|
||||||
|
if(INVALID_FILE_ATTRIBUTES == ::GetFileAttributesA(tmp_name.c_str()))
|
||||||
|
name_found = true;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
current_index++;
|
||||||
|
tmp_name = base_name + boost::lexical_cast<std::string>(current_index) + ".tmp";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result_name = tmp_name;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
inline
|
||||||
|
std::string get_temp_folder_a()
|
||||||
|
{
|
||||||
|
std::string str_result;
|
||||||
|
char sz_temp[MAX_PATH*2] = {0};
|
||||||
|
if(!::GetTempPathA( sizeof( sz_temp ), sz_temp ))
|
||||||
|
return str_result;
|
||||||
|
sz_temp[(sizeof(sz_temp)/sizeof(sz_temp[0])) -1] = 0;
|
||||||
|
str_result = sz_temp;
|
||||||
|
return str_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string convert_from_device_path_to_standart(const std::string& path)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
STRSAFE_LPSTR pszFilename = (STRSAFE_LPSTR)path.c_str();
|
||||||
|
|
||||||
|
// Translate path with device name to drive letters.
|
||||||
|
char szTemp[4000] = {0};
|
||||||
|
|
||||||
|
if (::GetLogicalDriveStringsA(sizeof(szTemp)-1, szTemp))
|
||||||
|
{
|
||||||
|
char szName[MAX_PATH];
|
||||||
|
char szDrive[3] = " :";
|
||||||
|
BOOL bFound = FALSE;
|
||||||
|
char* p = szTemp;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Copy the drive letter to the template string
|
||||||
|
*szDrive = *p;
|
||||||
|
|
||||||
|
// Look up each device name
|
||||||
|
if (::QueryDosDeviceA(szDrive, szName, sizeof(szName)))
|
||||||
|
{
|
||||||
|
UINT uNameLen = strlen(szName);
|
||||||
|
|
||||||
|
if (uNameLen < MAX_PATH)
|
||||||
|
{
|
||||||
|
bFound = _mbsnbicmp((const unsigned char*)pszFilename, (const unsigned char*)szName,
|
||||||
|
uNameLen) == 0;
|
||||||
|
|
||||||
|
if (bFound)
|
||||||
|
{
|
||||||
|
// Reconstruct pszFilename using szTempFile
|
||||||
|
// Replace device path with DOS path
|
||||||
|
char szTempFile[MAX_PATH] = {0};
|
||||||
|
StringCchPrintfA(szTempFile,
|
||||||
|
MAX_PATH,
|
||||||
|
"%s%s",
|
||||||
|
szDrive,
|
||||||
|
pszFilename+uNameLen);
|
||||||
|
return szTempFile;
|
||||||
|
//::StringCchCopyNA(pszFilename, MAX_PATH+1, szTempFile, strlen(szTempFile));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go to the next NULL character.
|
||||||
|
while (*p++);
|
||||||
|
} while (!bFound && *p); // end of string
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
std::string get_process_path_by_pid(DWORD pid)
|
||||||
|
{
|
||||||
|
std::string res;
|
||||||
|
|
||||||
|
HANDLE hprocess = 0;
|
||||||
|
if( hprocess = ::OpenProcess( PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, FALSE, pid) )
|
||||||
|
{
|
||||||
|
char buff[MAX_PATH]= {0};
|
||||||
|
if(!::GetModuleFileNameExA( hprocess, 0, buff, MAX_PATH - 1 ))
|
||||||
|
res = "Unknown_b";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buff[MAX_PATH - 1]=0; //be happy!
|
||||||
|
res = buff;
|
||||||
|
std::string::size_type a = res.rfind( '\\' );
|
||||||
|
if ( a != std::string::npos )
|
||||||
|
res.erase( 0, a+1);
|
||||||
|
|
||||||
|
}
|
||||||
|
::CloseHandle( hprocess );
|
||||||
|
}else
|
||||||
|
res = "Unknown_a";
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
inline
|
||||||
|
std::wstring get_temp_file_name_w()
|
||||||
|
{
|
||||||
|
std::wstring str_result;
|
||||||
|
wchar_t sz_temp[MAX_PATH*2] = {0};
|
||||||
|
if(!::GetTempPathW( sizeof(sz_temp)/sizeof(sz_temp[0]), sz_temp ))
|
||||||
|
return str_result;
|
||||||
|
|
||||||
|
wchar_t sz_temp_file[MAX_PATH+1] = {0};
|
||||||
|
if(!::GetTempFileNameW( sz_temp, L"mail", 0, sz_temp_file))
|
||||||
|
return str_result;
|
||||||
|
|
||||||
|
sz_temp_file[(sizeof(sz_temp_file)/sizeof(sz_temp_file[0]))-1] = 0; //be happy!
|
||||||
|
str_result = sz_temp_file;
|
||||||
|
return str_result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
inline
|
||||||
|
bool is_file_exist(const std::string& path)
|
||||||
|
{
|
||||||
|
boost::filesystem::path p(path);
|
||||||
|
return boost::filesystem::exists(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
inline
|
||||||
|
bool save_string_to_handle(HANDLE hfile, const std::string& str)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if( INVALID_HANDLE_VALUE != hfile )
|
||||||
|
{
|
||||||
|
DWORD dw;
|
||||||
|
if( !::WriteFile( hfile, str.data(), (DWORD) str.size(), &dw, NULL) )
|
||||||
|
{
|
||||||
|
int err_code = GetLastError();
|
||||||
|
//LOG_PRINT("Failed to write to file handle: " << hfile<< " Last error code:" << err_code << " : " << log_space::get_win32_err_descr(err_code), LOG_LEVEL_2);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
::CloseHandle(hfile);
|
||||||
|
return true;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
//LOG_WIN32_ERROR(::GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool save_string_to_file(const std::string& path_to_file, const std::string& str)
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::ofstream fstream;
|
||||||
|
fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||||
|
fstream.open(path_to_file, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
|
||||||
|
fstream << str;
|
||||||
|
fstream.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
inline
|
||||||
|
bool load_form_handle(HANDLE hfile, std::string& str)
|
||||||
|
{
|
||||||
|
if( INVALID_HANDLE_VALUE != hfile )
|
||||||
|
{
|
||||||
|
bool res = true;
|
||||||
|
DWORD dw = 0;
|
||||||
|
DWORD fsize = ::GetFileSize(hfile, &dw);
|
||||||
|
if(fsize > 300000000)
|
||||||
|
{
|
||||||
|
::CloseHandle(hfile);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(fsize)
|
||||||
|
{
|
||||||
|
str.resize(fsize);
|
||||||
|
if(!::ReadFile( hfile, (LPVOID)str.data(), (DWORD)str.size(), &dw, NULL))
|
||||||
|
res = false;
|
||||||
|
}
|
||||||
|
::CloseHandle(hfile);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
inline
|
||||||
|
bool get_file_time(const std::string& path_to_file, OUT time_t& ft)
|
||||||
|
{
|
||||||
|
boost::system::error_code ec;
|
||||||
|
ft = boost::filesystem::last_write_time(boost::filesystem::path(path_to_file), ec);
|
||||||
|
if(!ec)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool set_file_time(const std::string& path_to_file, const time_t& ft)
|
||||||
|
{
|
||||||
|
boost::system::error_code ec;
|
||||||
|
boost::filesystem::last_write_time(boost::filesystem::path(path_to_file), ft, ec);
|
||||||
|
if(!ec)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool load_file_to_string(const std::string& path_to_file, std::string& target_str)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::ifstream fstream;
|
||||||
|
fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||||
|
fstream.open(path_to_file, std::ios_base::binary | std::ios_base::in | std::ios::ate);
|
||||||
|
|
||||||
|
std::ifstream::pos_type file_size = fstream.tellg();
|
||||||
|
|
||||||
|
if(file_size > 1000000000)
|
||||||
|
return false;//don't go crazy
|
||||||
|
size_t file_size_t = static_cast<size_t>(file_size);
|
||||||
|
|
||||||
|
target_str.resize(file_size_t);
|
||||||
|
|
||||||
|
fstream.seekg (0, std::ios::beg);
|
||||||
|
fstream.read((char*)target_str.data(), target_str.size());
|
||||||
|
fstream.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool append_string_to_file(const std::string& path_to_file, const std::string& str)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::ofstream fstream;
|
||||||
|
fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||||
|
fstream.open(path_to_file.c_str(), std::ios_base::binary | std::ios_base::out | std::ios_base::app);
|
||||||
|
fstream << str;
|
||||||
|
fstream.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
bool remove_dir_and_subirs(const char* path_to_dir);
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool clean_dir(const char* path_to_dir)
|
||||||
|
{
|
||||||
|
if(!path_to_dir)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string folder = path_to_dir;
|
||||||
|
WIN32_FIND_DATAA find_data = {0};
|
||||||
|
HANDLE hfind = ::FindFirstFileA((folder + "\\*.*").c_str(), &find_data);
|
||||||
|
if(INVALID_HANDLE_VALUE == hfind)
|
||||||
|
return false;
|
||||||
|
do{
|
||||||
|
if(!strcmp("..", find_data.cFileName) || (!strcmp(".", find_data.cFileName)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(find_data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
|
||||||
|
{
|
||||||
|
if(!remove_dir_and_subirs((folder + "\\" + find_data.cFileName).c_str()))
|
||||||
|
return false;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
if(!::DeleteFileA((folder + "\\" + find_data.cFileName).c_str()))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}while(::FindNextFileA(hfind, &find_data));
|
||||||
|
::FindClose(hfind);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#ifdef WINDOWS_PLATFORM
|
||||||
|
inline bool get_folder_content(const std::string& path, std::list<WIN32_FIND_DATAA>& OUT target_list)
|
||||||
|
{
|
||||||
|
WIN32_FIND_DATAA find_data = {0};
|
||||||
|
HANDLE hfind = ::FindFirstFileA((path + "\\*.*").c_str(), &find_data);
|
||||||
|
if(INVALID_HANDLE_VALUE == hfind)
|
||||||
|
return false;
|
||||||
|
do{
|
||||||
|
if(!strcmp("..", find_data.cFileName) || (!strcmp(".", find_data.cFileName)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
target_list.push_back(find_data);
|
||||||
|
|
||||||
|
}while(::FindNextFileA(hfind, &find_data));
|
||||||
|
::FindClose(hfind);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
inline bool get_folder_content(const std::string& path, std::list<std::string>& OUT target_list, bool only_files = false)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end
|
||||||
|
for ( boost::filesystem::directory_iterator itr( path ); itr != end_itr; ++itr )
|
||||||
|
{
|
||||||
|
if ( only_files && boost::filesystem::is_directory(itr->status()) )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
target_list.push_back(itr->path().filename().string());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //_FILE_IO_UTILS_H_
|
|
@ -0,0 +1,35 @@
|
||||||
|
// Copyright (c) 2006-2013, 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
|
||||||
|
|
||||||
|
std::stringstream& operator<<(std::stringstream& out, const std::wstring& ws)
|
||||||
|
{
|
||||||
|
std::string as = string_encoding::convert_to_ansii(ws);
|
||||||
|
out << as;
|
||||||
|
return out;
|
||||||
|
}
|
|
@ -0,0 +1,227 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _GZIP_ENCODING_H_
|
||||||
|
#define _GZIP_ENCODING_H_
|
||||||
|
#include "net/http_client_base.h"
|
||||||
|
#include "zlib/zlib.h"
|
||||||
|
//#include "http.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class content_encoding_gzip: public i_sub_handler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*! \brief
|
||||||
|
* Function content_encoding_gzip : Constructor
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
inline
|
||||||
|
content_encoding_gzip(i_target_handler* powner_filter, bool is_deflate_mode = false):m_powner_filter(powner_filter),
|
||||||
|
m_is_stream_ended(false),
|
||||||
|
m_is_deflate_mode(is_deflate_mode),
|
||||||
|
m_is_first_update_in(true)
|
||||||
|
{
|
||||||
|
memset(&m_zstream_in, 0, sizeof(m_zstream_in));
|
||||||
|
memset(&m_zstream_out, 0, sizeof(m_zstream_out));
|
||||||
|
int ret = 0;
|
||||||
|
if(is_deflate_mode)
|
||||||
|
{
|
||||||
|
ret = inflateInit(&m_zstream_in);
|
||||||
|
ret = deflateInit(&m_zstream_out, Z_DEFAULT_COMPRESSION);
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
ret = inflateInit2(&m_zstream_in, 0x1F);
|
||||||
|
ret = deflateInit2(&m_zstream_out, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 0x1F, 8, Z_DEFAULT_STRATEGY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*! \brief
|
||||||
|
* Function content_encoding_gzip : Destructor
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
inline
|
||||||
|
~content_encoding_gzip()
|
||||||
|
{
|
||||||
|
inflateEnd(& m_zstream_in );
|
||||||
|
deflateEnd(& m_zstream_out );
|
||||||
|
}
|
||||||
|
/*! \brief
|
||||||
|
* Function update_in : Entry point for income data
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
inline
|
||||||
|
virtual bool update_in( std::string& piece_of_transfer)
|
||||||
|
{
|
||||||
|
|
||||||
|
bool is_first_time_here = m_is_first_update_in;
|
||||||
|
m_is_first_update_in = false;
|
||||||
|
|
||||||
|
if(m_pre_decode.size())
|
||||||
|
m_pre_decode += piece_of_transfer;
|
||||||
|
else
|
||||||
|
m_pre_decode.swap(piece_of_transfer);
|
||||||
|
piece_of_transfer.clear();
|
||||||
|
|
||||||
|
std::string decode_summary_buff;
|
||||||
|
|
||||||
|
size_t ungzip_size = m_pre_decode.size() * 0x30;
|
||||||
|
std::string current_decode_buff(ungzip_size, 'X');
|
||||||
|
|
||||||
|
//Here the cycle is introduced where we unpack the buffer, the cycle is required
|
||||||
|
//because of the case where if after unpacking the data will exceed the awaited size, we will not halt with error
|
||||||
|
bool continue_unpacking = true;
|
||||||
|
bool first_step = true;
|
||||||
|
while(m_pre_decode.size() && continue_unpacking)
|
||||||
|
{
|
||||||
|
|
||||||
|
//fill buffers
|
||||||
|
m_zstream_in.next_in = (Bytef*)m_pre_decode.data();
|
||||||
|
m_zstream_in.avail_in = (uInt)m_pre_decode.size();
|
||||||
|
m_zstream_in.next_out = (Bytef*)current_decode_buff.data();
|
||||||
|
m_zstream_in.avail_out = (uInt)ungzip_size;
|
||||||
|
|
||||||
|
int flag = Z_SYNC_FLUSH;
|
||||||
|
int ret = inflate(&m_zstream_in, flag);
|
||||||
|
CHECK_AND_ASSERT_MES(ret>=0 || m_zstream_in.avail_out ||m_is_deflate_mode, false, "content_encoding_gzip::update_in() Failed to inflate. err = " << ret);
|
||||||
|
|
||||||
|
if(Z_STREAM_END == ret)
|
||||||
|
m_is_stream_ended = true;
|
||||||
|
else if(Z_DATA_ERROR == ret && is_first_time_here && m_is_deflate_mode&& first_step)
|
||||||
|
{
|
||||||
|
// some servers (notably Apache with mod_deflate) don't generate zlib headers
|
||||||
|
// insert a dummy header and try again
|
||||||
|
static char dummy_head[2] =
|
||||||
|
{
|
||||||
|
0x8 + 0x7 * 0x10,
|
||||||
|
(((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF,
|
||||||
|
};
|
||||||
|
inflateReset(&m_zstream_in);
|
||||||
|
m_zstream_in.next_in = (Bytef*) dummy_head;
|
||||||
|
m_zstream_in.avail_in = sizeof(dummy_head);
|
||||||
|
|
||||||
|
ret = inflate(&m_zstream_in, Z_NO_FLUSH);
|
||||||
|
if (ret != Z_OK)
|
||||||
|
{
|
||||||
|
LOCAL_ASSERT(0);
|
||||||
|
m_pre_decode.swap(piece_of_transfer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_zstream_in.next_in = (Bytef*)m_pre_decode.data();
|
||||||
|
m_zstream_in.avail_in = (uInt)m_pre_decode.size();
|
||||||
|
|
||||||
|
ret = inflate(&m_zstream_in, Z_NO_FLUSH);
|
||||||
|
if (ret != Z_OK)
|
||||||
|
{
|
||||||
|
LOCAL_ASSERT(0);
|
||||||
|
m_pre_decode.swap(piece_of_transfer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//leave only unpacked part in the output buffer to start with it the next time
|
||||||
|
m_pre_decode.erase(0, m_pre_decode.size()-m_zstream_in.avail_in);
|
||||||
|
//if decoder gave nothing to return, then everything is ahead, now simply break
|
||||||
|
if(ungzip_size == m_zstream_in.avail_out)
|
||||||
|
break;
|
||||||
|
|
||||||
|
//decode_buff currently stores data parts that were unpacked, fix this size
|
||||||
|
current_decode_buff.resize(ungzip_size - m_zstream_in.avail_out);
|
||||||
|
if(decode_summary_buff.size())
|
||||||
|
decode_summary_buff += current_decode_buff;
|
||||||
|
else
|
||||||
|
current_decode_buff.swap(decode_summary_buff);
|
||||||
|
|
||||||
|
current_decode_buff.resize(ungzip_size);
|
||||||
|
first_step = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Process these data if required
|
||||||
|
bool res = true;
|
||||||
|
|
||||||
|
res = m_powner_filter->handle_target_data(decode_summary_buff);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
/*! \brief
|
||||||
|
* Function stop : Entry point for stop signal and flushing cached data buffer.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
inline
|
||||||
|
virtual void stop(std::string& OUT collect_remains)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
private:
|
||||||
|
/*! \brief
|
||||||
|
* Pointer to parent HTTP-parser
|
||||||
|
*/
|
||||||
|
i_target_handler* m_powner_filter;
|
||||||
|
/*! \brief
|
||||||
|
* ZLIB object for income stream
|
||||||
|
*/
|
||||||
|
z_stream m_zstream_in;
|
||||||
|
/*! \brief
|
||||||
|
* ZLIB object for outcome stream
|
||||||
|
*/
|
||||||
|
z_stream m_zstream_out;
|
||||||
|
/*! \brief
|
||||||
|
* Data that could not be unpacked immediately, left to wait for the next packet of data
|
||||||
|
*/
|
||||||
|
std::string m_pre_decode;
|
||||||
|
/*! \brief
|
||||||
|
* The data are accumulated for a package in the buffer to send the web client
|
||||||
|
*/
|
||||||
|
std::string m_pre_encode;
|
||||||
|
/*! \brief
|
||||||
|
* Signals that stream looks like ended
|
||||||
|
*/
|
||||||
|
bool m_is_stream_ended;
|
||||||
|
/*! \brief
|
||||||
|
* If this flag is set, income data is in HTTP-deflate mode
|
||||||
|
*/
|
||||||
|
bool m_is_deflate_mode;
|
||||||
|
/*! \brief
|
||||||
|
* Marks that it is a first data packet
|
||||||
|
*/
|
||||||
|
bool m_is_first_update_in;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif //_GZIP_ENCODING_H_
|
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* libEtPan! -- a mail stuff library
|
||||||
|
*
|
||||||
|
* Copyright (C) 2001, 2005 - DINH Viet Hoa
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. 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.
|
||||||
|
* 3. Neither the name of the libEtPan! project 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 AUTHORS 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 AUTHORS OR CONTRIBUTORS 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* hmac-md5.h -- HMAC_MD5 functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* $Id: hmac-md5.h,v 1.1.1.1 2005/03/18 20:17:28 zautrix Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HMAC_MD5_H
|
||||||
|
#define HMAC_MD5_H 1
|
||||||
|
|
||||||
|
namespace md5
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define HMAC_MD5_SIZE 16
|
||||||
|
|
||||||
|
/* intermediate MD5 context */
|
||||||
|
typedef struct HMAC_MD5_CTX_s {
|
||||||
|
MD5_CTX ictx, octx;
|
||||||
|
} HMAC_MD5_CTX;
|
||||||
|
|
||||||
|
/* intermediate HMAC state
|
||||||
|
* values stored in network byte order (Big Endian)
|
||||||
|
*/
|
||||||
|
typedef struct HMAC_MD5_STATE_s {
|
||||||
|
UINT4 istate[4];
|
||||||
|
UINT4 ostate[4];
|
||||||
|
} HMAC_MD5_STATE;
|
||||||
|
|
||||||
|
/* One step hmac computation
|
||||||
|
*
|
||||||
|
* digest may be same as text or key
|
||||||
|
*/
|
||||||
|
void hmac_md5(const unsigned char *text, int text_len,
|
||||||
|
const unsigned char *key, int key_len,
|
||||||
|
unsigned char digest[HMAC_MD5_SIZE]);
|
||||||
|
|
||||||
|
/* create context from key
|
||||||
|
*/
|
||||||
|
void hmac_md5_init(HMAC_MD5_CTX *hmac,
|
||||||
|
const unsigned char *key, int key_len);
|
||||||
|
|
||||||
|
/* precalculate intermediate state from key
|
||||||
|
*/
|
||||||
|
void hmac_md5_precalc(HMAC_MD5_STATE *hmac,
|
||||||
|
const unsigned char *key, int key_len);
|
||||||
|
|
||||||
|
/* initialize context from intermediate state
|
||||||
|
*/
|
||||||
|
void hmac_md5_import(HMAC_MD5_CTX *hmac, HMAC_MD5_STATE *state);
|
||||||
|
|
||||||
|
#define hmac_md5_update(hmac, text, text_len) MD5Update(&(hmac)->ictx, (text), (text_len))
|
||||||
|
|
||||||
|
/* finish hmac from intermediate result. Intermediate result is zeroed.
|
||||||
|
*/
|
||||||
|
void hmac_md5_final(unsigned char digest[HMAC_MD5_SIZE],
|
||||||
|
HMAC_MD5_CTX *hmac);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* HMAC_MD5_H */
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright (c) 2006-2013, 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
|
||||||
|
|
||||||
|
#define BOOST_FILESYSTEM_VERSION 3
|
||||||
|
#define ENABLE_RELEASE_LOGGING
|
||||||
|
|
||||||
|
#include "misc_log_ex.h"
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,272 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 <list>
|
||||||
|
#include <numeric>
|
||||||
|
#include <boost/timer.hpp>
|
||||||
|
#include <boost/uuid/uuid.hpp>
|
||||||
|
#include <boost/uuid/random_generator.hpp>
|
||||||
|
|
||||||
|
#include "misc_os_dependent.h"
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace math_helper
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename val, int default_base>
|
||||||
|
class average
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
average()
|
||||||
|
{
|
||||||
|
m_base = default_base;
|
||||||
|
m_last_avg_val = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool set_base()
|
||||||
|
{
|
||||||
|
CRITICAL_REGION_LOCAL(m_lock);
|
||||||
|
|
||||||
|
m_base = default_base;
|
||||||
|
if(m_list.size() > m_base)
|
||||||
|
m_list.resize(m_base);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef val value_type;
|
||||||
|
|
||||||
|
void push(const value_type& vl)
|
||||||
|
{
|
||||||
|
CRITICAL_REGION_LOCAL(m_lock);
|
||||||
|
|
||||||
|
//#ifndef DEBUG_STUB
|
||||||
|
m_list.push_back(vl);
|
||||||
|
if(m_list.size() > m_base )
|
||||||
|
m_list.pop_front();
|
||||||
|
//#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
double update(const value_type& vl)
|
||||||
|
{
|
||||||
|
CRITICAL_REGION_LOCAL(m_lock);
|
||||||
|
//#ifndef DEBUG_STUB
|
||||||
|
push(vl);
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
return get_avg();
|
||||||
|
}
|
||||||
|
|
||||||
|
double get_avg()
|
||||||
|
{
|
||||||
|
CRITICAL_REGION_LOCAL(m_lock);
|
||||||
|
|
||||||
|
value_type vl = std::accumulate(m_list.begin(), m_list.end(), value_type(0));
|
||||||
|
if(m_list.size())
|
||||||
|
return m_last_avg_val = (double)(vl/m_list.size());
|
||||||
|
|
||||||
|
return m_last_avg_val = (double)vl;
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type get_last_val()
|
||||||
|
{
|
||||||
|
CRITICAL_REGION_LOCAL(m_lock);
|
||||||
|
if(m_list.size())
|
||||||
|
return m_list.back();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned int m_base;
|
||||||
|
double m_last_avg_val;
|
||||||
|
std::list<value_type> m_list;
|
||||||
|
critical_section m_lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef WINDOWS_PLATFORM
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
class timing_guard_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~timing_guard_base(){};
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class timing_guard: public timing_guard_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
timing_guard(T& avrg):m_avrg(avrg)
|
||||||
|
{
|
||||||
|
m_start_ticks = ::GetTickCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
~timing_guard()
|
||||||
|
{
|
||||||
|
m_avrg.push(::GetTickCount()-m_start_ticks);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T& m_avrg;
|
||||||
|
DWORD m_start_ticks;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class t_timing>
|
||||||
|
timing_guard_base* create_timing_guard(t_timing& timing){return new timing_guard<t_timing>(timing);}
|
||||||
|
|
||||||
|
#define BEGIN_TIMING_ZONE(timing_var) { boost::shared_ptr<math_helper::timing_guard_base> local_timing_guard_ptr(math_helper::create_timing_guard(timing_var));
|
||||||
|
#define END_TIMING_ZONE() }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//#ifdef WINDOWS_PLATFORM_EX
|
||||||
|
template<boost::uint64_t default_time_window>
|
||||||
|
class speed
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
speed()
|
||||||
|
{
|
||||||
|
m_time_window = default_time_window;
|
||||||
|
m_last_speed_value = 0;
|
||||||
|
}
|
||||||
|
bool chick()
|
||||||
|
{
|
||||||
|
#ifndef DEBUG_STUB
|
||||||
|
boost::uint64_t ticks = misc_utils::get_tick_count();
|
||||||
|
CRITICAL_REGION_BEGIN(m_lock);
|
||||||
|
m_chicks.push_back(ticks);
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
//flush(ticks);
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool chick(size_t count)
|
||||||
|
{
|
||||||
|
for(size_t s = 0; s != count; s++)
|
||||||
|
chick();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t get_speed()
|
||||||
|
{
|
||||||
|
flush(misc_utils::get_tick_count());
|
||||||
|
return m_last_speed_value = m_chicks.size();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
|
||||||
|
bool flush(boost::uint64_t ticks)
|
||||||
|
{
|
||||||
|
CRITICAL_REGION_BEGIN(m_lock);
|
||||||
|
std::list<boost::uint64_t>::iterator it = m_chicks.begin();
|
||||||
|
while(it != m_chicks.end())
|
||||||
|
{
|
||||||
|
if(*it + m_time_window < ticks)
|
||||||
|
m_chicks.erase(it++);
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<boost::uint64_t> m_chicks;
|
||||||
|
boost::uint64_t m_time_window;
|
||||||
|
size_t m_last_speed_value;
|
||||||
|
critical_section m_lock;
|
||||||
|
};
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
template<class tlist>
|
||||||
|
void randomize_list(tlist& t_list)
|
||||||
|
{
|
||||||
|
for(typename tlist::iterator it = t_list.begin();it!=t_list.end();it++)
|
||||||
|
{
|
||||||
|
size_t offset = rand()%t_list.size();
|
||||||
|
typename tlist::iterator it_2 = t_list.begin();
|
||||||
|
for(size_t local_offset = 0;local_offset!=offset;local_offset++)
|
||||||
|
it_2++;
|
||||||
|
if(it_2 == it)
|
||||||
|
continue;
|
||||||
|
std::swap(*it_2, *it);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
PRAGMA_WARNING_PUSH
|
||||||
|
PRAGMA_GCC("GCC diagnostic ignored \"-Wstrict-aliasing\"")
|
||||||
|
inline
|
||||||
|
uint64_t generated_random_uint64()
|
||||||
|
{
|
||||||
|
boost::uuids::uuid id___ = boost::uuids::random_generator()();
|
||||||
|
return *reinterpret_cast<uint64_t*>(&id___.data[0]); //(*reinterpret_cast<uint64_t*>(&id___.data[0]) ^ *reinterpret_cast<uint64_t*>(&id___.data[8]));
|
||||||
|
}
|
||||||
|
PRAGMA_WARNING_POP
|
||||||
|
template<int default_interval, bool start_immediate = true>
|
||||||
|
class once_a_time_seconds
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
once_a_time_seconds():m_interval(default_interval)
|
||||||
|
{
|
||||||
|
m_last_worked_time = 0;
|
||||||
|
if(!start_immediate)
|
||||||
|
time(&m_last_worked_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class functor_t>
|
||||||
|
bool do_call(functor_t functr)
|
||||||
|
{
|
||||||
|
time_t current_time = 0;
|
||||||
|
time(¤t_time);
|
||||||
|
|
||||||
|
if(current_time - m_last_worked_time > m_interval)
|
||||||
|
{
|
||||||
|
bool res = functr();
|
||||||
|
time(&m_last_worked_time);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
time_t m_last_worked_time;
|
||||||
|
time_t m_interval;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* libEtPan! -- a mail stuff library
|
||||||
|
*
|
||||||
|
* Copyright (C) 2001, 2005 - DINH Viet Hoa
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. 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.
|
||||||
|
* 3. Neither the name of the libEtPan! project 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 AUTHORS 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 AUTHORS OR CONTRIBUTORS 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* $Id: md5.h,v 1.1.1.1 2005/03/18 20:17:27 zautrix Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* MD5.H - header file for MD5C.C
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
|
||||||
|
rights reserved.
|
||||||
|
|
||||||
|
License to copy and use this software is granted provided that it
|
||||||
|
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
|
||||||
|
Algorithm" in all material mentioning or referencing this software
|
||||||
|
or this function.
|
||||||
|
|
||||||
|
License is also granted to make and use derivative works provided
|
||||||
|
that such works are identified as "derived from the RSA Data
|
||||||
|
Security, Inc. MD5 Message-Digest Algorithm" in all material
|
||||||
|
mentioning or referencing the derived work.
|
||||||
|
|
||||||
|
RSA Data Security, Inc. makes no representations concerning either
|
||||||
|
the merchantability of this software or the suitability of this
|
||||||
|
software for any particular purpose. It is provided "as is"
|
||||||
|
without express or implied warranty of any kind.
|
||||||
|
These notices must be retained in any copies of any part of this
|
||||||
|
documentation and/or software.
|
||||||
|
*/
|
||||||
|
#ifndef MD5_H
|
||||||
|
#define MD5_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "md5global.h"
|
||||||
|
|
||||||
|
namespace md5
|
||||||
|
{
|
||||||
|
/* MD5 context. */
|
||||||
|
typedef struct {
|
||||||
|
UINT4 state[4]; /* state (ABCD) */
|
||||||
|
UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
|
||||||
|
unsigned char buffer[64]; /* input buffer */
|
||||||
|
} MD5_CTX;
|
||||||
|
|
||||||
|
static void MD5Init(MD5_CTX * context);
|
||||||
|
static void MD5Update( MD5_CTX *context, const unsigned char *input, unsigned int inputLen );
|
||||||
|
static void MD5Final ( unsigned char digest[16], MD5_CTX *context );
|
||||||
|
static void hmac_md5(const unsigned char* text, int text_len, const unsigned char* key, int key_len, unsigned char *digest);
|
||||||
|
|
||||||
|
|
||||||
|
inline bool md5( unsigned char *input, int ilen, unsigned char output[16] )
|
||||||
|
{
|
||||||
|
MD5_CTX ctx;
|
||||||
|
|
||||||
|
MD5Init( &ctx );
|
||||||
|
MD5Update( &ctx, input, ilen );
|
||||||
|
MD5Final( output, &ctx);
|
||||||
|
|
||||||
|
memset( &ctx, 0, sizeof( MD5_CTX) );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "md5_l.inl"
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,563 @@
|
||||||
|
/*
|
||||||
|
* libEtPan! -- a mail stuff library
|
||||||
|
*
|
||||||
|
* Copyright (C) 2001, 2005 - DINH Viet Hoa
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. 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.
|
||||||
|
* 3. Neither the name of the libEtPan! project 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 AUTHORS 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 AUTHORS OR CONTRIBUTORS 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* $Id: md5.c,v 1.1.1.1 2005/03/18 20:17:27 zautrix Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
|
||||||
|
rights reserved.
|
||||||
|
|
||||||
|
License to copy and use this software is granted provided that it
|
||||||
|
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
|
||||||
|
Algorithm" in all material mentioning or referencing this software
|
||||||
|
or this function.
|
||||||
|
|
||||||
|
License is also granted to make and use derivative works provided
|
||||||
|
that such works are identified as "derived from the RSA Data
|
||||||
|
Security, Inc. MD5 Message-Digest Algorithm" in all material
|
||||||
|
mentioning or referencing the derived work.
|
||||||
|
|
||||||
|
RSA Data Security, Inc. makes no representations concerning either
|
||||||
|
the merchantability of this software or the suitability of this
|
||||||
|
software for any particular purpose. It is provided "as is"
|
||||||
|
without express or implied warranty of any kind.
|
||||||
|
|
||||||
|
These notices must be retained in any copies of any part of this
|
||||||
|
documentation and/or software.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* do i need all of this just for htonl()? damn. */
|
||||||
|
//#include <sys/types.h>
|
||||||
|
//#include <sys/param.h>
|
||||||
|
//#include <sys/socket.h>
|
||||||
|
//#include <netinet/in.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "md5global.h"
|
||||||
|
#include "md5_l.h"
|
||||||
|
#include "hmac-md5.h"
|
||||||
|
|
||||||
|
namespace md5
|
||||||
|
{
|
||||||
|
/* Constants for MD5Transform routine.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define S11 7
|
||||||
|
#define S12 12
|
||||||
|
#define S13 17
|
||||||
|
#define S14 22
|
||||||
|
#define S21 5
|
||||||
|
#define S22 9
|
||||||
|
#define S23 14
|
||||||
|
#define S24 20
|
||||||
|
#define S31 4
|
||||||
|
#define S32 11
|
||||||
|
#define S33 16
|
||||||
|
#define S34 23
|
||||||
|
#define S41 6
|
||||||
|
#define S42 10
|
||||||
|
#define S43 15
|
||||||
|
#define S44 21
|
||||||
|
|
||||||
|
/*
|
||||||
|
static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64]));
|
||||||
|
static void Encode PROTO_LIST
|
||||||
|
((unsigned char *, UINT4 *, unsigned int));
|
||||||
|
static void Decode PROTO_LIST
|
||||||
|
((UINT4 *, unsigned char *, unsigned int));
|
||||||
|
static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int));
|
||||||
|
static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int));
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void MD5_memcpy (POINTER output, POINTER input, unsigned int len)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
output[i] = input[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note: Replace "for loop" with standard memset if possible.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void MD5_memset (POINTER output, int value, unsigned int len)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
((char *)output)[i] = (char)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void MD5Transform (UINT4 state[4], unsigned char block[64]);
|
||||||
|
|
||||||
|
static unsigned char* PADDING()
|
||||||
|
{
|
||||||
|
static unsigned char local_PADDING[64] = {
|
||||||
|
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
return local_PADDING;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* F, G, H and I are basic MD5 functions.
|
||||||
|
|
||||||
|
*/
|
||||||
|
#ifdef I
|
||||||
|
/* This might be defined via NANA */
|
||||||
|
#undef I
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MD5_M_F(x, y, z) (((x) & (y)) | ((~x) & (z)))
|
||||||
|
#define MD5_M_G(x, y, z) (((x) & (z)) | ((y) & (~z)))
|
||||||
|
#define MD5_M_H(x, y, z) ((x) ^ (y) ^ (z))
|
||||||
|
#define MD5_M_I(x, y, z) ((y) ^ ((x) | (~z)))
|
||||||
|
|
||||||
|
/* ROTATE_LEFT rotates x left n bits.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
|
||||||
|
|
||||||
|
/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
|
||||||
|
Rotation is separate from addition to prevent recomputation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define FF(a, b, c, d, x, s, ac) { (a) += MD5_M_F ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); }
|
||||||
|
#define GG(a, b, c, d, x, s, ac) { (a) += MD5_M_G ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); }
|
||||||
|
#define HH(a, b, c, d, x, s, ac) { (a) += MD5_M_H ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); }
|
||||||
|
#define II(a, b, c, d, x, s, ac) { (a) += MD5_M_I ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); }
|
||||||
|
|
||||||
|
/* MD5 initialization. Begins an MD5 operation, writing a new context.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void MD5Init(MD5_CTX * context)
|
||||||
|
{
|
||||||
|
context->count[0] = context->count[1] = 0;
|
||||||
|
|
||||||
|
/* Load magic initialization constants.
|
||||||
|
|
||||||
|
*/
|
||||||
|
context->state[0] = 0x67452301;
|
||||||
|
context->state[1] = 0xefcdab89;
|
||||||
|
context->state[2] = 0x98badcfe;
|
||||||
|
context->state[3] = 0x10325476;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MD5 block update operation. Continues an MD5 message-digest
|
||||||
|
operation, processing another message block, and updating the context.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void MD5Update( MD5_CTX *context, const unsigned char *input, unsigned int inputLen )
|
||||||
|
{
|
||||||
|
unsigned int i, index, partLen;
|
||||||
|
|
||||||
|
/* Compute number of bytes mod 64 */
|
||||||
|
index = (unsigned int)((context->count[0] >> 3) & 0x3F);
|
||||||
|
|
||||||
|
/* Update number of bits */
|
||||||
|
if ((context->count[0] += ((UINT4)inputLen << 3))
|
||||||
|
< ((UINT4)inputLen << 3))
|
||||||
|
context->count[1]++;
|
||||||
|
context->count[1] += ((UINT4)inputLen >> 29);
|
||||||
|
|
||||||
|
partLen = 64 - index;
|
||||||
|
|
||||||
|
/* Transform as many times as possible.
|
||||||
|
|
||||||
|
*/
|
||||||
|
if (inputLen >= partLen)
|
||||||
|
{
|
||||||
|
MD5_memcpy( (POINTER)&context->buffer[index], (POINTER)input, partLen );
|
||||||
|
MD5Transform( context->state, context->buffer );
|
||||||
|
|
||||||
|
for (i = partLen; i + 63 < inputLen; i += 64)
|
||||||
|
MD5Transform (context->state, (unsigned char*)&input[i]);
|
||||||
|
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
i = 0;
|
||||||
|
|
||||||
|
/* Buffer remaining input */
|
||||||
|
MD5_memcpy( (POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encodes input (UINT4) into output (unsigned char). Assumes len is
|
||||||
|
a multiple of 4.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void Encode (unsigned char *output, UINT4 *input, unsigned int len)
|
||||||
|
{
|
||||||
|
unsigned int i, j;
|
||||||
|
|
||||||
|
for (i = 0, j = 0; j < len; i++, j += 4) {
|
||||||
|
output[j] = (unsigned char)(input[i] & 0xff);
|
||||||
|
output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
|
||||||
|
output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
|
||||||
|
output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decodes input (unsigned char) into output (UINT4). Assumes len is
|
||||||
|
a multiple of 4.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void Decode (UINT4 *output, unsigned char *input, unsigned int len)
|
||||||
|
{
|
||||||
|
unsigned int i, j;
|
||||||
|
|
||||||
|
for (i = 0, j = 0; j < len; i++, j += 4)
|
||||||
|
output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | (((UINT4)input[j+2]) << 16)
|
||||||
|
| (((UINT4)input[j+3]) << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MD5 finalization. Ends an MD5 message-digest operation, writing the
|
||||||
|
the message digest and zeroizing the context.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void MD5Final ( unsigned char digest[16], MD5_CTX *context )
|
||||||
|
{
|
||||||
|
unsigned char bits[8];
|
||||||
|
unsigned int index, padLen;
|
||||||
|
|
||||||
|
/* Save number of bits */
|
||||||
|
Encode (bits, context->count, 8);
|
||||||
|
|
||||||
|
/* Pad out to 56 mod 64.
|
||||||
|
|
||||||
|
*/
|
||||||
|
index = (unsigned int)((context->count[0] >> 3) & 0x3f);
|
||||||
|
padLen = (index < 56) ? (56 - index) : (120 - index);
|
||||||
|
MD5Update (context, PADDING(), padLen);
|
||||||
|
|
||||||
|
/* Append length (before padding) */
|
||||||
|
MD5Update (context, bits, 8);
|
||||||
|
|
||||||
|
/* Store state in digest */
|
||||||
|
Encode (digest, context->state, 16);
|
||||||
|
|
||||||
|
/* Zeroize sensitive information.
|
||||||
|
|
||||||
|
*/
|
||||||
|
MD5_memset ((POINTER)context, 0, sizeof (*context));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MD5 basic transformation. Transforms state based on block.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void MD5Transform (UINT4 state[4], unsigned char block[64])
|
||||||
|
{
|
||||||
|
UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
|
||||||
|
|
||||||
|
Decode (x, block, 64);
|
||||||
|
|
||||||
|
/* Round 1 */
|
||||||
|
FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
|
||||||
|
FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
|
||||||
|
FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
|
||||||
|
FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
|
||||||
|
FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
|
||||||
|
FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
|
||||||
|
FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
|
||||||
|
FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
|
||||||
|
FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
|
||||||
|
FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
|
||||||
|
FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
|
||||||
|
FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
|
||||||
|
FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
|
||||||
|
FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
|
||||||
|
FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
|
||||||
|
FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
|
||||||
|
|
||||||
|
/* Round 2 */
|
||||||
|
GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
|
||||||
|
GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
|
||||||
|
GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
|
||||||
|
GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
|
||||||
|
GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
|
||||||
|
GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
|
||||||
|
GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
|
||||||
|
GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
|
||||||
|
GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
|
||||||
|
GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
|
||||||
|
GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
|
||||||
|
GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
|
||||||
|
GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
|
||||||
|
GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
|
||||||
|
GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
|
||||||
|
GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
|
||||||
|
|
||||||
|
/* Round 3 */
|
||||||
|
HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
|
||||||
|
HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
|
||||||
|
HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
|
||||||
|
HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
|
||||||
|
HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
|
||||||
|
HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
|
||||||
|
HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
|
||||||
|
HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
|
||||||
|
HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
|
||||||
|
HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
|
||||||
|
HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
|
||||||
|
HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
|
||||||
|
HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
|
||||||
|
HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
|
||||||
|
HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
|
||||||
|
HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
|
||||||
|
|
||||||
|
/* Round 4 */
|
||||||
|
II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
|
||||||
|
II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
|
||||||
|
II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
|
||||||
|
II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
|
||||||
|
II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
|
||||||
|
II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
|
||||||
|
II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
|
||||||
|
II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
|
||||||
|
II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
|
||||||
|
II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
|
||||||
|
II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
|
||||||
|
II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
|
||||||
|
II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
|
||||||
|
II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
|
||||||
|
II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
|
||||||
|
II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
|
||||||
|
|
||||||
|
state[0] += a;
|
||||||
|
state[1] += b;
|
||||||
|
state[2] += c;
|
||||||
|
state[3] += d;
|
||||||
|
|
||||||
|
/* Zeroize sensitive information.
|
||||||
|
*/
|
||||||
|
MD5_memset ((POINTER)x, 0, sizeof (x));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note: Replace "for loop" with standard memcpy if possible.
|
||||||
|
|
||||||
|
*/
|
||||||
|
inline
|
||||||
|
void hmac_md5_init(HMAC_MD5_CTX *hmac,
|
||||||
|
const unsigned char *key,
|
||||||
|
int key_len)
|
||||||
|
{
|
||||||
|
unsigned char k_ipad[65]; /* inner padding -
|
||||||
|
* key XORd with ipad
|
||||||
|
*/
|
||||||
|
unsigned char k_opad[65]; /* outer padding -
|
||||||
|
* key XORd with opad
|
||||||
|
*/
|
||||||
|
unsigned char tk[16];
|
||||||
|
int i;
|
||||||
|
/* if key is longer than 64 bytes reset it to key=MD5(key) */
|
||||||
|
if (key_len > 64) {
|
||||||
|
|
||||||
|
MD5_CTX tctx;
|
||||||
|
|
||||||
|
MD5Init(&tctx);
|
||||||
|
MD5Update(&tctx, key, key_len);
|
||||||
|
MD5Final(tk, &tctx);
|
||||||
|
|
||||||
|
key = tk;
|
||||||
|
key_len = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* the HMAC_MD5 transform looks like:
|
||||||
|
*
|
||||||
|
* MD5(K XOR opad, MD5(K XOR ipad, text))
|
||||||
|
*
|
||||||
|
* where K is an n byte key
|
||||||
|
* ipad is the byte 0x36 repeated 64 times
|
||||||
|
* opad is the byte 0x5c repeated 64 times
|
||||||
|
* and text is the data being protected
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* start out by storing key in pads */
|
||||||
|
MD5_memset(k_ipad, '\0', sizeof k_ipad);
|
||||||
|
MD5_memset(k_opad, '\0', sizeof k_opad);
|
||||||
|
MD5_memcpy( k_ipad, (POINTER)key, key_len);
|
||||||
|
MD5_memcpy( k_opad, (POINTER)key, key_len);
|
||||||
|
|
||||||
|
/* XOR key with ipad and opad values */
|
||||||
|
for (i=0; i<64; i++) {
|
||||||
|
k_ipad[i] ^= 0x36;
|
||||||
|
k_opad[i] ^= 0x5c;
|
||||||
|
}
|
||||||
|
|
||||||
|
MD5Init(&hmac->ictx); /* init inner context */
|
||||||
|
MD5Update(&hmac->ictx, k_ipad, 64); /* apply inner pad */
|
||||||
|
|
||||||
|
MD5Init(&hmac->octx); /* init outer context */
|
||||||
|
MD5Update(&hmac->octx, k_opad, 64); /* apply outer pad */
|
||||||
|
|
||||||
|
/* scrub the pads and key context (if used) */
|
||||||
|
MD5_memset( (POINTER)&k_ipad, 0, sizeof(k_ipad));
|
||||||
|
MD5_memset( (POINTER)&k_opad, 0, sizeof(k_opad));
|
||||||
|
MD5_memset( (POINTER)&tk, 0, sizeof(tk));
|
||||||
|
|
||||||
|
/* and we're done. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The precalc and import routines here rely on the fact that we pad
|
||||||
|
* the key out to 64 bytes and use that to initialize the md5
|
||||||
|
* contexts, and that updating an md5 context with 64 bytes of data
|
||||||
|
* leaves nothing left over; all of the interesting state is contained
|
||||||
|
* in the state field, and none of it is left over in the count and
|
||||||
|
* buffer fields. So all we have to do is save the state field; we
|
||||||
|
* can zero the others when we reload it. Which is why the decision
|
||||||
|
* was made to pad the key out to 64 bytes in the first place. */
|
||||||
|
inline
|
||||||
|
void hmac_md5_precalc(HMAC_MD5_STATE *state,
|
||||||
|
const unsigned char *key,
|
||||||
|
int key_len)
|
||||||
|
{
|
||||||
|
HMAC_MD5_CTX hmac;
|
||||||
|
unsigned lupe;
|
||||||
|
|
||||||
|
hmac_md5_init(&hmac, key, key_len);
|
||||||
|
for (lupe = 0; lupe < 4; lupe++) {
|
||||||
|
state->istate[lupe] = htonl(hmac.ictx.state[lupe]);
|
||||||
|
state->ostate[lupe] = htonl(hmac.octx.state[lupe]);
|
||||||
|
}
|
||||||
|
MD5_memset( (POINTER)&hmac, 0, sizeof(hmac));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline
|
||||||
|
void hmac_md5_import(HMAC_MD5_CTX *hmac,
|
||||||
|
HMAC_MD5_STATE *state)
|
||||||
|
{
|
||||||
|
unsigned lupe;
|
||||||
|
MD5_memset( (POINTER)hmac, 0, sizeof(HMAC_MD5_CTX));
|
||||||
|
for (lupe = 0; lupe < 4; lupe++) {
|
||||||
|
hmac->ictx.state[lupe] = ntohl(state->istate[lupe]);
|
||||||
|
hmac->octx.state[lupe] = ntohl(state->ostate[lupe]);
|
||||||
|
}
|
||||||
|
/* Init the counts to account for our having applied
|
||||||
|
* 64 bytes of key; this works out to 0x200 (64 << 3; see
|
||||||
|
* MD5Update above...) */
|
||||||
|
hmac->ictx.count[0] = hmac->octx.count[0] = 0x200;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void hmac_md5_final(unsigned char digest[HMAC_MD5_SIZE],
|
||||||
|
HMAC_MD5_CTX *hmac)
|
||||||
|
{
|
||||||
|
MD5Final(digest, &hmac->ictx); /* Finalize inner md5 */
|
||||||
|
MD5Update(&hmac->octx, digest, 16); /* Update outer ctx */
|
||||||
|
MD5Final(digest, &hmac->octx); /* Finalize outer md5 */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void hmac_md5(const unsigned char* text, int text_len, const unsigned char* key, int key_len, unsigned char *digest)
|
||||||
|
{
|
||||||
|
MD5_CTX context;
|
||||||
|
|
||||||
|
unsigned char k_ipad[65]; /* inner padding -
|
||||||
|
* key XORd with ipad
|
||||||
|
*/
|
||||||
|
unsigned char k_opad[65]; /* outer padding -
|
||||||
|
* key XORd with opad
|
||||||
|
*/
|
||||||
|
unsigned char tk[16];
|
||||||
|
int i;
|
||||||
|
/* if key is longer than 64 bytes reset it to key=MD5(key) */
|
||||||
|
if (key_len > 64) {
|
||||||
|
|
||||||
|
MD5_CTX tctx;
|
||||||
|
|
||||||
|
MD5Init(&tctx);
|
||||||
|
MD5Update(&tctx, key, key_len);
|
||||||
|
MD5Final(tk, &tctx);
|
||||||
|
|
||||||
|
key = tk;
|
||||||
|
key_len = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* the HMAC_MD5 transform looks like:
|
||||||
|
*
|
||||||
|
* MD5(K XOR opad, MD5(K XOR ipad, text))
|
||||||
|
*
|
||||||
|
* where K is an n byte key
|
||||||
|
* ipad is the byte 0x36 repeated 64 times
|
||||||
|
* opad is the byte 0x5c repeated 64 times
|
||||||
|
* and text is the data being protected
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* start out by storing key in pads */
|
||||||
|
MD5_memset(k_ipad, '\0', sizeof k_ipad);
|
||||||
|
MD5_memset(k_opad, '\0', sizeof k_opad);
|
||||||
|
MD5_memcpy( k_ipad, (POINTER)key, key_len);
|
||||||
|
MD5_memcpy( k_opad, (POINTER)key, key_len);
|
||||||
|
|
||||||
|
/* XOR key with ipad and opad values */
|
||||||
|
for (i=0; i<64; i++) {
|
||||||
|
k_ipad[i] ^= 0x36;
|
||||||
|
k_opad[i] ^= 0x5c;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* perform inner MD5
|
||||||
|
*/
|
||||||
|
|
||||||
|
MD5Init(&context); /* init context for 1st
|
||||||
|
* pass */
|
||||||
|
MD5Update(&context, k_ipad, 64); /* start with inner pad */
|
||||||
|
MD5Update(&context, text, text_len); /* then text of datagram */
|
||||||
|
MD5Final(digest, &context); /* finish up 1st pass */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* perform outer MD5
|
||||||
|
*/
|
||||||
|
MD5Init(&context); /* init context for 2nd
|
||||||
|
* pass */
|
||||||
|
MD5Update(&context, k_opad, 64); /* start with outer pad */
|
||||||
|
MD5Update(&context, digest, 16); /* then results of 1st
|
||||||
|
* hash */
|
||||||
|
MD5Final(digest, &context); /* finish up 2nd pass */
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* libEtPan! -- a mail stuff library
|
||||||
|
*
|
||||||
|
* Copyright (C) 2001, 2005 - DINH Viet Hoa
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. 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.
|
||||||
|
* 3. Neither the name of the libEtPan! project 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 AUTHORS 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 AUTHORS OR CONTRIBUTORS 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* $Id: md5global.h,v 1.1.1.1 2005/03/18 20:17:28 zautrix Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* GLOBAL.H - RSAREF types and constants
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MD5GLOBAL_H
|
||||||
|
#define MD5GLOBAL_H
|
||||||
|
|
||||||
|
namespace md5
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/* PROTOTYPES should be set to one if and only if the compiler supports
|
||||||
|
function argument prototyping.
|
||||||
|
The following makes PROTOTYPES default to 0 if it has not already
|
||||||
|
been defined with C compiler flags.
|
||||||
|
*/
|
||||||
|
#ifndef PROTOTYPES
|
||||||
|
#define PROTOTYPES 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* POINTER defines a generic pointer type */
|
||||||
|
typedef unsigned char *POINTER;
|
||||||
|
|
||||||
|
/* UINT2 defines a two byte word */
|
||||||
|
typedef unsigned short int UINT2;
|
||||||
|
|
||||||
|
/* UINT4 defines a four byte word */
|
||||||
|
//typedef unsigned long int UINT4;
|
||||||
|
typedef unsigned int UINT4;
|
||||||
|
|
||||||
|
/* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
|
||||||
|
If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it
|
||||||
|
returns an empty list.
|
||||||
|
*/
|
||||||
|
#if PROTOTYPES
|
||||||
|
#define PROTO_LIST(list) list
|
||||||
|
#else
|
||||||
|
#define PROTO_LIST(list) ()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,162 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 <limits>
|
||||||
|
#include <boost/thread.hpp>
|
||||||
|
#include <boost/utility/value_init.hpp>
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
#define STD_TRY_BEGIN() try {
|
||||||
|
|
||||||
|
#define STD_TRY_CATCH(where_, ret_val) \
|
||||||
|
} \
|
||||||
|
catch (const std::exception &e) \
|
||||||
|
{ \
|
||||||
|
LOG_ERROR("EXCEPTION: " << where_ << ", mes: "<< e.what()); \
|
||||||
|
return ret_val; \
|
||||||
|
} \
|
||||||
|
catch (...) \
|
||||||
|
{ \
|
||||||
|
LOG_ERROR("EXCEPTION: " << where_ ); \
|
||||||
|
return ret_val; \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define AUTO_VAL_INIT(v) boost::value_initialized<decltype(v)>()
|
||||||
|
|
||||||
|
namespace misc_utils
|
||||||
|
{
|
||||||
|
template<typename t_type>
|
||||||
|
t_type get_max_t_val(t_type t)
|
||||||
|
{
|
||||||
|
return (std::numeric_limits<t_type>::max)();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename t_iterator>
|
||||||
|
t_iterator move_it_forward(t_iterator it, size_t count)
|
||||||
|
{
|
||||||
|
while(count--)
|
||||||
|
it++;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename t_iterator>
|
||||||
|
t_iterator move_it_backward(t_iterator it, size_t count)
|
||||||
|
{
|
||||||
|
while(count--)
|
||||||
|
it--;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TEMPLATE STRUCT less
|
||||||
|
template<class _Ty>
|
||||||
|
struct less_as_pod
|
||||||
|
: public std::binary_function<_Ty, _Ty, bool>
|
||||||
|
{ // functor for operator<
|
||||||
|
bool operator()(const _Ty& _Left, const _Ty& _Right) const
|
||||||
|
{ // apply operator< to operands
|
||||||
|
return memcmp(&_Left, &_Right, sizeof(_Left)) < 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class _Ty>
|
||||||
|
bool is_less_as_pod(const _Ty& _Left, const _Ty& _Right)
|
||||||
|
{ // apply operator< to operands
|
||||||
|
return memcmp(&_Left, &_Right, sizeof(_Left)) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool sleep_no_w(long ms )
|
||||||
|
{
|
||||||
|
boost::this_thread::sleep(
|
||||||
|
boost::get_system_time() +
|
||||||
|
boost::posix_time::milliseconds( std::max<long>(ms,0) ) );
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class type_vec_type>
|
||||||
|
type_vec_type median(std::vector<type_vec_type> &v)
|
||||||
|
{
|
||||||
|
if(v.empty())
|
||||||
|
return boost::value_initialized<type_vec_type>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
|
||||||
|
struct call_befor_die_base
|
||||||
|
{
|
||||||
|
virtual ~call_befor_die_base(){}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef boost::shared_ptr<call_befor_die_base> auto_scope_leave_caller;
|
||||||
|
|
||||||
|
|
||||||
|
template<class t_scope_leave_handler>
|
||||||
|
struct call_befor_die: public call_befor_die_base
|
||||||
|
{
|
||||||
|
t_scope_leave_handler m_func;
|
||||||
|
call_befor_die(t_scope_leave_handler f):m_func(f)
|
||||||
|
{}
|
||||||
|
~call_befor_die()
|
||||||
|
{
|
||||||
|
m_func();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class t_scope_leave_handler>
|
||||||
|
auto_scope_leave_caller create_scope_leave_handler(t_scope_leave_handler f)
|
||||||
|
{
|
||||||
|
auto_scope_leave_caller slc(new call_befor_die<t_scope_leave_handler>(f));
|
||||||
|
return slc;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,108 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
#ifdef WIN32
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//#ifdef _WIN32_WINNT
|
||||||
|
// #undef _WIN32_WINNT
|
||||||
|
// #define _WIN32_WINNT 0x0600
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __MACH__
|
||||||
|
#include <mach/clock.h>
|
||||||
|
#include <mach/mach.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace misc_utils
|
||||||
|
{
|
||||||
|
|
||||||
|
inline boost::uint64_t get_tick_count()
|
||||||
|
{
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
return ::GetTickCount64();
|
||||||
|
#elif defined(__MACH__)
|
||||||
|
clock_serv_t cclock;
|
||||||
|
mach_timespec_t mts;
|
||||||
|
|
||||||
|
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
|
||||||
|
clock_get_time(cclock, &mts);
|
||||||
|
mach_port_deallocate(mach_task_self(), cclock);
|
||||||
|
|
||||||
|
return (mts.tv_sec * 1000) + (mts.tv_nsec/1000000);
|
||||||
|
#else
|
||||||
|
struct timespec ts;
|
||||||
|
if(clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (ts.tv_sec * 1000) + (ts.tv_nsec/1000000);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline int call_sys_cmd(const std::string& cmd)
|
||||||
|
{
|
||||||
|
std::cout << "# " << cmd << std::endl;
|
||||||
|
|
||||||
|
FILE * fp ;
|
||||||
|
//char tstCommand[] ="ls *";
|
||||||
|
char path[1000] = {0};
|
||||||
|
#if !defined(__GNUC__)
|
||||||
|
fp = _popen(cmd.c_str(), "r");
|
||||||
|
#else
|
||||||
|
fp = popen(cmd.c_str(), "r");
|
||||||
|
#endif
|
||||||
|
while ( fgets( path, 1000, fp ) != NULL )
|
||||||
|
std::cout << path;
|
||||||
|
|
||||||
|
#if !defined(__GNUC__)
|
||||||
|
_pclose(fp);
|
||||||
|
#else
|
||||||
|
pclose(fp);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline std::string get_thread_string_id()
|
||||||
|
{
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
return boost::lexical_cast<std::string>(GetCurrentThreadId());
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
return boost::lexical_cast<std::string>(pthread_self());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,316 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _ABSTRACT_TCP_SERVER_H_
|
||||||
|
#define _ABSTRACT_TCP_SERVER_H_
|
||||||
|
|
||||||
|
#include <process.h>
|
||||||
|
#include <list>
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include "winobj.h"
|
||||||
|
//#include "threads_helper.h"
|
||||||
|
#include "net_utils_base.h"
|
||||||
|
|
||||||
|
#pragma comment(lib, "Ws2_32.lib")
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
class soket_sender: public i_service_endpoint
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
soket_sender(SOCKET sock):m_sock(sock){}
|
||||||
|
private:
|
||||||
|
virtual bool handle_send(const void* ptr, size_t cb)
|
||||||
|
{
|
||||||
|
if(cb != send(m_sock, (char*)ptr, (int)cb, 0))
|
||||||
|
{
|
||||||
|
int sock_err = WSAGetLastError();
|
||||||
|
LOG_ERROR("soket_sender: Failed to send " << cb << " bytes, Error=" << sock_err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SOCKET m_sock;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
template<class THandler>
|
||||||
|
class abstract_tcp_server
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
abstract_tcp_server();
|
||||||
|
|
||||||
|
bool init_server(int port_no);
|
||||||
|
bool deinit_server();
|
||||||
|
bool run_server();
|
||||||
|
bool send_stop_signal();
|
||||||
|
|
||||||
|
typename THandler::config_type& get_config_object(){return m_config;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool invoke_connection(SOCKET hnew_sock, long ip_from, int post_from);
|
||||||
|
static unsigned __stdcall ConnectionHandlerProc(void* lpParameter);
|
||||||
|
|
||||||
|
class thread_context;
|
||||||
|
typedef std::list<thread_context> connections_container;
|
||||||
|
typedef typename connections_container::iterator connections_iterator;
|
||||||
|
|
||||||
|
struct thread_context
|
||||||
|
{
|
||||||
|
HANDLE m_htread;
|
||||||
|
SOCKET m_socket;
|
||||||
|
abstract_tcp_server* powner;
|
||||||
|
connection_context m_context;
|
||||||
|
typename connections_iterator m_self_it;
|
||||||
|
};
|
||||||
|
|
||||||
|
SOCKET m_listen_socket;
|
||||||
|
int m_port;
|
||||||
|
bool m_initialized;
|
||||||
|
volatile LONG m_stop_server;
|
||||||
|
volatile LONG m_threads_count;
|
||||||
|
typename THandler::config_type m_config;
|
||||||
|
connections_container m_connections;
|
||||||
|
critical_section m_connections_lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class THandler>
|
||||||
|
unsigned __stdcall abstract_tcp_server<THandler>::ConnectionHandlerProc(void* lpParameter)
|
||||||
|
{
|
||||||
|
|
||||||
|
thread_context* pthread_context = (thread_context*)lpParameter;
|
||||||
|
if(!pthread_context)
|
||||||
|
return 0;
|
||||||
|
abstract_tcp_server<THandler>* pthis = pthread_context->powner;
|
||||||
|
|
||||||
|
::InterlockedIncrement(&pthis->m_threads_count);
|
||||||
|
|
||||||
|
::CoInitialize(NULL);
|
||||||
|
|
||||||
|
|
||||||
|
LOG_PRINT("Handler thread STARTED with socket=" << pthread_context->m_socket, LOG_LEVEL_2);
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
|
soket_sender sndr(pthread_context->m_socket);
|
||||||
|
THandler srv(&sndr, pthread_context->powner->m_config, pthread_context->m_context);
|
||||||
|
|
||||||
|
|
||||||
|
srv.after_init_connection();
|
||||||
|
|
||||||
|
char buff[1000] = {0};
|
||||||
|
std::string ansver;
|
||||||
|
while ( (res = recv(pthread_context->m_socket, (char*)buff, 1000, 0)) > 0)
|
||||||
|
{
|
||||||
|
LOG_PRINT("Data in, " << res << " bytes", LOG_LEVEL_3);
|
||||||
|
if(!srv.handle_recv(buff, res))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
shutdown(pthread_context->m_socket, SD_BOTH);
|
||||||
|
closesocket(pthread_context->m_socket);
|
||||||
|
|
||||||
|
abstract_tcp_server* powner = pthread_context->powner;
|
||||||
|
LOG_PRINT("Handler thread with socket=" << pthread_context->m_socket << " STOPPED", LOG_LEVEL_2);
|
||||||
|
powner->m_connections_lock.lock();
|
||||||
|
::CloseHandle(pthread_context->m_htread);
|
||||||
|
pthread_context->powner->m_connections.erase(pthread_context->m_self_it);
|
||||||
|
powner->m_connections_lock.unlock();
|
||||||
|
CoUninitialize();
|
||||||
|
::InterlockedDecrement(&pthis->m_threads_count);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------
|
||||||
|
template<class THandler>
|
||||||
|
abstract_tcp_server<THandler>::abstract_tcp_server():m_listen_socket(INVALID_SOCKET),
|
||||||
|
m_initialized(false),
|
||||||
|
m_stop_server(0), m_port(0), m_threads_count(0)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------------
|
||||||
|
template<class THandler>
|
||||||
|
bool abstract_tcp_server<THandler>::init_server(int port_no)
|
||||||
|
{
|
||||||
|
m_port = port_no;
|
||||||
|
WSADATA wsad = {0};
|
||||||
|
int err = ::WSAStartup(MAKEWORD(2,2), &wsad);
|
||||||
|
if ( err != 0 || LOBYTE( wsad.wVersion ) != 2 || HIBYTE( wsad.wVersion ) != 2 )
|
||||||
|
{
|
||||||
|
LOG_ERROR("Could not find a usable WinSock DLL, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_initialized = true;
|
||||||
|
|
||||||
|
m_listen_socket = ::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
|
||||||
|
if(INVALID_SOCKET == m_listen_socket)
|
||||||
|
{
|
||||||
|
err = ::WSAGetLastError();
|
||||||
|
LOG_ERROR("Failed to create socket, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int opt = 1;
|
||||||
|
setsockopt (m_listen_socket, SOL_SOCKET,SO_REUSEADDR, reinterpret_cast<char*>(&opt), sizeof(int));
|
||||||
|
|
||||||
|
sockaddr_in adr = {0};
|
||||||
|
adr.sin_family = AF_INET;
|
||||||
|
adr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||||
|
adr.sin_port = (u_short)htons(port_no);
|
||||||
|
|
||||||
|
err = bind(m_listen_socket, (const sockaddr*)&adr, sizeof(adr ));
|
||||||
|
if(SOCKET_ERROR == err )
|
||||||
|
{
|
||||||
|
err = ::WSAGetLastError();
|
||||||
|
LOG_PRINT("Failed to Bind, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_2);
|
||||||
|
deinit_server();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
::InterlockedExchange(&m_stop_server, 0);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------
|
||||||
|
template<class THandler>
|
||||||
|
bool abstract_tcp_server<THandler>::deinit_server()
|
||||||
|
{
|
||||||
|
|
||||||
|
if(!m_initialized)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if(INVALID_SOCKET != m_listen_socket)
|
||||||
|
{
|
||||||
|
shutdown(m_listen_socket, SD_BOTH);
|
||||||
|
int res = closesocket(m_listen_socket);
|
||||||
|
if(SOCKET_ERROR == res)
|
||||||
|
{
|
||||||
|
int err = ::WSAGetLastError();
|
||||||
|
LOG_ERROR("Failed to closesocket(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
|
||||||
|
}
|
||||||
|
m_listen_socket = INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
int res = ::WSACleanup();
|
||||||
|
if(SOCKET_ERROR == res)
|
||||||
|
{
|
||||||
|
int err = ::WSAGetLastError();
|
||||||
|
LOG_ERROR("Failed to WSACleanup(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
|
||||||
|
}
|
||||||
|
m_initialized = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------
|
||||||
|
template<class THandler>
|
||||||
|
bool abstract_tcp_server<THandler>::send_stop_signal()
|
||||||
|
{
|
||||||
|
InterlockedExchange(&m_stop_server, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------
|
||||||
|
template<class THandler>
|
||||||
|
bool abstract_tcp_server<THandler>::run_server()
|
||||||
|
{
|
||||||
|
int err = listen(m_listen_socket, 10000);
|
||||||
|
if(SOCKET_ERROR == err )
|
||||||
|
{
|
||||||
|
err = ::WSAGetLastError();
|
||||||
|
LOG_ERROR("Failed to listen, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_PRINT("Listening port "<< m_port << "...." , LOG_LEVEL_2);
|
||||||
|
|
||||||
|
while(!m_stop_server)
|
||||||
|
{
|
||||||
|
sockaddr_in adr_from = {0};
|
||||||
|
int adr_len = sizeof(adr_from);
|
||||||
|
fd_set read_fs = {0};
|
||||||
|
read_fs.fd_count = 1;
|
||||||
|
read_fs.fd_array[0] = m_listen_socket;
|
||||||
|
TIMEVAL tv = {0};
|
||||||
|
tv.tv_usec = 100;
|
||||||
|
int select_res = select(0, &read_fs, NULL, NULL, &tv);
|
||||||
|
if(!select_res)
|
||||||
|
continue;
|
||||||
|
SOCKET new_sock = WSAAccept(m_listen_socket, (sockaddr *)&adr_from, &adr_len, NULL, NULL);
|
||||||
|
LOG_PRINT("Accepted connection on socket=" << new_sock, LOG_LEVEL_2);
|
||||||
|
invoke_connection(new_sock, adr_from.sin_addr.s_addr, adr_from.sin_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit_server();
|
||||||
|
|
||||||
|
#define ABSTR_TCP_SRV_WAIT_COUNT_MAX 5000
|
||||||
|
#define ABSTR_TCP_SRV_WAIT_COUNT_INTERVAL 1000
|
||||||
|
|
||||||
|
int wait_count = 0;
|
||||||
|
|
||||||
|
while(m_threads_count && wait_count*1000 < ABSTR_TCP_SRV_WAIT_COUNT_MAX)
|
||||||
|
{
|
||||||
|
::Sleep(ABSTR_TCP_SRV_WAIT_COUNT_INTERVAL);
|
||||||
|
wait_count++;
|
||||||
|
}
|
||||||
|
LOG_PRINT("abstract_tcp_server exit with wait count=" << wait_count*ABSTR_TCP_SRV_WAIT_COUNT_INTERVAL << "(max=" << ABSTR_TCP_SRV_WAIT_COUNT_MAX <<")", LOG_LEVEL_0);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------
|
||||||
|
template<class THandler>
|
||||||
|
bool abstract_tcp_server<THandler>::invoke_connection(SOCKET hnew_sock, long ip_from, int post_from)
|
||||||
|
{
|
||||||
|
m_connections_lock.lock();
|
||||||
|
m_connections.push_back(thread_context());
|
||||||
|
m_connections_lock.unlock();
|
||||||
|
m_connections.back().m_socket = hnew_sock;
|
||||||
|
m_connections.back().powner = this;
|
||||||
|
m_connections.back().m_self_it = --m_connections.end();
|
||||||
|
m_connections.back().m_context.m_remote_ip = ip_from;
|
||||||
|
m_connections.back().m_context.m_remote_port = post_from;
|
||||||
|
m_connections.back().m_htread = threads_helper::create_thread(ConnectionHandlerProc, &m_connections.back());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------------
|
||||||
|
//----------------------------------------------------------------------------------------
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif //_ABSTRACT_TCP_SERVER_H_
|
|
@ -0,0 +1,276 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _ABSTRACT_TCP_SERVER2_H_
|
||||||
|
#define _ABSTRACT_TCP_SERVER2_H_
|
||||||
|
|
||||||
|
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/array.hpp>
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
#include <boost/enable_shared_from_this.hpp>
|
||||||
|
#include <boost/interprocess/detail/atomic.hpp>
|
||||||
|
#include <boost/thread/thread.hpp>
|
||||||
|
#include "net_utils_base.h"
|
||||||
|
#include "syncobj.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define ABSTRACT_SERVER_SEND_QUE_MAX_COUNT 100
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
|
||||||
|
struct i_connection_filter
|
||||||
|
{
|
||||||
|
virtual bool is_remote_ip_allowed(boost::uint32_t adress)=0;
|
||||||
|
protected:
|
||||||
|
virtual ~i_connection_filter(){}
|
||||||
|
};
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
/// Represents a single connection from a client.
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
class connection
|
||||||
|
: public boost::enable_shared_from_this<connection<t_protocol_handler> >,
|
||||||
|
private boost::noncopyable,
|
||||||
|
public i_service_endpoint
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef typename t_protocol_handler::connection_context t_connection_context;
|
||||||
|
/// Construct a connection with the given io_service.
|
||||||
|
explicit connection(boost::asio::io_service& io_service,
|
||||||
|
typename t_protocol_handler::config_type& config, volatile boost::uint32_t& sock_count, i_connection_filter * &pfilter);
|
||||||
|
|
||||||
|
virtual ~connection();
|
||||||
|
/// Get the socket associated with the connection.
|
||||||
|
boost::asio::ip::tcp::socket& socket();
|
||||||
|
|
||||||
|
/// Start the first asynchronous operation for the connection.
|
||||||
|
bool start(bool is_income, bool is_multithreaded);
|
||||||
|
|
||||||
|
void get_context(t_connection_context& context_){context_ = context;}
|
||||||
|
|
||||||
|
void call_back_starter();
|
||||||
|
private:
|
||||||
|
//----------------- i_service_endpoint ---------------------
|
||||||
|
virtual bool do_send(const void* ptr, size_t cb);
|
||||||
|
virtual bool close();
|
||||||
|
virtual bool call_run_once_service_io();
|
||||||
|
virtual bool request_callback();
|
||||||
|
virtual boost::asio::io_service& get_io_service();
|
||||||
|
virtual bool add_ref();
|
||||||
|
virtual bool release();
|
||||||
|
//------------------------------------------------------
|
||||||
|
boost::shared_ptr<connection<t_protocol_handler> > safe_shared_from_this();
|
||||||
|
bool shutdown();
|
||||||
|
/// Handle completion of a read operation.
|
||||||
|
void handle_read(const boost::system::error_code& e,
|
||||||
|
std::size_t bytes_transferred);
|
||||||
|
|
||||||
|
/// Handle completion of a write operation.
|
||||||
|
void handle_write(const boost::system::error_code& e, size_t cb);
|
||||||
|
|
||||||
|
/// Strand to ensure the connection's handlers are not called concurrently.
|
||||||
|
boost::asio::io_service::strand strand_;
|
||||||
|
|
||||||
|
/// Socket for the connection.
|
||||||
|
boost::asio::ip::tcp::socket socket_;
|
||||||
|
|
||||||
|
/// Buffer for incoming data.
|
||||||
|
boost::array<char, 8192> buffer_;
|
||||||
|
|
||||||
|
t_connection_context context;
|
||||||
|
volatile boost::uint32_t m_want_close_connection;
|
||||||
|
std::atomic<bool> m_was_shutdown;
|
||||||
|
critical_section m_send_que_lock;
|
||||||
|
std::list<std::string> m_send_que;
|
||||||
|
volatile boost::uint32_t& m_ref_sockets_count;
|
||||||
|
i_connection_filter* &m_pfilter;
|
||||||
|
volatile bool m_is_multithreaded;
|
||||||
|
|
||||||
|
//this should be the last one, because it could be wait on destructor, while other activities possible on other threads
|
||||||
|
t_protocol_handler m_protocol_handler;
|
||||||
|
//typename t_protocol_handler::config_type m_dummy_config;
|
||||||
|
std::list<boost::shared_ptr<connection<t_protocol_handler> > > m_self_refs; // add_ref/release support
|
||||||
|
critical_section m_self_refs_lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
class boosted_tcp_server
|
||||||
|
: private boost::noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef boost::shared_ptr<connection<t_protocol_handler> > connection_ptr;
|
||||||
|
typedef typename t_protocol_handler::connection_context t_connection_context;
|
||||||
|
/// Construct the server to listen on the specified TCP address and port, and
|
||||||
|
/// serve up files from the given directory.
|
||||||
|
boosted_tcp_server();
|
||||||
|
explicit boosted_tcp_server(boost::asio::io_service& external_io_service);
|
||||||
|
~boosted_tcp_server();
|
||||||
|
|
||||||
|
bool init_server(uint32_t port, const std::string address = "0.0.0.0");
|
||||||
|
bool init_server(const std::string port, const std::string& address = "0.0.0.0");
|
||||||
|
|
||||||
|
/// Run the server's io_service loop.
|
||||||
|
bool run_server(size_t threads_count, bool wait = true);
|
||||||
|
|
||||||
|
/// wait for service workers stop
|
||||||
|
bool timed_wait_server_stop(boost::uint64_t wait_mseconds);
|
||||||
|
|
||||||
|
/// Stop the server.
|
||||||
|
void send_stop_signal();
|
||||||
|
|
||||||
|
bool is_stop_signal_sent();
|
||||||
|
|
||||||
|
void set_threads_prefix(const std::string& prefix_name);
|
||||||
|
|
||||||
|
bool deinit_server(){return true;}
|
||||||
|
|
||||||
|
size_t get_threads_count(){return m_threads_count;}
|
||||||
|
|
||||||
|
void set_connection_filter(i_connection_filter* pfilter);
|
||||||
|
|
||||||
|
bool connect(const std::string& adr, const std::string& port, boost::uint32_t conn_timeot, t_connection_context& cn, const std::string& bind_ip = "0.0.0.0");
|
||||||
|
template<class t_callback>
|
||||||
|
bool connect_async(const std::string& adr, const std::string& port, boost::uint32_t conn_timeot, t_callback cb, const std::string& bind_ip = "0.0.0.0");
|
||||||
|
|
||||||
|
typename t_protocol_handler::config_type& get_config_object(){return m_config;}
|
||||||
|
|
||||||
|
int get_binded_port(){return m_port;}
|
||||||
|
|
||||||
|
boost::asio::io_service& get_io_service(){return io_service_;}
|
||||||
|
|
||||||
|
struct idle_callback_conext_base
|
||||||
|
{
|
||||||
|
virtual ~idle_callback_conext_base(){}
|
||||||
|
|
||||||
|
virtual bool call_handler(){return true;}
|
||||||
|
|
||||||
|
idle_callback_conext_base(boost::asio::io_service& io_serice):
|
||||||
|
m_timer(io_serice)
|
||||||
|
{}
|
||||||
|
boost::asio::deadline_timer m_timer;
|
||||||
|
boost::uint64_t m_period;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class t_handler>
|
||||||
|
struct idle_callback_conext: public idle_callback_conext_base
|
||||||
|
{
|
||||||
|
idle_callback_conext(boost::asio::io_service& io_serice, t_handler& h, boost::uint64_t period):
|
||||||
|
idle_callback_conext_base(io_serice),
|
||||||
|
m_handler(h)
|
||||||
|
{this->m_period = period;}
|
||||||
|
|
||||||
|
t_handler m_handler;
|
||||||
|
virtual bool call_handler()
|
||||||
|
{
|
||||||
|
return m_handler();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class t_handler>
|
||||||
|
bool add_idle_handler(t_handler t_callback, boost::uint64_t timeout_ms)
|
||||||
|
{
|
||||||
|
boost::shared_ptr<idle_callback_conext_base> ptr(new idle_callback_conext<t_handler>(io_service_, t_callback, timeout_ms));
|
||||||
|
//needed call handler here ?...
|
||||||
|
ptr->m_timer.expires_from_now(boost::posix_time::milliseconds(ptr->m_period));
|
||||||
|
ptr->m_timer.async_wait(boost::bind(&boosted_tcp_server<t_protocol_handler>::global_timer_handler, this, ptr));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool global_timer_handler(/*const boost::system::error_code& err, */boost::shared_ptr<idle_callback_conext_base> ptr)
|
||||||
|
{
|
||||||
|
//if handler return false - he don't want to be called anymore
|
||||||
|
if(!ptr->call_handler())
|
||||||
|
return true;
|
||||||
|
ptr->m_timer.expires_from_now(boost::posix_time::milliseconds(ptr->m_period));
|
||||||
|
ptr->m_timer.async_wait(boost::bind(&boosted_tcp_server<t_protocol_handler>::global_timer_handler, this, ptr));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class t_handler>
|
||||||
|
bool async_call(t_handler t_callback)
|
||||||
|
{
|
||||||
|
io_service_.post(t_callback);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
typename t_protocol_handler::config_type m_config;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Run the server's io_service loop.
|
||||||
|
bool worker_thread();
|
||||||
|
/// Handle completion of an asynchronous accept operation.
|
||||||
|
void handle_accept(const boost::system::error_code& e);
|
||||||
|
|
||||||
|
bool is_thread_worker();
|
||||||
|
|
||||||
|
/// The io_service used to perform asynchronous operations.
|
||||||
|
std::unique_ptr<boost::asio::io_service> m_io_service_local_instance;
|
||||||
|
boost::asio::io_service& io_service_;
|
||||||
|
|
||||||
|
/// Acceptor used to listen for incoming connections.
|
||||||
|
boost::asio::ip::tcp::acceptor acceptor_;
|
||||||
|
|
||||||
|
/// The next connection to be accepted.
|
||||||
|
connection_ptr new_connection_;
|
||||||
|
std::atomic<bool> m_stop_signal_sent;
|
||||||
|
uint32_t m_port;
|
||||||
|
volatile boost::uint32_t m_sockets_count;
|
||||||
|
std::string m_address;
|
||||||
|
std::string m_thread_name_prefix;
|
||||||
|
size_t m_threads_count;
|
||||||
|
i_connection_filter* m_pfilter;
|
||||||
|
std::vector<boost::shared_ptr<boost::thread> > m_threads;
|
||||||
|
boost::thread::id m_main_thread_id;
|
||||||
|
critical_section m_threads_lock;
|
||||||
|
volatile uint32_t m_thread_index;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "abstract_tcp_server2.inl"
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,811 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "net_utils_base.h"
|
||||||
|
#include <boost/lambda/bind.hpp>
|
||||||
|
#include <boost/foreach.hpp>
|
||||||
|
#include <boost/lambda/lambda.hpp>
|
||||||
|
#include <boost/uuid/random_generator.hpp>
|
||||||
|
#include <boost/chrono.hpp>
|
||||||
|
#include <boost/utility/value_init.hpp>
|
||||||
|
#include <boost/asio/deadline_timer.hpp>
|
||||||
|
#include "misc_language.h"
|
||||||
|
#include "pragma_comp_defs.h"
|
||||||
|
|
||||||
|
PRAGMA_WARNING_PUSH
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
PRAGMA_WARNING_DISABLE_VS(4355)
|
||||||
|
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
connection<t_protocol_handler>::connection(boost::asio::io_service& io_service,
|
||||||
|
typename t_protocol_handler::config_type& config, volatile boost::uint32_t& sock_count, i_connection_filter* &pfilter)
|
||||||
|
: strand_(io_service),
|
||||||
|
socket_(io_service),
|
||||||
|
//context(typename boost::value_initialized<t_connection_context>())
|
||||||
|
m_protocol_handler(this, config, context),
|
||||||
|
m_want_close_connection(0),
|
||||||
|
m_was_shutdown(0),
|
||||||
|
m_ref_sockets_count(sock_count),
|
||||||
|
m_pfilter(pfilter)
|
||||||
|
{
|
||||||
|
boost::interprocess::ipcdetail::atomic_inc32(&m_ref_sockets_count);
|
||||||
|
}
|
||||||
|
PRAGMA_WARNING_DISABLE_VS(4355)
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
connection<t_protocol_handler>::~connection()
|
||||||
|
{
|
||||||
|
if(!m_was_shutdown)
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("[sock " << socket_.native_handle() << "] Socket destroyed without shutdown.");
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_PRINT_L3("[sock " << socket_.native_handle() << "] Socket destroyed");
|
||||||
|
boost::interprocess::ipcdetail::atomic_dec32(&m_ref_sockets_count);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
boost::asio::ip::tcp::socket& connection<t_protocol_handler>::socket()
|
||||||
|
{
|
||||||
|
return socket_;
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
boost::shared_ptr<connection<t_protocol_handler> > connection<t_protocol_handler>::safe_shared_from_this()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return connection<t_protocol_handler>::shared_from_this();
|
||||||
|
}
|
||||||
|
catch (const boost::bad_weak_ptr&)
|
||||||
|
{
|
||||||
|
// It happens when the connection is being deleted
|
||||||
|
return boost::shared_ptr<connection<t_protocol_handler> >();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
bool connection<t_protocol_handler>::start(bool is_income, bool is_multithreaded)
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
|
||||||
|
// Use safe_shared_from_this, because of this is public method and it can be called on the object being deleted
|
||||||
|
auto self = safe_shared_from_this();
|
||||||
|
if(!self)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_is_multithreaded = is_multithreaded;
|
||||||
|
|
||||||
|
boost::system::error_code ec;
|
||||||
|
auto remote_ep = socket_.remote_endpoint(ec);
|
||||||
|
CHECK_AND_NO_ASSERT_MES(!ec, false, "Failed to get remote endpoint: " << ec.message() << ':' << ec.value());
|
||||||
|
|
||||||
|
auto local_ep = socket_.local_endpoint(ec);
|
||||||
|
CHECK_AND_NO_ASSERT_MES(!ec, false, "Failed to get local endpoint: " << ec.message() << ':' << ec.value());
|
||||||
|
|
||||||
|
context = boost::value_initialized<t_connection_context>();
|
||||||
|
long ip_ = boost::asio::detail::socket_ops::host_to_network_long(remote_ep.address().to_v4().to_ulong());
|
||||||
|
|
||||||
|
context.set_details(boost::uuids::random_generator()(), ip_, remote_ep.port(), is_income);
|
||||||
|
LOG_PRINT_L3("[sock " << socket_.native_handle() << "] new connection from " << print_connection_context_short(context) <<
|
||||||
|
" to " << local_ep.address().to_string() << ':' << local_ep.port() <<
|
||||||
|
", total sockets objects " << m_ref_sockets_count);
|
||||||
|
|
||||||
|
if(m_pfilter && !m_pfilter->is_remote_ip_allowed(context.m_remote_ip))
|
||||||
|
{
|
||||||
|
LOG_PRINT_L2("[sock " << socket_.native_handle() << "] ip denied " << string_tools::get_ip_string_from_int32(context.m_remote_ip) << ", shutdowning connection");
|
||||||
|
close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_protocol_handler.after_init_connection();
|
||||||
|
|
||||||
|
socket_.async_read_some(boost::asio::buffer(buffer_),
|
||||||
|
strand_.wrap(
|
||||||
|
boost::bind(&connection<t_protocol_handler>::handle_read, self,
|
||||||
|
boost::asio::placeholders::error,
|
||||||
|
boost::asio::placeholders::bytes_transferred)));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
CATCH_ENTRY_L0("connection<t_protocol_handler>::start()", false);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
bool connection<t_protocol_handler>::request_callback()
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
LOG_PRINT_L2("[" << print_connection_context_short(context) << "] request_callback");
|
||||||
|
// Use safe_shared_from_this, because of this is public method and it can be called on the object being deleted
|
||||||
|
auto self = safe_shared_from_this();
|
||||||
|
if(!self)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
strand_.post(boost::bind(&connection<t_protocol_handler>::call_back_starter, self));
|
||||||
|
CATCH_ENTRY_L0("connection<t_protocol_handler>::request_callback()", false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
boost::asio::io_service& connection<t_protocol_handler>::get_io_service()
|
||||||
|
{
|
||||||
|
return socket_.get_io_service();
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
bool connection<t_protocol_handler>::add_ref()
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
LOG_PRINT_L4("[sock " << socket_.native_handle() << "] add_ref");
|
||||||
|
CRITICAL_REGION_LOCAL(m_self_refs_lock);
|
||||||
|
|
||||||
|
// Use safe_shared_from_this, because of this is public method and it can be called on the object being deleted
|
||||||
|
auto self = safe_shared_from_this();
|
||||||
|
if(!self)
|
||||||
|
return false;
|
||||||
|
if(m_was_shutdown)
|
||||||
|
return false;
|
||||||
|
m_self_refs.push_back(self);
|
||||||
|
return true;
|
||||||
|
CATCH_ENTRY_L0("connection<t_protocol_handler>::add_ref()", false);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
bool connection<t_protocol_handler>::release()
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
boost::shared_ptr<connection<t_protocol_handler> > back_connection_copy;
|
||||||
|
LOG_PRINT_L4("[sock " << socket_.native_handle() << "] release");
|
||||||
|
CRITICAL_REGION_BEGIN(m_self_refs_lock);
|
||||||
|
CHECK_AND_ASSERT_MES(m_self_refs.size(), false, "[sock " << socket_.native_handle() << "] m_self_refs empty at connection<t_protocol_handler>::release() call");
|
||||||
|
//erasing from container without additional copy can cause start deleting object, including m_self_refs
|
||||||
|
back_connection_copy = m_self_refs.back();
|
||||||
|
m_self_refs.pop_back();
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
return true;
|
||||||
|
CATCH_ENTRY_L0("connection<t_protocol_handler>::release()", false);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
void connection<t_protocol_handler>::call_back_starter()
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
LOG_PRINT_L2("[" << print_connection_context_short(context) << "] fired_callback");
|
||||||
|
m_protocol_handler.handle_qued_callback();
|
||||||
|
CATCH_ENTRY_L0("connection<t_protocol_handler>::call_back_starter()", void());
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
void connection<t_protocol_handler>::handle_read(const boost::system::error_code& e,
|
||||||
|
std::size_t bytes_transferred)
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
LOG_PRINT_L4("[sock " << socket_.native_handle() << "] Async read calledback.");
|
||||||
|
|
||||||
|
if (!e)
|
||||||
|
{
|
||||||
|
LOG_PRINT("[sock " << socket_.native_handle() << "] RECV " << bytes_transferred, LOG_LEVEL_4);
|
||||||
|
bool recv_res = m_protocol_handler.handle_recv(buffer_.data(), bytes_transferred);
|
||||||
|
if(!recv_res)
|
||||||
|
{
|
||||||
|
LOG_PRINT("[sock " << socket_.native_handle() << "] protocol_want_close", LOG_LEVEL_4);
|
||||||
|
|
||||||
|
//some error in protocol, protocol handler ask to close connection
|
||||||
|
boost::interprocess::ipcdetail::atomic_write32(&m_want_close_connection, 1);
|
||||||
|
bool do_shutdown = false;
|
||||||
|
CRITICAL_REGION_BEGIN(m_send_que_lock);
|
||||||
|
if(!m_send_que.size())
|
||||||
|
do_shutdown = true;
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
if(do_shutdown)
|
||||||
|
shutdown();
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
socket_.async_read_some(boost::asio::buffer(buffer_),
|
||||||
|
strand_.wrap(
|
||||||
|
boost::bind(&connection<t_protocol_handler>::handle_read, connection<t_protocol_handler>::shared_from_this(),
|
||||||
|
boost::asio::placeholders::error,
|
||||||
|
boost::asio::placeholders::bytes_transferred)));
|
||||||
|
LOG_PRINT_L4("[sock " << socket_.native_handle() << "]Async read requested.");
|
||||||
|
}
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("[sock " << socket_.native_handle() << "] Some not success at read: " << e.message() << ':' << e.value());
|
||||||
|
if(e.value() != 2)
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("[sock " << socket_.native_handle() << "] Some problems at read: " << e.message() << ':' << e.value());
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If an error occurs then no new asynchronous operations are started. This
|
||||||
|
// means that all shared_ptr references to the connection object will
|
||||||
|
// disappear and the object will be destroyed automatically after this
|
||||||
|
// handler returns. The connection class's destructor closes the socket.
|
||||||
|
CATCH_ENTRY_L0("connection<t_protocol_handler>::handle_read", void());
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
bool connection<t_protocol_handler>::call_run_once_service_io()
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
if(!m_is_multithreaded)
|
||||||
|
{
|
||||||
|
//single thread model, we can wait in blocked call
|
||||||
|
size_t cnt = socket_.get_io_service().run_one();
|
||||||
|
if(!cnt)//service is going to quit
|
||||||
|
return false;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
//multi thread model, we can't(!) wait in blocked call
|
||||||
|
//so we make non blocking call and releasing CPU by calling sleep(0);
|
||||||
|
//if no handlers were called
|
||||||
|
//TODO: Maybe we need to have have critical section + event + callback to upper protocol to
|
||||||
|
//ask it inside(!) critical region if we still able to go in event wait...
|
||||||
|
size_t cnt = socket_.get_io_service().poll_one();
|
||||||
|
if(!cnt)
|
||||||
|
misc_utils::sleep_no_w(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
CATCH_ENTRY_L0("connection<t_protocol_handler>::call_run_once_service_io", false);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
bool connection<t_protocol_handler>::do_send(const void* ptr, size_t cb)
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
// Use safe_shared_from_this, because of this is public method and it can be called on the object being deleted
|
||||||
|
auto self = safe_shared_from_this();
|
||||||
|
if(!self)
|
||||||
|
return false;
|
||||||
|
if(m_was_shutdown)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
LOG_PRINT("[sock " << socket_.native_handle() << "] SEND " << cb, LOG_LEVEL_4);
|
||||||
|
//some data should be wrote to stream
|
||||||
|
//request complete
|
||||||
|
|
||||||
|
epee::critical_region_t<decltype(m_send_que_lock)> send_guard(m_send_que_lock);
|
||||||
|
if(m_send_que.size() > ABSTRACT_SERVER_SEND_QUE_MAX_COUNT)
|
||||||
|
{
|
||||||
|
send_guard.unlock();
|
||||||
|
LOG_ERROR("send que size is more than ABSTRACT_SERVER_SEND_QUE_MAX_COUNT(" << ABSTRACT_SERVER_SEND_QUE_MAX_COUNT << "), shutting down connection");
|
||||||
|
close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_send_que.resize(m_send_que.size()+1);
|
||||||
|
m_send_que.back().assign((const char*)ptr, cb);
|
||||||
|
|
||||||
|
if(m_send_que.size() > 1)
|
||||||
|
{
|
||||||
|
//active operation should be in progress, nothing to do, just wait last operation callback
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
//no active operation
|
||||||
|
if(m_send_que.size()!=1)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Looks like no active operations, but send que size != 1!!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::asio::async_write(socket_, boost::asio::buffer(m_send_que.front().data(), m_send_que.front().size()),
|
||||||
|
//strand_.wrap(
|
||||||
|
boost::bind(&connection<t_protocol_handler>::handle_write, self, _1, _2)
|
||||||
|
//)
|
||||||
|
);
|
||||||
|
|
||||||
|
LOG_PRINT_L4("[sock " << socket_.native_handle() << "] Async send requested " << m_send_que.front().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
CATCH_ENTRY_L0("connection<t_protocol_handler>::do_send", false);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
bool connection<t_protocol_handler>::shutdown()
|
||||||
|
{
|
||||||
|
// Initiate graceful connection closure.
|
||||||
|
boost::system::error_code ignored_ec;
|
||||||
|
socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
|
||||||
|
m_was_shutdown = true;
|
||||||
|
m_protocol_handler.release_protocol();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
bool connection<t_protocol_handler>::close()
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
LOG_PRINT_L4("[sock " << socket_.native_handle() << "] Que Shutdown called.");
|
||||||
|
size_t send_que_size = 0;
|
||||||
|
CRITICAL_REGION_BEGIN(m_send_que_lock);
|
||||||
|
send_que_size = m_send_que.size();
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
boost::interprocess::ipcdetail::atomic_write32(&m_want_close_connection, 1);
|
||||||
|
if(!send_que_size)
|
||||||
|
{
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
CATCH_ENTRY_L0("connection<t_protocol_handler>::close", false);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
void connection<t_protocol_handler>::handle_write(const boost::system::error_code& e, size_t cb)
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
LOG_PRINT_L4("[sock " << socket_.native_handle() << "] Async send calledback " << cb);
|
||||||
|
|
||||||
|
if (e)
|
||||||
|
{
|
||||||
|
LOG_PRINT_L0("[sock " << socket_.native_handle() << "] Some problems at write: " << e.message() << ':' << e.value());
|
||||||
|
shutdown();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool do_shutdown = false;
|
||||||
|
CRITICAL_REGION_BEGIN(m_send_que_lock);
|
||||||
|
if(m_send_que.empty())
|
||||||
|
{
|
||||||
|
LOG_ERROR("[sock " << socket_.native_handle() << "] m_send_que.size() == 0 at handle_write!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_send_que.pop_front();
|
||||||
|
if(m_send_que.empty())
|
||||||
|
{
|
||||||
|
if(boost::interprocess::ipcdetail::atomic_read32(&m_want_close_connection))
|
||||||
|
{
|
||||||
|
do_shutdown = true;
|
||||||
|
}
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
//have more data to send
|
||||||
|
boost::asio::async_write(socket_, boost::asio::buffer(m_send_que.front().data(), m_send_que.front().size()),
|
||||||
|
//strand_.wrap(
|
||||||
|
boost::bind(&connection<t_protocol_handler>::handle_write, connection<t_protocol_handler>::shared_from_this(), _1, _2));
|
||||||
|
//);
|
||||||
|
}
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
|
||||||
|
if(do_shutdown)
|
||||||
|
{
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
CATCH_ENTRY_L0("connection<t_protocol_handler>::handle_write", void());
|
||||||
|
}
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
boosted_tcp_server<t_protocol_handler>::boosted_tcp_server():
|
||||||
|
m_io_service_local_instance(new boost::asio::io_service()),
|
||||||
|
io_service_(*m_io_service_local_instance.get()),
|
||||||
|
acceptor_(io_service_),
|
||||||
|
new_connection_(new connection<t_protocol_handler>(io_service_, m_config, m_sockets_count, m_pfilter)),
|
||||||
|
m_stop_signal_sent(false), m_port(0), m_sockets_count(0), m_threads_count(0), m_pfilter(NULL), m_thread_index(0)
|
||||||
|
{
|
||||||
|
m_thread_name_prefix = "NET";
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
boosted_tcp_server<t_protocol_handler>::boosted_tcp_server(boost::asio::io_service& extarnal_io_service):
|
||||||
|
io_service_(extarnal_io_service),
|
||||||
|
acceptor_(io_service_),
|
||||||
|
new_connection_(new connection<t_protocol_handler>(io_service_, m_config, m_sockets_count, m_pfilter)),
|
||||||
|
m_stop_signal_sent(false), m_port(0), m_sockets_count(0), m_threads_count(0), m_pfilter(NULL), m_thread_index(0)
|
||||||
|
{
|
||||||
|
m_thread_name_prefix = "NET";
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
boosted_tcp_server<t_protocol_handler>::~boosted_tcp_server()
|
||||||
|
{
|
||||||
|
this->send_stop_signal();
|
||||||
|
timed_wait_server_stop(10000);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
bool boosted_tcp_server<t_protocol_handler>::init_server(uint32_t port, const std::string address)
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
m_stop_signal_sent = false;
|
||||||
|
m_port = port;
|
||||||
|
m_address = address;
|
||||||
|
// Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
|
||||||
|
boost::asio::ip::tcp::resolver resolver(io_service_);
|
||||||
|
boost::asio::ip::tcp::resolver::query query(address, boost::lexical_cast<std::string>(port));
|
||||||
|
boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
|
||||||
|
acceptor_.open(endpoint.protocol());
|
||||||
|
acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
|
||||||
|
acceptor_.bind(endpoint);
|
||||||
|
acceptor_.listen();
|
||||||
|
boost::asio::ip::tcp::endpoint binded_endpoint = acceptor_.local_endpoint();
|
||||||
|
m_port = binded_endpoint.port();
|
||||||
|
acceptor_.async_accept(new_connection_->socket(),
|
||||||
|
boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept, this,
|
||||||
|
boost::asio::placeholders::error));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::init_server", false);
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
PUSH_WARNINGS
|
||||||
|
DISABLE_GCC_WARNING(maybe-uninitialized)
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
bool boosted_tcp_server<t_protocol_handler>::init_server(const std::string port, const std::string& address)
|
||||||
|
{
|
||||||
|
uint32_t p = 0;
|
||||||
|
|
||||||
|
if (port.size() && !string_tools::get_xtype_from_string(p, port)) {
|
||||||
|
return false;
|
||||||
|
LOG_ERROR("Failed to convert port no = port");
|
||||||
|
}
|
||||||
|
return this->init_server(p, address);
|
||||||
|
}
|
||||||
|
POP_WARNINGS
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
bool boosted_tcp_server<t_protocol_handler>::worker_thread()
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
uint32_t local_thr_index = boost::interprocess::ipcdetail::atomic_inc32(&m_thread_index);
|
||||||
|
std::string thread_name = std::string("[") + m_thread_name_prefix;
|
||||||
|
thread_name += boost::to_string(local_thr_index) + "]";
|
||||||
|
log_space::log_singletone::set_thread_log_prefix(thread_name);
|
||||||
|
while(!m_stop_signal_sent)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
io_service_.run();
|
||||||
|
}
|
||||||
|
catch(const std::exception& ex)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Exception at server worker thread, what=" << ex.what());
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Exception at server worker thread, unknown execption");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG_PRINT_L4("Worker thread finished");
|
||||||
|
return true;
|
||||||
|
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::worker_thread", false);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
void boosted_tcp_server<t_protocol_handler>::set_threads_prefix(const std::string& prefix_name)
|
||||||
|
{
|
||||||
|
m_thread_name_prefix = prefix_name;
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
void boosted_tcp_server<t_protocol_handler>::set_connection_filter(i_connection_filter* pfilter)
|
||||||
|
{
|
||||||
|
m_pfilter = pfilter;
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
bool boosted_tcp_server<t_protocol_handler>::run_server(size_t threads_count, bool wait)
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
m_threads_count = threads_count;
|
||||||
|
m_main_thread_id = boost::this_thread::get_id();
|
||||||
|
log_space::log_singletone::set_thread_log_prefix("[SRV_MAIN]");
|
||||||
|
while(!m_stop_signal_sent)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Create a pool of threads to run all of the io_services.
|
||||||
|
CRITICAL_REGION_BEGIN(m_threads_lock);
|
||||||
|
for (std::size_t i = 0; i < threads_count; ++i)
|
||||||
|
{
|
||||||
|
boost::shared_ptr<boost::thread> thread(new boost::thread(
|
||||||
|
boost::bind(&boosted_tcp_server<t_protocol_handler>::worker_thread, this)));
|
||||||
|
m_threads.push_back(thread);
|
||||||
|
}
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
// Wait for all threads in the pool to exit.
|
||||||
|
if(wait)
|
||||||
|
{
|
||||||
|
for (std::size_t i = 0; i < m_threads.size(); ++i)
|
||||||
|
m_threads[i]->join();
|
||||||
|
m_threads.clear();
|
||||||
|
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(wait && !m_stop_signal_sent)
|
||||||
|
{
|
||||||
|
//some problems with the listening socket ?..
|
||||||
|
LOG_PRINT_L0("Net service stopped without stop request, restarting...");
|
||||||
|
if(!this->init_server(m_port, m_address))
|
||||||
|
{
|
||||||
|
LOG_PRINT_L0("Reiniting service failed, exit.");
|
||||||
|
return false;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
LOG_PRINT_L0("Reiniting OK.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::run_server", false);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
bool boosted_tcp_server<t_protocol_handler>::is_thread_worker()
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
CRITICAL_REGION_LOCAL(m_threads_lock);
|
||||||
|
BOOST_FOREACH(boost::shared_ptr<boost::thread>& thp, m_threads)
|
||||||
|
{
|
||||||
|
if(thp->get_id() == boost::this_thread::get_id())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(m_threads_count == 1 && boost::this_thread::get_id() == m_main_thread_id)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::is_thread_worker", false);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
bool boosted_tcp_server<t_protocol_handler>::timed_wait_server_stop(boost::uint64_t wait_mseconds)
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
boost::chrono::milliseconds ms(wait_mseconds);
|
||||||
|
for (std::size_t i = 0; i < m_threads.size(); ++i)
|
||||||
|
{
|
||||||
|
if(m_threads[i]->joinable() && !m_threads[i]->try_join_for(ms))
|
||||||
|
{
|
||||||
|
LOG_PRINT_L0("Interrupting thread " << m_threads[i]->native_handle());
|
||||||
|
m_threads[i]->interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::timed_wait_server_stop", false);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
void boosted_tcp_server<t_protocol_handler>::send_stop_signal()
|
||||||
|
{
|
||||||
|
m_stop_signal_sent = true;
|
||||||
|
TRY_ENTRY();
|
||||||
|
io_service_.stop();
|
||||||
|
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::send_stop_signal()", void());
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
bool boosted_tcp_server<t_protocol_handler>::is_stop_signal_sent()
|
||||||
|
{
|
||||||
|
return m_stop_signal_sent;
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
void boosted_tcp_server<t_protocol_handler>::handle_accept(const boost::system::error_code& e)
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
if (!e)
|
||||||
|
{
|
||||||
|
connection_ptr conn(std::move(new_connection_));
|
||||||
|
|
||||||
|
new_connection_.reset(new connection<t_protocol_handler>(io_service_, m_config, m_sockets_count, m_pfilter));
|
||||||
|
acceptor_.async_accept(new_connection_->socket(),
|
||||||
|
boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept, this,
|
||||||
|
boost::asio::placeholders::error));
|
||||||
|
|
||||||
|
bool r = conn->start(true, 1 < m_threads_count);
|
||||||
|
if (!r)
|
||||||
|
LOG_ERROR("[sock " << conn->socket().native_handle() << "] Failed to start connection, connections_count = " << m_sockets_count);
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
LOG_ERROR("Some problems at accept: " << e.message() << ", connections_count = " << m_sockets_count);
|
||||||
|
}
|
||||||
|
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::handle_accept", void());
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
bool boosted_tcp_server<t_protocol_handler>::connect(const std::string& adr, const std::string& port, boost::uint32_t conn_timeout, t_connection_context& conn_context, const std::string& bind_ip)
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
|
||||||
|
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_config, m_sockets_count, m_pfilter) );
|
||||||
|
boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket();
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
boost::asio::ip::tcp::resolver resolver(io_service_);
|
||||||
|
boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), adr, 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 " << adr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
//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);
|
||||||
|
|
||||||
|
sock_.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(adr.c_str()), 0);
|
||||||
|
sock_.bind(local_endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
NOTICE: be careful to make sync connection from event handler: in case if all threads suddenly do sync connect, there will be no thread to dispatch events from io service.
|
||||||
|
*/
|
||||||
|
|
||||||
|
boost::system::error_code ec = boost::asio::error::would_block;
|
||||||
|
|
||||||
|
//have another free thread(s), work in wait mode, without event handling
|
||||||
|
struct local_async_context
|
||||||
|
{
|
||||||
|
boost::system::error_code ec;
|
||||||
|
boost::mutex connect_mut;
|
||||||
|
boost::condition_variable cond;
|
||||||
|
};
|
||||||
|
|
||||||
|
boost::shared_ptr<local_async_context> local_shared_context(new local_async_context());
|
||||||
|
local_shared_context->ec = boost::asio::error::would_block;
|
||||||
|
boost::unique_lock<boost::mutex> lock(local_shared_context->connect_mut);
|
||||||
|
auto connect_callback = [](boost::system::error_code ec_, boost::shared_ptr<local_async_context> shared_context)
|
||||||
|
{
|
||||||
|
shared_context->connect_mut.lock(); shared_context->ec = ec_; shared_context->connect_mut.unlock(); shared_context->cond.notify_one();
|
||||||
|
};
|
||||||
|
|
||||||
|
sock_.async_connect(remote_endpoint, boost::bind<void>(connect_callback, _1, local_shared_context));
|
||||||
|
while(local_shared_context->ec == boost::asio::error::would_block)
|
||||||
|
{
|
||||||
|
bool r = local_shared_context->cond.timed_wait(lock, boost::get_system_time() + boost::posix_time::milliseconds(conn_timeout));
|
||||||
|
if(local_shared_context->ec == boost::asio::error::would_block && !r)
|
||||||
|
{
|
||||||
|
//timeout
|
||||||
|
sock_.close();
|
||||||
|
LOG_PRINT_L3("Failed to connect to " << adr << ":" << port << ", because of timeout (" << conn_timeout << ")");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ec = local_shared_context->ec;
|
||||||
|
|
||||||
|
if (ec || !sock_.is_open())
|
||||||
|
{
|
||||||
|
LOG_PRINT("Some problems at connect, message: " << ec.message(), LOG_LEVEL_3);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_PRINT_L3("Connected success to " << adr << ':' << port);
|
||||||
|
|
||||||
|
bool r = new_connection_l->start(false, 1 < m_threads_count);
|
||||||
|
if (r)
|
||||||
|
{
|
||||||
|
new_connection_l->get_context(conn_context);
|
||||||
|
//new_connection_l.reset(new connection<t_protocol_handler>(io_service_, m_config, m_sockets_count, m_pfilter));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_ERROR("[sock " << new_connection_->socket().native_handle() << "] Failed to start connection, connections_count = " << m_sockets_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
|
||||||
|
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::connect", false);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
template<class t_protocol_handler> template<class t_callback>
|
||||||
|
bool boosted_tcp_server<t_protocol_handler>::connect_async(const std::string& adr, const std::string& port, boost::uint32_t conn_timeout, t_callback cb, const std::string& bind_ip)
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_config, m_sockets_count, m_pfilter) );
|
||||||
|
boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket();
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
boost::asio::ip::tcp::resolver resolver(io_service_);
|
||||||
|
boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), adr, 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 " << adr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
boost::asio::ip::tcp::endpoint remote_endpoint(*iterator);
|
||||||
|
|
||||||
|
sock_.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(adr.c_str()), 0);
|
||||||
|
sock_.bind(local_endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::shared_ptr<boost::asio::deadline_timer> sh_deadline(new boost::asio::deadline_timer(io_service_));
|
||||||
|
//start deadline
|
||||||
|
sh_deadline->expires_from_now(boost::posix_time::milliseconds(conn_timeout));
|
||||||
|
sh_deadline->async_wait([=](const boost::system::error_code& error)
|
||||||
|
{
|
||||||
|
if(error != boost::asio::error::operation_aborted)
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("Failed to connect to " << adr << ':' << port << ", because of timeout (" << conn_timeout << ")");
|
||||||
|
new_connection_l->socket().close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//start async connect
|
||||||
|
sock_.async_connect(remote_endpoint, [=](const boost::system::error_code& ec_)
|
||||||
|
{
|
||||||
|
t_connection_context conn_context = AUTO_VAL_INIT(conn_context);
|
||||||
|
boost::system::error_code ignored_ec;
|
||||||
|
boost::asio::ip::tcp::socket::endpoint_type lep = new_connection_l->socket().local_endpoint(ignored_ec);
|
||||||
|
if(!ec_)
|
||||||
|
{//success
|
||||||
|
if(!sh_deadline->cancel())
|
||||||
|
{
|
||||||
|
cb(conn_context, boost::asio::error::operation_aborted);//this mean that deadline timer already queued callback with cancel operation, rare situation
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("[sock " << new_connection_l->socket().native_handle() << "] Connected success to " << adr << ':' << port <<
|
||||||
|
" from " << lep.address().to_string() << ':' << lep.port());
|
||||||
|
bool r = new_connection_l->start(false, 1 < m_threads_count);
|
||||||
|
if (r)
|
||||||
|
{
|
||||||
|
new_connection_l->get_context(conn_context);
|
||||||
|
cb(conn_context, ec_);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("[sock " << new_connection_l->socket().native_handle() << "] Failed to start connection to " << adr << ':' << port);
|
||||||
|
cb(conn_context, boost::asio::error::fault);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("[sock " << new_connection_l->socket().native_handle() << "] Failed to connect to " << adr << ':' << port <<
|
||||||
|
" from " << lep.address().to_string() << ':' << lep.port() << ": " << ec_.message() << ':' << ec_.value());
|
||||||
|
cb(conn_context, ec_);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::connect_async", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PRAGMA_WARNING_POP
|
|
@ -0,0 +1,233 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _LEVIN_CP_SERVER_H_
|
||||||
|
#define _LEVIN_CP_SERVER_H_
|
||||||
|
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <rpc.h>
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
|
#include "misc_log_ex.h"
|
||||||
|
//#include "threads_helper.h"
|
||||||
|
#include "syncobj.h"
|
||||||
|
#define ENABLE_PROFILING
|
||||||
|
#include "profile_tools.h"
|
||||||
|
#include "net_utils_base.h"
|
||||||
|
#include "pragma_comp_defs.h"
|
||||||
|
|
||||||
|
#define LEVIN_DEFAULT_DATA_BUFF_SIZE 2000
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
|
||||||
|
template<class TProtocol>
|
||||||
|
class cp_server_impl//: public abstract_handler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
cp_server_impl(/*abstract_handler* phandler = NULL*/);
|
||||||
|
virtual ~cp_server_impl();
|
||||||
|
|
||||||
|
bool init_server(int port_no);
|
||||||
|
bool deinit_server();
|
||||||
|
bool run_server(int threads_count = 0);
|
||||||
|
bool send_stop_signal();
|
||||||
|
bool is_stop_signal();
|
||||||
|
virtual bool on_net_idle(){return true;}
|
||||||
|
size_t get_active_connections_num();
|
||||||
|
typename TProtocol::config_type& get_config_object(){return m_config;}
|
||||||
|
private:
|
||||||
|
enum overlapped_operation_type
|
||||||
|
{
|
||||||
|
op_type_recv,
|
||||||
|
op_type_send,
|
||||||
|
op_type_stop
|
||||||
|
};
|
||||||
|
|
||||||
|
struct io_data_base
|
||||||
|
{
|
||||||
|
OVERLAPPED m_overlapped;
|
||||||
|
WSABUF DataBuf;
|
||||||
|
overlapped_operation_type m_op_type;
|
||||||
|
DWORD TotalBuffBytes;
|
||||||
|
volatile LONG m_is_in_use;
|
||||||
|
char Buffer[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
PRAGMA_WARNING_PUSH
|
||||||
|
PRAGMA_WARNING_DISABLE_VS(4355)
|
||||||
|
template<class TProtocol>
|
||||||
|
struct connection: public net_utils::i_service_endpoint
|
||||||
|
{
|
||||||
|
connection(typename TProtocol::config_type& ref_config):m_sock(INVALID_SOCKET), m_tprotocol_handler(this, ref_config, context), m_psend_data(NULL), m_precv_data(NULL), m_asked_to_shutdown(0), m_connection_shutwoned(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//connection():m_sock(INVALID_SOCKET), m_tprotocol_handler(this, m_dummy_config, context), m_psend_data(NULL), m_precv_data(NULL), m_asked_to_shutdown(0), m_connection_shutwoned(0)
|
||||||
|
//{
|
||||||
|
//}
|
||||||
|
|
||||||
|
connection<TProtocol>& operator=(const connection<TProtocol>& obj)
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool init_buffers()
|
||||||
|
{
|
||||||
|
m_psend_data = (io_data_base*)new char[sizeof(io_data_base) + LEVIN_DEFAULT_DATA_BUFF_SIZE-1];
|
||||||
|
m_psend_data->TotalBuffBytes = LEVIN_DEFAULT_DATA_BUFF_SIZE;
|
||||||
|
m_precv_data = (io_data_base*)new char[sizeof(io_data_base) + LEVIN_DEFAULT_DATA_BUFF_SIZE-1];
|
||||||
|
m_precv_data->TotalBuffBytes = LEVIN_DEFAULT_DATA_BUFF_SIZE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool query_shutdown()
|
||||||
|
{
|
||||||
|
if(!::InterlockedCompareExchange(&m_asked_to_shutdown, 1, 0))
|
||||||
|
{
|
||||||
|
m_psend_data->m_op_type = op_type_stop;
|
||||||
|
::PostQueuedCompletionStatus(m_completion_port, 0, (ULONG_PTR)this, &m_psend_data->m_overlapped);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//bool set_config(typename TProtocol::config_type& config)
|
||||||
|
//{
|
||||||
|
// this->~connection();
|
||||||
|
// new(this) connection<TProtocol>(config);
|
||||||
|
// return true;
|
||||||
|
//}
|
||||||
|
~connection()
|
||||||
|
{
|
||||||
|
if(m_psend_data)
|
||||||
|
delete m_psend_data;
|
||||||
|
|
||||||
|
if(m_precv_data)
|
||||||
|
delete m_precv_data;
|
||||||
|
}
|
||||||
|
virtual bool handle_send(const void* ptr, size_t cb)
|
||||||
|
{
|
||||||
|
PROFILE_FUNC("[handle_send]");
|
||||||
|
if(m_psend_data->TotalBuffBytes < cb)
|
||||||
|
resize_send_buff((DWORD)cb);
|
||||||
|
|
||||||
|
ZeroMemory(&m_psend_data->m_overlapped, sizeof(OVERLAPPED));
|
||||||
|
m_psend_data->DataBuf.len = (u_long)cb;//m_psend_data->TotalBuffBytes;
|
||||||
|
m_psend_data->DataBuf.buf = m_psend_data->Buffer;
|
||||||
|
memcpy(m_psend_data->DataBuf.buf, ptr, cb);
|
||||||
|
m_psend_data->m_op_type = op_type_send;
|
||||||
|
InterlockedExchange(&m_psend_data->m_is_in_use, 1);
|
||||||
|
DWORD bytes_sent = 0;
|
||||||
|
DWORD flags = 0;
|
||||||
|
int res = 0;
|
||||||
|
{
|
||||||
|
PROFILE_FUNC("[handle_send] ::WSASend");
|
||||||
|
res = ::WSASend(m_sock, &(m_psend_data->DataBuf), 1, &bytes_sent, flags, &(m_psend_data->m_overlapped), NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(res == SOCKET_ERROR )
|
||||||
|
{
|
||||||
|
int err = ::WSAGetLastError();
|
||||||
|
if(WSA_IO_PENDING == err )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
LOG_ERROR("BIG FAIL: WSASend error code not correct, res=" << res << " last_err=" << err);
|
||||||
|
::InterlockedExchange(&m_psend_data->m_is_in_use, 0);
|
||||||
|
query_shutdown();
|
||||||
|
//closesocket(m_psend_data);
|
||||||
|
return false;
|
||||||
|
}else if(0 == res)
|
||||||
|
{
|
||||||
|
::InterlockedExchange(&m_psend_data->m_is_in_use, 0);
|
||||||
|
if(!bytes_sent || bytes_sent != cb)
|
||||||
|
{
|
||||||
|
int err = ::WSAGetLastError();
|
||||||
|
LOG_ERROR("BIG FAIL: WSASend immediatly complete? but bad results, res=" << res << " last_err=" << err);
|
||||||
|
query_shutdown();
|
||||||
|
return false;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool resize_send_buff(DWORD new_size)
|
||||||
|
{
|
||||||
|
if(m_psend_data->TotalBuffBytes >= new_size)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
delete m_psend_data;
|
||||||
|
m_psend_data = (io_data_base*)new char[sizeof(io_data_base) + new_size-1];
|
||||||
|
m_psend_data->TotalBuffBytes = new_size;
|
||||||
|
LOG_PRINT("Connection buffer resized up to " << new_size, LOG_LEVEL_3);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SOCKET m_sock;
|
||||||
|
net_utils::connection_context_base context;
|
||||||
|
TProtocol m_tprotocol_handler;
|
||||||
|
typename TProtocol::config_type m_dummy_config;
|
||||||
|
io_data_base* m_precv_data;
|
||||||
|
io_data_base* m_psend_data;
|
||||||
|
HANDLE m_completion_port;
|
||||||
|
volatile LONG m_asked_to_shutdown;
|
||||||
|
volatile LONG m_connection_shutwoned;
|
||||||
|
};
|
||||||
|
PRAGMA_WARNING_POP
|
||||||
|
|
||||||
|
bool worker_thread_member();
|
||||||
|
static unsigned CALLBACK worker_thread(void* param);
|
||||||
|
|
||||||
|
bool add_new_connection(SOCKET new_sock, long ip_from, int port_from);
|
||||||
|
bool shutdown_connection(connection<TProtocol>* pconn);
|
||||||
|
|
||||||
|
|
||||||
|
typedef std::map<SOCKET, boost::shared_ptr<connection<TProtocol> > > connections_container;
|
||||||
|
SOCKET m_listen_socket;
|
||||||
|
HANDLE m_completion_port;
|
||||||
|
connections_container m_connections;
|
||||||
|
critical_section m_connections_lock;
|
||||||
|
int m_port;
|
||||||
|
volatile LONG m_stop;
|
||||||
|
//abstract_handler* m_phandler;
|
||||||
|
bool m_initialized;
|
||||||
|
volatile LONG m_worker_thread_counter;
|
||||||
|
typename TProtocol::config_type m_config;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#include "abstract_tcp_server_cp.inl"
|
||||||
|
|
||||||
|
|
||||||
|
#endif //_LEVIN_SERVER_H_
|
|
@ -0,0 +1,605 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 comment(lib, "Ws2_32.lib")
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
template<class TProtocol>
|
||||||
|
cp_server_impl<TProtocol>::cp_server_impl():
|
||||||
|
m_port(0), m_stop(false),
|
||||||
|
m_worker_thread_counter(0), m_listen_socket(INVALID_SOCKET)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------
|
||||||
|
template<class TProtocol>
|
||||||
|
cp_server_impl<TProtocol>::~cp_server_impl()
|
||||||
|
{
|
||||||
|
deinit_server();
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------
|
||||||
|
template<class TProtocol>
|
||||||
|
bool cp_server_impl<TProtocol>::init_server(int port_no)
|
||||||
|
{
|
||||||
|
m_port = port_no;
|
||||||
|
|
||||||
|
WSADATA wsad = {0};
|
||||||
|
int err = ::WSAStartup(MAKEWORD(2,2), &wsad);
|
||||||
|
if ( err != 0 || LOBYTE( wsad.wVersion ) != 2 || HIBYTE( wsad.wVersion ) != 2 )
|
||||||
|
{
|
||||||
|
LOG_ERROR("Could not find a usable WinSock DLL, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_initialized = true;
|
||||||
|
|
||||||
|
m_listen_socket = ::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
|
||||||
|
if(INVALID_SOCKET == m_listen_socket)
|
||||||
|
{
|
||||||
|
err = ::WSAGetLastError();
|
||||||
|
LOG_ERROR("Failed to create socket, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int opt = 1;
|
||||||
|
err = setsockopt (m_listen_socket, SOL_SOCKET,SO_REUSEADDR, reinterpret_cast<char*>(&opt), sizeof(int));
|
||||||
|
if(SOCKET_ERROR == err )
|
||||||
|
{
|
||||||
|
err = ::WSAGetLastError();
|
||||||
|
LOG_PRINT("Failed to setsockopt(SO_REUSEADDR), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_1);
|
||||||
|
deinit_server();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sockaddr_in adr = {0};
|
||||||
|
adr.sin_family = AF_INET;
|
||||||
|
adr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||||
|
adr.sin_port = (u_short)htons(m_port);
|
||||||
|
|
||||||
|
//binding
|
||||||
|
err = bind(m_listen_socket, (const sockaddr*)&adr, sizeof(adr ));
|
||||||
|
if(SOCKET_ERROR == err )
|
||||||
|
{
|
||||||
|
err = ::WSAGetLastError();
|
||||||
|
LOG_PRINT("Failed to Bind, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_1);
|
||||||
|
deinit_server();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
m_completion_port = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
|
||||||
|
if(INVALID_HANDLE_VALUE == m_completion_port)
|
||||||
|
{
|
||||||
|
err = ::WSAGetLastError();
|
||||||
|
LOG_PRINT("Failed to CreateIoCompletionPort, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_1);
|
||||||
|
deinit_server();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------
|
||||||
|
|
||||||
|
//-------------------------------------------------------------
|
||||||
|
static int CALLBACK CPConditionFunc(
|
||||||
|
IN LPWSABUF lpCallerId,
|
||||||
|
IN LPWSABUF lpCallerData,
|
||||||
|
IN OUT LPQOS lpSQOS,
|
||||||
|
IN OUT LPQOS lpGQOS,
|
||||||
|
IN LPWSABUF lpCalleeId,
|
||||||
|
OUT LPWSABUF lpCalleeData,
|
||||||
|
OUT GROUP FAR *g,
|
||||||
|
IN DWORD_PTR dwCallbackData
|
||||||
|
)
|
||||||
|
{
|
||||||
|
|
||||||
|
/*cp_server_impl* pthis = (cp_server_impl*)dwCallbackData;
|
||||||
|
if(!pthis)
|
||||||
|
return CF_REJECT;*/
|
||||||
|
/*if(pthis->get_active_connections_num()>=FD_SETSIZE-1)
|
||||||
|
{
|
||||||
|
LOG_PRINT("Maximum connections count overfull.", LOG_LEVEL_2);
|
||||||
|
return CF_REJECT;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
return CF_ACCEPT;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------
|
||||||
|
template<class TProtocol>
|
||||||
|
size_t cp_server_impl<TProtocol>::get_active_connections_num()
|
||||||
|
{
|
||||||
|
return m_connections.size();
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------
|
||||||
|
template<class TProtocol>
|
||||||
|
unsigned CALLBACK cp_server_impl<TProtocol>::worker_thread(void* param)
|
||||||
|
{
|
||||||
|
if(!param)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
cp_server_impl<TProtocol>* pthis = (cp_server_impl<TProtocol>*)param;
|
||||||
|
pthis->worker_thread_member();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------
|
||||||
|
template<class TProtocol>
|
||||||
|
bool cp_server_impl<TProtocol>::worker_thread_member()
|
||||||
|
{
|
||||||
|
LOG_PRINT("Worker thread STARTED", LOG_LEVEL_1);
|
||||||
|
bool stop_handling = false;
|
||||||
|
while(!stop_handling)
|
||||||
|
{
|
||||||
|
PROFILE_FUNC("[worker_thread]Worker Loop");
|
||||||
|
DWORD bytes_transfered = 0;
|
||||||
|
connection<TProtocol>* pconnection = 0;
|
||||||
|
io_data_base* pio_data = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
PROFILE_FUNC("[worker_thread]GetQueuedCompletionStatus");
|
||||||
|
BOOL res = ::GetQueuedCompletionStatus (m_completion_port, &bytes_transfered , (PULONG_PTR)&pconnection, (LPOVERLAPPED *)&pio_data, INFINITE);
|
||||||
|
if (res == 0)
|
||||||
|
{
|
||||||
|
// check return code for error
|
||||||
|
int err = GetLastError();
|
||||||
|
LOG_PRINT("GetQueuedCompletionStatus failed with error " << err << " " << log_space::get_win32_err_descr(err), LOG_LEVEL_1);
|
||||||
|
|
||||||
|
if(pio_data)
|
||||||
|
::InterlockedExchange(&pio_data->m_is_in_use, 0);
|
||||||
|
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pio_data)
|
||||||
|
::InterlockedExchange(&pio_data->m_is_in_use, 0);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(!bytes_transfered && !pconnection && !pio_data)
|
||||||
|
{
|
||||||
|
//signal to stop
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!pconnection || !pio_data)
|
||||||
|
{
|
||||||
|
LOG_PRINT("BIG FAIL: pconnection or pio_data is empty: pconnection=" << pconnection << " pio_data=" << pio_data, LOG_LEVEL_0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(::InterlockedCompareExchange(&pconnection->m_connection_shutwoned, 0, 0))
|
||||||
|
{
|
||||||
|
LOG_ERROR("InterlockedCompareExchange(&pconnection->m_connection_shutwoned, 0, 0)");
|
||||||
|
//DebugBreak();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pio_data->m_op_type == op_type_stop)
|
||||||
|
{
|
||||||
|
if(!pconnection)
|
||||||
|
{
|
||||||
|
LOG_ERROR("op_type=op_type_stop, but pconnection is empty!!!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
shutdown_connection(pconnection);
|
||||||
|
continue;//
|
||||||
|
}
|
||||||
|
else if(pio_data->m_op_type == op_type_send)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
//do nothing, just queuing request
|
||||||
|
}else if(pio_data->m_op_type == op_type_recv)
|
||||||
|
{
|
||||||
|
PROFILE_FUNC("[worker_thread]m_tprotocol_handler.handle_recv");
|
||||||
|
if(bytes_transfered)
|
||||||
|
{
|
||||||
|
bool res = pconnection->m_tprotocol_handler.handle_recv(pio_data->Buffer, bytes_transfered);
|
||||||
|
if(!res)
|
||||||
|
pconnection->query_shutdown();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pconnection->query_shutdown();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//preparing new request,
|
||||||
|
|
||||||
|
{
|
||||||
|
PROFILE_FUNC("[worker_thread]RECV Request small loop");
|
||||||
|
int res = 0;
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
LOG_PRINT("Prepearing data for WSARecv....", LOG_LEVEL_3);
|
||||||
|
ZeroMemory(&pio_data->m_overlapped, sizeof(OVERLAPPED));
|
||||||
|
pio_data->DataBuf.len = pio_data->TotalBuffBytes;
|
||||||
|
pio_data->DataBuf.buf = pio_data->Buffer;
|
||||||
|
pio_data->m_op_type = op_type_recv;
|
||||||
|
//calling WSARecv() and go to completion waiting
|
||||||
|
DWORD bytes_recvd = 0;
|
||||||
|
DWORD flags = 0;
|
||||||
|
|
||||||
|
LOG_PRINT("Calling WSARecv....", LOG_LEVEL_3);
|
||||||
|
::InterlockedExchange(&pio_data->m_is_in_use, 1);
|
||||||
|
res = WSARecv(pconnection->m_sock, &(pio_data->DataBuf), 1, &bytes_recvd , &flags, &(pio_data->m_overlapped), NULL);
|
||||||
|
if(res == SOCKET_ERROR )
|
||||||
|
{
|
||||||
|
int err = ::WSAGetLastError();
|
||||||
|
if(WSA_IO_PENDING == err )
|
||||||
|
{//go pending, ok
|
||||||
|
LOG_PRINT("WSARecv return WSA_IO_PENDING", LOG_LEVEL_3);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
LOG_ERROR("BIG FAIL: WSARecv error code not correct, res=" << res << " last_err=" << err);
|
||||||
|
::InterlockedExchange(&pio_data->m_is_in_use, 0);
|
||||||
|
pconnection->query_shutdown();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
/*else if(0 == res)
|
||||||
|
{
|
||||||
|
if(!bytes_recvd)
|
||||||
|
{
|
||||||
|
::InterlockedExchange(&pio_data->m_is_in_use, 0);
|
||||||
|
LOG_PRINT("WSARecv return 0, bytes_recvd=0, graceful close.", LOG_LEVEL_3);
|
||||||
|
int err = ::WSAGetLastError();
|
||||||
|
//LOG_ERROR("BIG FAIL: WSARecv error code not correct, res=" << res << " last_err=" << err);
|
||||||
|
//pconnection->query_shutdown();
|
||||||
|
break;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
LOG_PRINT("WSARecv return immediatily 0, bytes_recvd=" << bytes_recvd, LOG_LEVEL_3);
|
||||||
|
//pconnection->m_tprotocol_handler.handle_recv(pio_data->Buffer, bytes_recvd);
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LOG_PRINT("Worker thread STOPED", LOG_LEVEL_1);
|
||||||
|
::InterlockedDecrement(&m_worker_thread_counter);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------
|
||||||
|
template<class TProtocol>
|
||||||
|
bool cp_server_impl<TProtocol>::shutdown_connection(connection<TProtocol>* pconn)
|
||||||
|
{
|
||||||
|
PROFILE_FUNC("[shutdown_connection]");
|
||||||
|
|
||||||
|
if(!pconn)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Attempt to remove null pptr connection!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_PRINT("Shutting down connection ("<< pconn << ")", LOG_LEVEL_3);
|
||||||
|
}
|
||||||
|
m_connections_lock.lock();
|
||||||
|
connections_container::iterator it = m_connections.find(pconn->m_sock);
|
||||||
|
m_connections_lock.unlock();
|
||||||
|
if(it == m_connections.end())
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to find closing socket=" << pconn->m_sock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SOCKET sock = it->second->m_sock;
|
||||||
|
{
|
||||||
|
PROFILE_FUNC("[shutdown_connection] shutdown, close");
|
||||||
|
::shutdown(it->second->m_sock, SD_SEND );
|
||||||
|
}
|
||||||
|
size_t close_sock_wait_count = 0;
|
||||||
|
{
|
||||||
|
LOG_PRINT("Entered to 'in_use wait zone'", LOG_LEVEL_3);
|
||||||
|
PROFILE_FUNC("[shutdown_connection] wait for in_use");
|
||||||
|
while(::InterlockedCompareExchange(&it->second->m_precv_data->m_is_in_use, 1, 1))
|
||||||
|
{
|
||||||
|
|
||||||
|
Sleep(100);
|
||||||
|
close_sock_wait_count++;
|
||||||
|
}
|
||||||
|
LOG_PRINT("First step to 'in_use wait zone'", LOG_LEVEL_3);
|
||||||
|
|
||||||
|
|
||||||
|
while(::InterlockedCompareExchange(&it->second->m_psend_data->m_is_in_use, 1, 1))
|
||||||
|
{
|
||||||
|
Sleep(100);
|
||||||
|
close_sock_wait_count++;
|
||||||
|
}
|
||||||
|
LOG_PRINT("Leaved 'in_use wait zone'", LOG_LEVEL_3);
|
||||||
|
}
|
||||||
|
|
||||||
|
::closesocket(it->second->m_sock);
|
||||||
|
|
||||||
|
::InterlockedExchange(&it->second->m_connection_shutwoned, 1);
|
||||||
|
m_connections_lock.lock();
|
||||||
|
m_connections.erase(it);
|
||||||
|
m_connections_lock.unlock();
|
||||||
|
LOG_PRINT("Socked " << sock << " closed, wait_count=" << close_sock_wait_count, LOG_LEVEL_2);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------
|
||||||
|
template<class TProtocol>
|
||||||
|
bool cp_server_impl<TProtocol>::run_server(int threads_count = 0)
|
||||||
|
{
|
||||||
|
int err = listen(m_listen_socket, 100);
|
||||||
|
if(SOCKET_ERROR == err )
|
||||||
|
{
|
||||||
|
err = ::WSAGetLastError();
|
||||||
|
LOG_ERROR("Failed to listen, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!threads_count)
|
||||||
|
{
|
||||||
|
SYSTEM_INFO si = {0};
|
||||||
|
::GetSystemInfo(&si);
|
||||||
|
threads_count = si.dwNumberOfProcessors + 2;
|
||||||
|
}
|
||||||
|
for(int i = 0; i != threads_count; i++)
|
||||||
|
{
|
||||||
|
boost::thread(boost::bind(&cp_server_impl::worker_thread_member, this));
|
||||||
|
//HANDLE h_thread = threads_helper::create_thread(worker_thread, this);
|
||||||
|
InterlockedIncrement(&m_worker_thread_counter);
|
||||||
|
//::CloseHandle(h_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_PRINT("Numbers of worker threads started: " << threads_count, LOG_LEVEL_1);
|
||||||
|
|
||||||
|
m_stop = false;
|
||||||
|
while(!m_stop)
|
||||||
|
{
|
||||||
|
PROFILE_FUNC("[run_server] main_loop");
|
||||||
|
TIMEVAL tv = {0};
|
||||||
|
tv.tv_sec = 0;
|
||||||
|
tv.tv_usec = 100;
|
||||||
|
fd_set sock_set;
|
||||||
|
sock_set.fd_count = 1;
|
||||||
|
sock_set.fd_array[0] = m_listen_socket;
|
||||||
|
int select_res = 0;
|
||||||
|
{
|
||||||
|
PROFILE_FUNC("[run_server] select");
|
||||||
|
select_res = select(0, &sock_set, &sock_set, NULL, &tv);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(SOCKET_ERROR == select_res)
|
||||||
|
{
|
||||||
|
err = ::WSAGetLastError();
|
||||||
|
LOG_ERROR("Failed to select, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(!select_res)
|
||||||
|
{
|
||||||
|
on_net_idle();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sockaddr_in adr_from = {0};
|
||||||
|
int adr_len = sizeof(adr_from);
|
||||||
|
SOCKET new_sock = INVALID_SOCKET;
|
||||||
|
{
|
||||||
|
PROFILE_FUNC("[run_server] WSAAccept");
|
||||||
|
new_sock = ::WSAAccept(m_listen_socket, (sockaddr *)&adr_from, &adr_len, CPConditionFunc, (DWORD_PTR)this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(INVALID_SOCKET == new_sock)
|
||||||
|
{
|
||||||
|
if(m_stop)
|
||||||
|
break;
|
||||||
|
int err = ::WSAGetLastError();
|
||||||
|
LOG_PRINT("Failed to WSAAccept, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
LOG_PRINT("Accepted connection (new socket=" << new_sock << ")", LOG_LEVEL_2);
|
||||||
|
{
|
||||||
|
PROFILE_FUNC("[run_server] Add new connection");
|
||||||
|
add_new_connection(new_sock, adr_from.sin_addr.s_addr, adr_from.sin_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
LOG_PRINT("Closing connections("<< m_connections.size() << ") and waiting...", LOG_LEVEL_2);
|
||||||
|
m_connections_lock.lock();
|
||||||
|
for(connections_container::iterator it = m_connections.begin(); it != m_connections.end(); it++)
|
||||||
|
{
|
||||||
|
::shutdown(it->second->m_sock, SD_BOTH);
|
||||||
|
::closesocket(it->second->m_sock);
|
||||||
|
}
|
||||||
|
m_connections_lock.unlock();
|
||||||
|
size_t wait_count = 0;
|
||||||
|
while(m_connections.size() && wait_count < 100)
|
||||||
|
{
|
||||||
|
::Sleep(100);
|
||||||
|
wait_count++;
|
||||||
|
}
|
||||||
|
LOG_PRINT("Connections closed OK (wait_count=" << wait_count << ")", LOG_LEVEL_2);
|
||||||
|
|
||||||
|
|
||||||
|
LOG_PRINT("Stopping worker threads("<< m_worker_thread_counter << ").", LOG_LEVEL_2);
|
||||||
|
for(int i = 0; i<m_worker_thread_counter; i++)
|
||||||
|
{
|
||||||
|
::PostQueuedCompletionStatus(m_completion_port, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_count = 0;
|
||||||
|
while(InterlockedCompareExchange(&m_worker_thread_counter, 0, 0) && wait_count < 100)
|
||||||
|
{
|
||||||
|
Sleep(100);
|
||||||
|
wait_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_PRINT("Net Server STOPPED, wait_count = " << wait_count, LOG_LEVEL_1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------
|
||||||
|
template<class TProtocol>
|
||||||
|
bool cp_server_impl<TProtocol>::add_new_connection(SOCKET new_sock, long ip_from, int port_from)
|
||||||
|
{
|
||||||
|
PROFILE_FUNC("[add_new_connection]");
|
||||||
|
|
||||||
|
LOG_PRINT("Add new connection zone: entering lock", LOG_LEVEL_3);
|
||||||
|
m_connections_lock.lock();
|
||||||
|
|
||||||
|
boost::shared_ptr<connection<TProtocol> > ptr;
|
||||||
|
ptr.reset(new connection<TProtocol>(m_config));
|
||||||
|
|
||||||
|
connection<TProtocol>& conn = *ptr.get();
|
||||||
|
m_connections[new_sock] = ptr;
|
||||||
|
LOG_PRINT("Add new connection zone: leaving lock", LOG_LEVEL_3);
|
||||||
|
m_connections_lock.unlock();
|
||||||
|
conn.init_buffers();
|
||||||
|
conn.m_sock = new_sock;
|
||||||
|
conn.context.m_remote_ip = ip_from;
|
||||||
|
conn.context.m_remote_port = port_from;
|
||||||
|
conn.m_completion_port = m_completion_port;
|
||||||
|
{
|
||||||
|
PROFILE_FUNC("[add_new_connection] CreateIoCompletionPort");
|
||||||
|
::CreateIoCompletionPort((HANDLE)new_sock, m_completion_port, (ULONG_PTR)&conn, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//if(NULL == ::CreateIoCompletionPort((HANDLE)new_sock, m_completion_port, (ULONG_PTR)&conn, 0))
|
||||||
|
//{
|
||||||
|
// int err = ::GetLastError();
|
||||||
|
// LOG_PRINT("Failed to CreateIoCompletionPort(associate socket and completion port), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_2);
|
||||||
|
// return false;
|
||||||
|
//}
|
||||||
|
|
||||||
|
conn.m_tprotocol_handler.after_init_connection();
|
||||||
|
{
|
||||||
|
PROFILE_FUNC("[add_new_connection] starting loop");
|
||||||
|
int res = 0;
|
||||||
|
while(true)//res!=SOCKET_ERROR)
|
||||||
|
{
|
||||||
|
PROFILE_FUNC("[add_new_connection] in loop time");
|
||||||
|
conn.m_precv_data->TotalBuffBytes = LEVIN_DEFAULT_DATA_BUFF_SIZE;
|
||||||
|
ZeroMemory(&conn.m_precv_data->m_overlapped, sizeof(OVERLAPPED));
|
||||||
|
conn.m_precv_data->DataBuf.len = conn.m_precv_data->TotalBuffBytes;
|
||||||
|
conn.m_precv_data->DataBuf.buf = conn.m_precv_data->Buffer;
|
||||||
|
conn.m_precv_data->m_op_type = op_type_recv;
|
||||||
|
InterlockedExchange(&conn.m_precv_data->m_is_in_use, 1);
|
||||||
|
DWORD bytes_recvd = 0;
|
||||||
|
DWORD flags = 0;
|
||||||
|
|
||||||
|
::InterlockedExchange(&conn.m_precv_data->m_is_in_use, 1);
|
||||||
|
{
|
||||||
|
PROFILE_FUNC("[add_new_connection] ::WSARecv");
|
||||||
|
res = ::WSARecv(conn.m_sock, &(conn.m_precv_data->DataBuf), 1, &bytes_recvd , &flags, &(conn.m_precv_data->m_overlapped), NULL);
|
||||||
|
}
|
||||||
|
if(res == SOCKET_ERROR )
|
||||||
|
{
|
||||||
|
int err = ::WSAGetLastError();
|
||||||
|
if(WSA_IO_PENDING == err )
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
LOG_ERROR("BIG FAIL: WSARecv error code not correct, res=" << res << " last_err=" << err << " " << log_space::get_win32_err_descr(err));
|
||||||
|
::InterlockedExchange(&conn.m_precv_data->m_is_in_use, 0);
|
||||||
|
conn.query_shutdown();
|
||||||
|
//shutdown_connection(&conn);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
break;
|
||||||
|
/*else if(0 == res)
|
||||||
|
{
|
||||||
|
if(!bytes_recvd)
|
||||||
|
{
|
||||||
|
PROFILE_FUNC("[add_new_connection] shutdown_connection");
|
||||||
|
::InterlockedExchange(&conn.m_precv_data->m_is_in_use, 0);
|
||||||
|
conn.query_shutdown();
|
||||||
|
//shutdown_connection(&conn);
|
||||||
|
break;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
PROFILE_FUNC("[add_new_connection] handle_recv");
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------
|
||||||
|
template<class TProtocol>
|
||||||
|
bool cp_server_impl<TProtocol>::deinit_server()
|
||||||
|
{
|
||||||
|
if(!m_initialized)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if(INVALID_SOCKET != m_listen_socket)
|
||||||
|
{
|
||||||
|
shutdown(m_listen_socket, SD_BOTH);
|
||||||
|
int res = closesocket(m_listen_socket);
|
||||||
|
if(SOCKET_ERROR == res)
|
||||||
|
{
|
||||||
|
int err = ::WSAGetLastError();
|
||||||
|
LOG_ERROR("Failed to closesocket(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
|
||||||
|
}
|
||||||
|
m_listen_socket = INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
int res = ::WSACleanup();
|
||||||
|
if(SOCKET_ERROR == res)
|
||||||
|
{
|
||||||
|
int err = ::WSAGetLastError();
|
||||||
|
LOG_ERROR("Failed to WSACleanup(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
|
||||||
|
}
|
||||||
|
m_initialized = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------
|
||||||
|
template<class TProtocol>
|
||||||
|
bool cp_server_impl<TProtocol>::send_stop_signal()
|
||||||
|
{
|
||||||
|
::InterlockedExchange(&m_stop, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------
|
||||||
|
template<class TProtocol>
|
||||||
|
bool cp_server_impl<TProtocol>::is_stop_signal()
|
||||||
|
{
|
||||||
|
return m_stop?true:false;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,184 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 <boost/lexical_cast.hpp>
|
||||||
|
#include <boost/regex.hpp>
|
||||||
|
|
||||||
|
#include "string_tools.h"
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
|
||||||
|
enum http_method{
|
||||||
|
http_method_get,
|
||||||
|
http_method_post,
|
||||||
|
http_method_put,
|
||||||
|
http_method_head,
|
||||||
|
http_method_etc,
|
||||||
|
http_method_unknown
|
||||||
|
};
|
||||||
|
|
||||||
|
enum http_content_type
|
||||||
|
{
|
||||||
|
http_content_type_text_html,
|
||||||
|
http_content_type_image_gif,
|
||||||
|
http_content_type_other,
|
||||||
|
http_content_type_not_set
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::list<std::pair<std::string, std::string> > fields_list;
|
||||||
|
|
||||||
|
inline
|
||||||
|
std::string get_value_from_fields_list(const std::string& param_name, const net_utils::http::fields_list& fields)
|
||||||
|
{
|
||||||
|
fields_list::const_iterator it = fields.begin();
|
||||||
|
for(; it != fields.end(); it++)
|
||||||
|
if(!string_tools::compare_no_case(param_name, it->first))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(it==fields.end())
|
||||||
|
return std::string();
|
||||||
|
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline
|
||||||
|
std::string get_value_from_uri_line(const std::string& param_name, const std::string& uri)
|
||||||
|
{
|
||||||
|
std::string buff = "([\\?|&])";
|
||||||
|
buff += param_name + "=([^&]*)";
|
||||||
|
boost::regex match_param(buff.c_str(), boost::regex::icase | boost::regex::normal);
|
||||||
|
boost::smatch result;
|
||||||
|
if(boost::regex_search(uri, result, match_param, boost::match_default) && result[0].matched)
|
||||||
|
{
|
||||||
|
return result[2];
|
||||||
|
}
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct http_header_info
|
||||||
|
{
|
||||||
|
std::string m_connection; //"Connection:"
|
||||||
|
std::string m_referer; //"Referer:"
|
||||||
|
std::string m_content_length; //"Content-Length:"
|
||||||
|
std::string m_content_type; //"Content-Type:"
|
||||||
|
std::string m_transfer_encoding;//"Transfer-Encoding:"
|
||||||
|
std::string m_content_encoding; //"Content-Encoding:"
|
||||||
|
std::string m_host; //"Host:"
|
||||||
|
std::string m_cookie; //"Cookie:"
|
||||||
|
fields_list m_etc_fields;
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
m_connection.clear();
|
||||||
|
m_referer.clear();
|
||||||
|
m_content_length.clear();
|
||||||
|
m_content_type.clear();
|
||||||
|
m_transfer_encoding.clear();
|
||||||
|
m_content_encoding.clear();
|
||||||
|
m_host.clear();
|
||||||
|
m_cookie.clear();
|
||||||
|
m_etc_fields.clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct uri_content
|
||||||
|
{
|
||||||
|
std::string m_path;
|
||||||
|
std::string m_query;
|
||||||
|
std::string m_fragment;
|
||||||
|
std::list<std::pair<std::string, std::string> > m_query_params;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct url_content
|
||||||
|
{
|
||||||
|
std::string schema;
|
||||||
|
std::string host;
|
||||||
|
std::string uri;
|
||||||
|
boost::uint64_t port;
|
||||||
|
uri_content m_uri_content;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct http_request_info
|
||||||
|
{
|
||||||
|
http_request_info():m_http_method(http_method_unknown),
|
||||||
|
m_http_ver_hi(0),
|
||||||
|
m_http_ver_lo(0),
|
||||||
|
m_have_to_block(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
http_method m_http_method;
|
||||||
|
std::string m_URI;
|
||||||
|
std::string m_http_method_str;
|
||||||
|
std::string m_full_request_str;
|
||||||
|
std::string m_replace_html;
|
||||||
|
std::string m_request_head;
|
||||||
|
int m_http_ver_hi;
|
||||||
|
int m_http_ver_lo;
|
||||||
|
bool m_have_to_block;
|
||||||
|
http_header_info m_header_info;
|
||||||
|
uri_content m_uri_content;
|
||||||
|
size_t m_full_request_buf_size;
|
||||||
|
std::string m_body;
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
this->~http_request_info();
|
||||||
|
new(this) http_request_info();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct http_response_info
|
||||||
|
{
|
||||||
|
int m_response_code;
|
||||||
|
std::string m_response_comment;
|
||||||
|
fields_list m_additional_fields;
|
||||||
|
std::string m_body;
|
||||||
|
std::string m_mime_tipe;
|
||||||
|
http_header_info m_header_info;
|
||||||
|
int m_http_ver_hi;// OUT paramter only
|
||||||
|
int m_http_ver_lo;// OUT paramter only
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
this->~http_response_info();
|
||||||
|
new(this) http_response_info();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,875 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 <boost/shared_ptr.hpp>
|
||||||
|
#include <boost/regex.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
//#include <mbstring.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include "net_helper.h"
|
||||||
|
#include "http_client_base.h"
|
||||||
|
|
||||||
|
#ifdef HTTP_ENABLE_GZIP
|
||||||
|
#include "gzip_encoding.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "string_tools.h"
|
||||||
|
#include "reg_exp_definer.h"
|
||||||
|
#include "http_base.h"
|
||||||
|
#include "to_nonconst_iterator.h"
|
||||||
|
#include "net_parse_helpers.h"
|
||||||
|
|
||||||
|
//#include "shlwapi.h"
|
||||||
|
|
||||||
|
//#pragma comment(lib, "shlwapi.lib")
|
||||||
|
|
||||||
|
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<int,int>(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<int,int>(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)
|
||||||
|
{
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
|
||||||
|
for(size_t i = 0; i!= uri.size(); i++)
|
||||||
|
{
|
||||||
|
result += convert(uri[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
|
||||||
|
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;
|
||||||
|
std::string m_header_cache;
|
||||||
|
http_response_info m_response_info;
|
||||||
|
size_t m_len_in_summary;
|
||||||
|
size_t m_len_in_remain;
|
||||||
|
//std::string* m_ptarget_buffer;
|
||||||
|
boost::shared_ptr<i_sub_handler> m_pcontent_encoding_handler;
|
||||||
|
reciev_machine_state m_state;
|
||||||
|
chunked_state m_chunked_state;
|
||||||
|
std::string m_chunked_cache;
|
||||||
|
critical_section m_lock;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void set_host_name(const std::string& name)
|
||||||
|
{
|
||||||
|
CRITICAL_REGION_LOCAL(m_lock);
|
||||||
|
m_host_buff = name;
|
||||||
|
}
|
||||||
|
bool connect(const std::string& host, int port, unsigned int timeout)
|
||||||
|
{
|
||||||
|
return connect(host, std::to_string(port), timeout);
|
||||||
|
}
|
||||||
|
bool connect(const std::string& host, const std::string& port, unsigned int timeout)
|
||||||
|
{
|
||||||
|
CRITICAL_REGION_LOCAL(m_lock);
|
||||||
|
m_host_buff = host;
|
||||||
|
m_port = port;
|
||||||
|
m_timeout = timeout;
|
||||||
|
|
||||||
|
return m_net_client.connect(host, port, timeout, timeout);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
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, m_timeout))
|
||||||
|
{
|
||||||
|
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<std::string>(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;
|
||||||
|
}
|
||||||
|
if(!recv_buffer.size())
|
||||||
|
{
|
||||||
|
//connection is going to be closed
|
||||||
|
if(reciev_machine_state_body_connection_close != m_state)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
|
||||||
|
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();
|
||||||
|
m_pcontent_encoding_handler->update_in(recv_buff);
|
||||||
|
|
||||||
|
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, size_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, size_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:"<<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<int>(result[1]);
|
||||||
|
m_response_info.m_http_ver_lo = boost::lexical_cast<int>(result[2]);
|
||||||
|
m_response_info.m_response_code = boost::lexical_cast<int>(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_error;
|
||||||
|
LOG_PRINT("Undefinded transfer type, consider http_body_transfer_connection_close method.", 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
//inline
|
||||||
|
template<class t_transport>
|
||||||
|
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())
|
||||||
|
{
|
||||||
|
CHECK_AND_ASSERT_MES(res, false, "failed to parse url: " << url);
|
||||||
|
|
||||||
|
if(!u_c.port)
|
||||||
|
u_c.port = 80;//default for http
|
||||||
|
|
||||||
|
res = tr.connect(u_c.host, static_cast<int>(u_c.port), timeout);
|
||||||
|
CHECK_AND_ASSERT_MES(res, false, "failed to connect " << u_c.host << ":" << u_c.port);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tr.invoke(u_c.uri, method, body, ppresponse_info, additional_params);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
|
||||||
|
// Copyright (c) 2006-2013, 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 "storages/serializeble_struct_helper.h"
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
template<class TArg, class TResult, class TTransport>
|
||||||
|
bool invoke_http_json_remote_command(const std::string& url, TArg& out_struct, TResult& result_struct, TTransport& transport, unsigned int timeout = 5000, const std::string& method = "GET")
|
||||||
|
{
|
||||||
|
std::string req_param;
|
||||||
|
StorageNamed::InMemStorageSpace::json::store_t_to_json(out_struct, req_param);
|
||||||
|
|
||||||
|
const http_response_info* pri = NULL;
|
||||||
|
if(!invoke_request(url, transport, timeout, &pri, method, req_param))
|
||||||
|
{
|
||||||
|
LOG_PRINT_L1("Failed to invoke http request to " << url);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!pri->m_response_code)
|
||||||
|
{
|
||||||
|
LOG_PRINT_L1("Failed to invoke http request to " << url << ", internal error (null response ptr)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pri->m_response_code != 200)
|
||||||
|
{
|
||||||
|
LOG_PRINT_L1("Failed to invoke http request to " << url << ", wrong response code: " << pri->m_response_code);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return StorageNamed::InMemStorageSpace::json::load_t_from_json(result_struct, pri->m_body);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template<class TArg, class TResult, class TTransport>
|
||||||
|
bool invoke_http_bin_remote_command(const std::string& url, TArg& out_struct, TResult& result_struct, TTransport& transport, unsigned int timeout = 5000, const std::string& method = "GET")
|
||||||
|
{
|
||||||
|
std::string req_param;
|
||||||
|
epee::StorageNamed::save_struct_as_storage_to_buff(out_struct, req_param);
|
||||||
|
|
||||||
|
const http_response_info* pri = NULL;
|
||||||
|
if(!invoke_request(url, transport, timeout, &pri, method, req_param))
|
||||||
|
{
|
||||||
|
LOG_PRINT_L1("Failed to invoke http request to " << url);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!pri->m_response_code)
|
||||||
|
{
|
||||||
|
LOG_PRINT_L1("Failed to invoke http request to " << url << ", internal error (null response ptr)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pri->m_response_code != 200)
|
||||||
|
{
|
||||||
|
LOG_PRINT_L1("Failed to invoke http request to " << url << ", wrong response code: " << pri->m_response_code);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return epee::StorageNamed::load_struct_from_storage_buff(result_struct, pri->m_body);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
// Copyright (c) 2006-2013, 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
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
struct i_sub_handler
|
||||||
|
{
|
||||||
|
virtual ~i_sub_handler(){}
|
||||||
|
|
||||||
|
virtual bool update_in( std::string& piece_of_transfer)=0;
|
||||||
|
virtual void stop(std::string& OUT collect_remains)=0;
|
||||||
|
virtual bool update_and_stop(std::string& OUT collect_remains, bool& is_changed)
|
||||||
|
{
|
||||||
|
is_changed = true;
|
||||||
|
bool res = this->update_in(collect_remains);
|
||||||
|
if(res)
|
||||||
|
this->stop(collect_remains);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct i_target_handler
|
||||||
|
{
|
||||||
|
virtual ~i_target_handler(){}
|
||||||
|
virtual bool handle_target_data( std::string& piece_of_transfer)=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class do_nothing_sub_handler: public i_sub_handler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
do_nothing_sub_handler(i_target_handler* powner_filter):m_powner_filter(powner_filter)
|
||||||
|
{}
|
||||||
|
virtual bool update_in( std::string& piece_of_transfer)
|
||||||
|
{
|
||||||
|
return m_powner_filter->handle_target_data(piece_of_transfer);
|
||||||
|
}
|
||||||
|
virtual void stop(std::string& OUT collect_remains)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
i_target_handler* m_powner_filter;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,177 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 <wininet.h>
|
||||||
|
#include <atlutil.h>
|
||||||
|
#pragma comment(lib, "Wininet.lib")
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
inline
|
||||||
|
bool http_ssl_invoke(const std::string& url, const std::string usr, const std::string psw, std::string& http_response_body, bool use_post = false)
|
||||||
|
{
|
||||||
|
bool final_res = false;
|
||||||
|
|
||||||
|
ATL::CUrl url_obj;
|
||||||
|
BOOL crack_rss = url_obj.CrackUrl(string_encoding::convert_to_t<std::basic_string<TCHAR> >(url).c_str());
|
||||||
|
|
||||||
|
HINTERNET hinet = ::InternetOpenA(SHARED_JOBSCOMMON_HTTP_AGENT, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
|
||||||
|
if(!hinet)
|
||||||
|
{
|
||||||
|
int err = ::GetLastError();
|
||||||
|
LOG_PRINT("Failed to call InternetOpenA, \nError: " << err << " " << log_space::get_win32_err_descr(err), LOG_LEVEL_0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD dwFlags = 0;
|
||||||
|
DWORD dwBuffLen = sizeof(dwFlags);
|
||||||
|
|
||||||
|
if(usr.size())
|
||||||
|
{
|
||||||
|
dwFlags |= INTERNET_FLAG_IGNORE_CERT_CN_INVALID|INTERNET_FLAG_IGNORE_CERT_DATE_INVALID|
|
||||||
|
INTERNET_FLAG_PRAGMA_NOCACHE | SECURITY_FLAG_IGNORE_UNKNOWN_CA|INTERNET_FLAG_SECURE;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
dwFlags |= INTERNET_FLAG_PRAGMA_NOCACHE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int port = url_obj.GetPortNumber();
|
||||||
|
BOOL res = FALSE;
|
||||||
|
|
||||||
|
HINTERNET hsession = ::InternetConnectA(hinet, string_encoding::convert_to_ansii(url_obj.GetHostName()).c_str(), port/*INTERNET_DEFAULT_HTTPS_PORT*/, usr.c_str(), psw.c_str(), INTERNET_SERVICE_HTTP, dwFlags, NULL);
|
||||||
|
if(hsession)
|
||||||
|
{
|
||||||
|
const std::string uri = string_encoding::convert_to_ansii(url_obj.GetUrlPath()) + string_encoding::convert_to_ansii(url_obj.GetExtraInfo());
|
||||||
|
|
||||||
|
HINTERNET hrequest = ::HttpOpenRequestA(hsession, use_post?"POST":NULL, uri.c_str(), NULL, NULL,NULL, dwFlags, NULL);
|
||||||
|
if(hrequest)
|
||||||
|
{
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
res = ::HttpSendRequestA(hrequest, NULL, 0, NULL, 0);
|
||||||
|
if(!res)
|
||||||
|
{
|
||||||
|
//ERROR_INTERNET_INVALID_CA 45
|
||||||
|
//ERROR_INTERNET_INVALID_URL (INTERNET_ERROR_BASE + 5)
|
||||||
|
int err = ::GetLastError();
|
||||||
|
LOG_PRINT("Failed to call HttpSendRequestA, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD code = 0;
|
||||||
|
DWORD buf_len = sizeof(code);
|
||||||
|
DWORD index = 0;
|
||||||
|
res = ::HttpQueryInfo(hrequest, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &code, &buf_len, &index);
|
||||||
|
if(!res)
|
||||||
|
{
|
||||||
|
//ERROR_INTERNET_INVALID_CA 45
|
||||||
|
//ERROR_INTERNET_INVALID_URL (INTERNET_ERROR_BASE + 5)
|
||||||
|
int err = ::GetLastError();
|
||||||
|
LOG_PRINT("Failed to call HttpQueryInfo, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(code < 200 || code > 299)
|
||||||
|
{
|
||||||
|
LOG_PRINT("Wrong server response, HttpQueryInfo returned statuse code" << code , LOG_LEVEL_0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char buff[100000] = {0};
|
||||||
|
DWORD readed = 0;
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
res = ::InternetReadFile(hrequest, buff, sizeof(buff), &readed);
|
||||||
|
if(!res)
|
||||||
|
{
|
||||||
|
int err = ::GetLastError();
|
||||||
|
LOG_PRINT("Failed to call InternetReadFile, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(readed)
|
||||||
|
{
|
||||||
|
http_response_body.append(buff, readed);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!res)
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
//we success
|
||||||
|
final_res = true;
|
||||||
|
|
||||||
|
res = ::InternetCloseHandle(hrequest);
|
||||||
|
if(!res)
|
||||||
|
{
|
||||||
|
int err = ::GetLastError();
|
||||||
|
LOG_PRINT("Failed to call InternetCloseHandle, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//ERROR_INTERNET_INVALID_CA
|
||||||
|
int err = ::GetLastError();
|
||||||
|
LOG_PRINT("Failed to call InternetOpenUrlA, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = ::InternetCloseHandle(hsession);
|
||||||
|
if(!res)
|
||||||
|
{
|
||||||
|
int err = ::GetLastError();
|
||||||
|
LOG_PRINT("Failed to call InternetCloseHandle, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0);
|
||||||
|
}
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
int err = ::GetLastError();
|
||||||
|
LOG_PRINT("Failed to call InternetConnectA(" << string_encoding::convert_to_ansii(url_obj.GetHostName()) << ", port " << port << " \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
res = ::InternetCloseHandle(hinet);
|
||||||
|
if(!res)
|
||||||
|
{
|
||||||
|
int err = ::GetLastError();
|
||||||
|
LOG_PRINT("Failed to call InternetCloseHandle, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0);
|
||||||
|
}
|
||||||
|
return final_res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,209 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _HTTP_SERVER_H_
|
||||||
|
#define _HTTP_SERVER_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "net_utils_base.h"
|
||||||
|
#include "to_nonconst_iterator.h"
|
||||||
|
#include "http_base.h"
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
struct http_server_config
|
||||||
|
{
|
||||||
|
std::string m_folder;
|
||||||
|
critical_section m_lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
class simple_http_connection_handler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef net_utils::connection_context_base connection_context;
|
||||||
|
typedef http_server_config config_type;
|
||||||
|
|
||||||
|
simple_http_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config);
|
||||||
|
virtual ~simple_http_connection_handler(){}
|
||||||
|
|
||||||
|
bool release_protocol()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool thread_init()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool thread_deinit()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool after_init_connection()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
virtual bool handle_recv(const void* ptr, size_t cb);
|
||||||
|
virtual bool handle_request(const http::http_request_info& query_info, http_response_info& response);
|
||||||
|
|
||||||
|
|
||||||
|
//temporary here
|
||||||
|
//bool parse_uri(const std::string uri, uri_content& content);
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum machine_state{
|
||||||
|
http_state_retriving_comand_line,
|
||||||
|
http_state_retriving_header,
|
||||||
|
http_state_retriving_body,
|
||||||
|
http_state_connection_close,
|
||||||
|
http_state_error
|
||||||
|
};
|
||||||
|
|
||||||
|
enum body_transfer_type{
|
||||||
|
http_body_transfer_chunked,
|
||||||
|
http_body_transfer_measure,//mean "Content-Length" valid
|
||||||
|
http_body_transfer_chunked_instead_measure,
|
||||||
|
http_body_transfer_connection_close,
|
||||||
|
http_body_transfer_multipart,
|
||||||
|
http_body_transfer_undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
bool handle_buff_in(std::string& buf);
|
||||||
|
|
||||||
|
bool analize_cached_request_header_and_invoke_state(size_t pos);
|
||||||
|
|
||||||
|
bool handle_invoke_query_line();
|
||||||
|
bool parse_cached_header(http_header_info& body_info, const std::string& m_cache_to_process, size_t pos);
|
||||||
|
std::string::size_type match_end_of_header(const std::string& buf);
|
||||||
|
bool get_len_from_content_lenght(const std::string& str, size_t& len);
|
||||||
|
bool handle_retriving_query_body();
|
||||||
|
bool handle_query_measure();
|
||||||
|
bool set_ready_state();
|
||||||
|
bool slash_to_back_slash(std::string& str);
|
||||||
|
std::string get_file_mime_tipe(const std::string& path);
|
||||||
|
std::string get_response_header(const http_response_info& response);
|
||||||
|
|
||||||
|
//major function
|
||||||
|
inline bool handle_request_and_send_response(const http::http_request_info& query_info);
|
||||||
|
|
||||||
|
|
||||||
|
std::string get_not_found_response_body(const std::string& URI);
|
||||||
|
|
||||||
|
std::string m_root_path;
|
||||||
|
std::string m_cache;
|
||||||
|
machine_state m_state;
|
||||||
|
body_transfer_type m_body_transfer_type;
|
||||||
|
bool m_is_stop_handling;
|
||||||
|
http::http_request_info m_query_info;
|
||||||
|
size_t m_len_summary, m_len_remain;
|
||||||
|
config_type& m_config;
|
||||||
|
bool m_want_close;
|
||||||
|
protected:
|
||||||
|
i_service_endpoint* m_psnd_hndlr;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct i_http_server_handler
|
||||||
|
{
|
||||||
|
virtual ~i_http_server_handler(){}
|
||||||
|
virtual bool handle_http_request(const http_request_info& query_info, http_response_info& response, const net_utils::connection_context_base& m_conn_context)=0;
|
||||||
|
virtual bool init_server_thread(){return true;}
|
||||||
|
virtual bool deinit_server_thread(){return true;}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct custum_handler_config: public http_server_config
|
||||||
|
{
|
||||||
|
i_http_server_handler* m_phandler;
|
||||||
|
};
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
class http_custom_handler: public simple_http_connection_handler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef custum_handler_config config_type;
|
||||||
|
|
||||||
|
http_custom_handler(i_service_endpoint* psnd_hndlr, config_type& config, const net_utils::connection_context_base& conn_context):simple_http_connection_handler(psnd_hndlr, config),
|
||||||
|
m_config(config),
|
||||||
|
m_conn_context(conn_context)
|
||||||
|
{}
|
||||||
|
inline bool handle_request(const http_request_info& query_info, http_response_info& response)
|
||||||
|
{
|
||||||
|
CHECK_AND_ASSERT_MES(m_config.m_phandler, false, "m_config.m_phandler is NULL!!!!");
|
||||||
|
//fill with default values
|
||||||
|
response.m_mime_tipe = "text/plain";
|
||||||
|
response.m_response_code = 200;
|
||||||
|
response.m_response_comment = "OK";
|
||||||
|
response.m_body.clear();
|
||||||
|
return m_config.m_phandler->handle_http_request(query_info, response, m_conn_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool thread_init()
|
||||||
|
{
|
||||||
|
return m_config.m_phandler->init_server_thread();;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool thread_deinit()
|
||||||
|
{
|
||||||
|
return m_config.m_phandler->deinit_server_thread();
|
||||||
|
}
|
||||||
|
void handle_qued_callback()
|
||||||
|
{}
|
||||||
|
bool after_init_connection()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
//simple_http_connection_handler::config_type m_stub_config;
|
||||||
|
config_type& m_config;
|
||||||
|
const net_utils::connection_context_base& m_conn_context;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "http_protocol_handler.inl"
|
||||||
|
|
||||||
|
#endif //_HTTP_SERVER_H_
|
|
@ -0,0 +1,664 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#include <boost/regex.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
#include "http_protocol_handler.h"
|
||||||
|
#include "reg_exp_definer.h"
|
||||||
|
#include "string_tools.h"
|
||||||
|
#include "file_io_utils.h"
|
||||||
|
#include "net_parse_helpers.h"
|
||||||
|
|
||||||
|
#define HTTP_MAX_URI_LEN 9000
|
||||||
|
#define HTTP_MAX_HEADER_LEN 100000
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
|
||||||
|
struct multipart_entry
|
||||||
|
{
|
||||||
|
std::list<std::pair<std::string, std::string> > m_etc_header_fields;
|
||||||
|
std::string m_content_disposition;
|
||||||
|
std::string m_content_type;
|
||||||
|
std::string m_body;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool match_boundary(const std::string& content_type, std::string& boundary)
|
||||||
|
{
|
||||||
|
STATIC_REGEXP_EXPR_1(rexp_match_boundary, "boundary=(.*?)(($)|([;\\s,]))", boost::regex::icase | boost::regex::normal);
|
||||||
|
// 1
|
||||||
|
boost::smatch result;
|
||||||
|
if(boost::regex_search(content_type, result, rexp_match_boundary, boost::match_default) && result[0].matched)
|
||||||
|
{
|
||||||
|
boundary = result[1];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool parse_header(std::string::const_iterator it_begin, std::string::const_iterator it_end, multipart_entry& entry)
|
||||||
|
{
|
||||||
|
STATIC_REGEXP_EXPR_1(rexp_mach_field,
|
||||||
|
"\n?((Content-Disposition)|(Content-Type)"
|
||||||
|
// 12 3
|
||||||
|
"|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]",
|
||||||
|
//4 56 7
|
||||||
|
boost::regex::icase | boost::regex::normal);
|
||||||
|
|
||||||
|
boost::smatch result;
|
||||||
|
std::string::const_iterator it_current_bound = it_begin;
|
||||||
|
std::string::const_iterator it_end_bound = it_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 = 6;
|
||||||
|
const size_t field_etc_name = 4;
|
||||||
|
|
||||||
|
int i = 2; //start position = 2
|
||||||
|
if(result[i++].matched)//"Content-Disposition"
|
||||||
|
entry.m_content_disposition = result[field_val];
|
||||||
|
else if(result[i++].matched)//"Content-Type"
|
||||||
|
entry.m_content_type = result[field_val];
|
||||||
|
else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!)
|
||||||
|
entry.m_etc_header_fields.push_back(std::pair<std::string, std::string>(result[field_etc_name], result[field_val]));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_ERROR("simple_http_connection_handler::parse_header() not matched last entry in:"<<std::string(it_current_bound, it_end));
|
||||||
|
}
|
||||||
|
|
||||||
|
it_current_bound = result[(int)result.size()-1].first;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool handle_part_of_multipart(std::string::const_iterator it_begin, std::string::const_iterator it_end, multipart_entry& entry)
|
||||||
|
{
|
||||||
|
std::string end_str = "\r\n\r\n";
|
||||||
|
std::string::const_iterator end_header_it = std::search(it_begin, it_end, end_str.begin(), end_str.end());
|
||||||
|
if(end_header_it == it_end)
|
||||||
|
{
|
||||||
|
//header not matched
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!parse_header(it_begin, end_header_it+4, entry))
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to parse header:" << std::string(it_begin, end_header_it+2));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.m_body.assign(end_header_it+4, it_end);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool parse_multipart_body(const std::string& content_type, const std::string& body, std::list<multipart_entry>& out_values)
|
||||||
|
{
|
||||||
|
//bool res = file_io_utils::load_file_to_string("C:\\public\\multupart_data", body);
|
||||||
|
|
||||||
|
std::string boundary;
|
||||||
|
if(!match_boundary(content_type, boundary))
|
||||||
|
{
|
||||||
|
LOG_PRINT("Failed to match boundary in content type: " << content_type, LOG_LEVEL_0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boundary+="\r\n";
|
||||||
|
bool is_stop = false;
|
||||||
|
bool first_step = true;
|
||||||
|
|
||||||
|
std::string::const_iterator it_begin = body.begin();
|
||||||
|
std::string::const_iterator it_end;
|
||||||
|
while(!is_stop)
|
||||||
|
{
|
||||||
|
std::string::size_type pos = body.find(boundary, std::distance(body.begin(), it_begin));
|
||||||
|
|
||||||
|
if(std::string::npos == pos)
|
||||||
|
{
|
||||||
|
is_stop = true;
|
||||||
|
boundary.erase(boundary.size()-2, 2);
|
||||||
|
boundary+= "--";
|
||||||
|
pos = body.find(boundary, std::distance(body.begin(), it_begin));
|
||||||
|
if(std::string::npos == pos)
|
||||||
|
{
|
||||||
|
LOG_PRINT("Error: Filed to match closing multipart tag", LOG_LEVEL_0);
|
||||||
|
it_end = body.end();
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
it_end = body.begin() + pos;
|
||||||
|
}
|
||||||
|
}else
|
||||||
|
it_end = body.begin() + pos;
|
||||||
|
|
||||||
|
|
||||||
|
if(first_step && !is_stop)
|
||||||
|
{
|
||||||
|
first_step = false;
|
||||||
|
it_begin = it_end + boundary.size();
|
||||||
|
std::string temp = "\r\n--";
|
||||||
|
boundary = temp + boundary;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
out_values.push_back(multipart_entry());
|
||||||
|
if(!handle_part_of_multipart(it_begin, it_end, out_values.back()))
|
||||||
|
{
|
||||||
|
LOG_PRINT("Failed to handle_part_of_multipart", LOG_LEVEL_0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
it_begin = it_end + boundary.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
simple_http_connection_handler::simple_http_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config):
|
||||||
|
m_state(http_state_retriving_comand_line),
|
||||||
|
m_body_transfer_type(http_body_transfer_undefined),
|
||||||
|
m_is_stop_handling(false),
|
||||||
|
m_len_summary(0),
|
||||||
|
m_len_remain(0),
|
||||||
|
m_config(config),
|
||||||
|
m_want_close(false),
|
||||||
|
m_psnd_hndlr(psnd_hndlr)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------
|
||||||
|
inline bool simple_http_connection_handler::set_ready_state()
|
||||||
|
{
|
||||||
|
m_is_stop_handling = false;
|
||||||
|
m_state = http_state_retriving_comand_line;
|
||||||
|
m_body_transfer_type = http_body_transfer_undefined;
|
||||||
|
m_query_info.clear();
|
||||||
|
m_len_summary = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------
|
||||||
|
inline bool simple_http_connection_handler::handle_recv(const void* ptr, size_t cb)
|
||||||
|
{
|
||||||
|
std::string buf((const char*)ptr, cb);
|
||||||
|
//LOG_PRINT_L0("HTTP_RECV: " << ptr << "\r\n" << buf);
|
||||||
|
//file_io_utils::save_string_to_file(string_tools::get_current_module_folder() + "/" + boost::lexical_cast<std::string>(ptr), std::string((const char*)ptr, cb));
|
||||||
|
|
||||||
|
bool res = handle_buff_in(buf);
|
||||||
|
if(m_want_close/*m_state == http_state_connection_close || m_state == http_state_error*/)
|
||||||
|
return false;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------
|
||||||
|
inline bool simple_http_connection_handler::handle_buff_in(std::string& buf)
|
||||||
|
{
|
||||||
|
|
||||||
|
if(m_cache.size())
|
||||||
|
m_cache += buf;
|
||||||
|
else
|
||||||
|
m_cache.swap(buf);
|
||||||
|
|
||||||
|
m_is_stop_handling = false;
|
||||||
|
while(!m_is_stop_handling)
|
||||||
|
{
|
||||||
|
switch(m_state)
|
||||||
|
{
|
||||||
|
case http_state_retriving_comand_line:
|
||||||
|
//The HTTP protocol does not place any a priori limit on the length of a URI. (c)RFC2616
|
||||||
|
//but we forebly restirct it len to HTTP_MAX_URI_LEN to make it more safely
|
||||||
|
if(!m_cache.size())
|
||||||
|
break;
|
||||||
|
|
||||||
|
//check_and_handle_fake_response();
|
||||||
|
if((m_cache[0] == '\r' || m_cache[0] == '\n'))
|
||||||
|
{
|
||||||
|
//some times it could be that before query line cold be few line breaks
|
||||||
|
//so we have to be calm without panic with assers
|
||||||
|
m_cache.erase(0, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(std::string::npos != m_cache.find('\n', 0))
|
||||||
|
handle_invoke_query_line();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_is_stop_handling = true;
|
||||||
|
if(m_cache.size() > HTTP_MAX_URI_LEN)
|
||||||
|
{
|
||||||
|
LOG_ERROR("simple_http_connection_handler::handle_buff_out: Too long URI line");
|
||||||
|
m_state = http_state_error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case http_state_retriving_header:
|
||||||
|
{
|
||||||
|
std::string::size_type pos = match_end_of_header(m_cache);
|
||||||
|
if(std::string::npos == pos)
|
||||||
|
{
|
||||||
|
m_is_stop_handling = true;
|
||||||
|
if(m_cache.size() > HTTP_MAX_HEADER_LEN)
|
||||||
|
{
|
||||||
|
LOG_ERROR("simple_http_connection_handler::handle_buff_in: Too long header area");
|
||||||
|
m_state = http_state_error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
analize_cached_request_header_and_invoke_state(pos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case http_state_retriving_body:
|
||||||
|
return handle_retriving_query_body();
|
||||||
|
case http_state_connection_close:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
LOG_ERROR("simple_http_connection_handler::handle_char_out: Wrong state: " << m_state);
|
||||||
|
return false;
|
||||||
|
case http_state_error:
|
||||||
|
LOG_ERROR("simple_http_connection_handler::handle_char_out: Error state!!!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!m_cache.size())
|
||||||
|
m_is_stop_handling = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------
|
||||||
|
inline bool analize_http_method(const boost::smatch& result, http::http_method& method, int& http_ver_major, int& http_ver_minor)
|
||||||
|
{
|
||||||
|
CHECK_AND_ASSERT_MES(result[0].matched, false, "simple_http_connection_handler::analize_http_method() assert failed...");
|
||||||
|
http_ver_major = boost::lexical_cast<int>(result[11]);
|
||||||
|
http_ver_minor = boost::lexical_cast<int>(result[12]);
|
||||||
|
if(result[4].matched)
|
||||||
|
method = http::http_method_get;
|
||||||
|
else if(result[5].matched)
|
||||||
|
method = http::http_method_head;
|
||||||
|
else if(result[6].matched)
|
||||||
|
method = http::http_method_post;
|
||||||
|
else if(result[7].matched)
|
||||||
|
method = http::http_method_put;
|
||||||
|
else
|
||||||
|
method = http::http_method_etc;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------
|
||||||
|
inline bool simple_http_connection_handler::handle_invoke_query_line()
|
||||||
|
{
|
||||||
|
LOG_FRAME("simple_http_connection_handler::handle_recognize_protocol_out(*)", LOG_LEVEL_3);
|
||||||
|
|
||||||
|
STATIC_REGEXP_EXPR_1(rexp_match_command_line, "^(((OPTIONS)|(GET)|(HEAD)|(POST)|(PUT)|(DELETE)|(TRACE)) (\\S+) HTTP/(\\d+).(\\d+))\r?\n", boost::regex::icase | boost::regex::normal);
|
||||||
|
// 123 4 5 6 7 8 9 10 11 12
|
||||||
|
//size_t match_len = 0;
|
||||||
|
boost::smatch result;
|
||||||
|
if(boost::regex_search(m_cache, result, rexp_match_command_line, boost::match_default) && result[0].matched)
|
||||||
|
{
|
||||||
|
analize_http_method(result, m_query_info.m_http_method, m_query_info.m_http_ver_hi, m_query_info.m_http_ver_hi);
|
||||||
|
m_query_info.m_URI = result[10];
|
||||||
|
parse_uri(m_query_info.m_URI, m_query_info.m_uri_content);
|
||||||
|
m_query_info.m_http_method_str = result[2];
|
||||||
|
m_query_info.m_full_request_str = result[0];
|
||||||
|
|
||||||
|
m_cache.erase(m_cache.begin(), to_nonsonst_iterator(m_cache, result[0].second));
|
||||||
|
|
||||||
|
m_state = http_state_retriving_header;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
m_state = http_state_error;
|
||||||
|
LOG_ERROR("simple_http_connection_handler::handle_invoke_query_line(): Failed to match first line: " << m_cache);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------
|
||||||
|
inline std::string::size_type simple_http_connection_handler::match_end_of_header(const std::string& buf)
|
||||||
|
{
|
||||||
|
|
||||||
|
//Here we returning head size, including terminating sequence (\r\n\r\n or \n\n)
|
||||||
|
std::string::size_type res = buf.find("\r\n\r\n");
|
||||||
|
if(std::string::npos != res)
|
||||||
|
return res+4;
|
||||||
|
res = buf.find("\n\n");
|
||||||
|
if(std::string::npos != res)
|
||||||
|
return res+2;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------
|
||||||
|
inline bool simple_http_connection_handler::analize_cached_request_header_and_invoke_state(size_t pos)
|
||||||
|
{
|
||||||
|
//LOG_PRINT_L4("HTTP HEAD:\r\n" << m_cache.substr(0, pos));
|
||||||
|
|
||||||
|
LOG_FRAME("simple_http_connection_handler::analize_cached_request_header_and_invoke_state(*)", LOG_LEVEL_3);
|
||||||
|
|
||||||
|
m_query_info.m_full_request_buf_size = pos;
|
||||||
|
m_query_info.m_request_head.assign(m_cache.begin(), m_cache.begin()+pos);
|
||||||
|
|
||||||
|
if(!parse_cached_header(m_query_info.m_header_info, m_cache, pos))
|
||||||
|
{
|
||||||
|
LOG_ERROR("simple_http_connection_handler::analize_cached_request_header_and_invoke_state(): failed to anilize request header: " << m_cache);
|
||||||
|
m_state = http_state_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cache.erase(0, pos);
|
||||||
|
|
||||||
|
std::string req_command_str = m_query_info.m_full_request_str;
|
||||||
|
//if we have POST or PUT command, it is very possible tha we will get body
|
||||||
|
//but now, we suppose than we have body only in case of we have "ContentLength"
|
||||||
|
if(m_query_info.m_header_info.m_content_length.size())
|
||||||
|
{
|
||||||
|
m_state = http_state_retriving_body;
|
||||||
|
m_body_transfer_type = http_body_transfer_measure;
|
||||||
|
if(!get_len_from_content_lenght(m_query_info.m_header_info.m_content_length, m_len_summary))
|
||||||
|
{
|
||||||
|
LOG_ERROR("simple_http_connection_handler::analize_cached_request_header_and_invoke_state(): Failed to get_len_from_content_lenght();, m_query_info.m_content_length="<<m_query_info.m_header_info.m_content_length);
|
||||||
|
m_state = http_state_error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(0 == m_len_summary)
|
||||||
|
{ //current query finished, next will be next query
|
||||||
|
if(handle_request_and_send_response(m_query_info))
|
||||||
|
set_ready_state();
|
||||||
|
else
|
||||||
|
m_state = http_state_error;
|
||||||
|
}
|
||||||
|
m_len_remain = m_len_summary;
|
||||||
|
}else
|
||||||
|
{//current query finished, next will be next query
|
||||||
|
handle_request_and_send_response(m_query_info);
|
||||||
|
set_ready_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
inline bool simple_http_connection_handler::handle_retriving_query_body()
|
||||||
|
{
|
||||||
|
switch(m_body_transfer_type)
|
||||||
|
{
|
||||||
|
case http_body_transfer_measure:
|
||||||
|
return handle_query_measure();
|
||||||
|
case http_body_transfer_chunked:
|
||||||
|
case http_body_transfer_connection_close:
|
||||||
|
case http_body_transfer_multipart:
|
||||||
|
case http_body_transfer_undefined:
|
||||||
|
default:
|
||||||
|
LOG_ERROR("simple_http_connection_handler::handle_retriving_query_body(): Unexpected m_body_query_type state:" << m_body_transfer_type);
|
||||||
|
m_state = http_state_error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
inline bool simple_http_connection_handler::handle_query_measure()
|
||||||
|
{
|
||||||
|
|
||||||
|
if(m_len_remain >= m_cache.size())
|
||||||
|
{
|
||||||
|
m_len_remain -= m_cache.size();
|
||||||
|
m_query_info.m_body += m_cache;
|
||||||
|
m_cache.clear();
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
m_query_info.m_body.append(m_cache.begin(), m_cache.begin() + m_len_remain);
|
||||||
|
m_cache.erase(0, m_len_remain);
|
||||||
|
m_len_remain = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!m_len_remain)
|
||||||
|
{
|
||||||
|
if(handle_request_and_send_response(m_query_info))
|
||||||
|
set_ready_state();
|
||||||
|
else
|
||||||
|
m_state = http_state_error;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------
|
||||||
|
inline bool simple_http_connection_handler::parse_cached_header(http_header_info& body_info, const std::string& m_cache_to_process, size_t pos)
|
||||||
|
{
|
||||||
|
LOG_FRAME("http_stream_filter::parse_cached_header(*)", LOG_LEVEL_3);
|
||||||
|
|
||||||
|
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.begin()+pos;
|
||||||
|
|
||||||
|
body_info.clear();
|
||||||
|
|
||||||
|
//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)//"Referer"
|
||||||
|
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];
|
||||||
|
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!)
|
||||||
|
body_info.m_etc_fields.push_back(std::pair<std::string, std::string>(result[field_etc_name], result[field_val]));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_ERROR("simple_http_connection_handler::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 simple_http_connection_handler::get_len_from_content_lenght(const std::string& str, size_t& OUT len)
|
||||||
|
{
|
||||||
|
STATIC_REGEXP_EXPR_1(rexp_mach_field, "\\d+", boost::regex::normal);
|
||||||
|
std::string res;
|
||||||
|
boost::smatch result;
|
||||||
|
if(!(boost::regex_search( str, result, rexp_mach_field, boost::match_default) && result[0].matched))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
len = boost::lexical_cast<size_t>(result[0]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
inline bool simple_http_connection_handler::handle_request_and_send_response(const http::http_request_info& query_info)
|
||||||
|
{
|
||||||
|
http_response_info response;
|
||||||
|
bool res = handle_request(query_info, response);
|
||||||
|
//CHECK_AND_ASSERT_MES(res, res, "handle_request(query_info, response) returned false" );
|
||||||
|
|
||||||
|
std::string response_data = get_response_header(response);
|
||||||
|
|
||||||
|
//LOG_PRINT_L0("HTTP_SEND: << \r\n" << response_data + response.m_body);
|
||||||
|
LOG_PRINT_L3("HTTP_RESPONSE_HEAD: << \r\n" << response_data);
|
||||||
|
|
||||||
|
m_psnd_hndlr->do_send((void*)response_data.data(), response_data.size());
|
||||||
|
if(response.m_body.size())
|
||||||
|
m_psnd_hndlr->do_send((void*)response.m_body.data(), response.m_body.size());
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
inline bool simple_http_connection_handler::handle_request(const http::http_request_info& query_info, http_response_info& response)
|
||||||
|
{
|
||||||
|
|
||||||
|
std::string uri_to_path = query_info.m_uri_content.m_path;
|
||||||
|
if("/" == uri_to_path)
|
||||||
|
uri_to_path = "/index.html";
|
||||||
|
|
||||||
|
//slash_to_back_slash(uri_to_path);
|
||||||
|
m_config.m_lock.lock();
|
||||||
|
std::string destination_file_path = m_config.m_folder + uri_to_path;
|
||||||
|
m_config.m_lock.unlock();
|
||||||
|
if(!file_io_utils::load_file_to_string(destination_file_path.c_str(), response.m_body))
|
||||||
|
{
|
||||||
|
LOG_PRINT("URI \""<< query_info.m_full_request_str.substr(0, query_info.m_full_request_str.size()-2) << "\" [" << destination_file_path << "] Not Found (404 )" , LOG_LEVEL_1);
|
||||||
|
response.m_body = get_not_found_response_body(query_info.m_URI);
|
||||||
|
response.m_response_code = 404;
|
||||||
|
response.m_response_comment = "Not found";
|
||||||
|
response.m_mime_tipe = "text/html";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_PRINT(" -->> " << query_info.m_full_request_str << "\r\n<<--OK" , LOG_LEVEL_3);
|
||||||
|
response.m_response_code = 200;
|
||||||
|
response.m_response_comment = "OK";
|
||||||
|
response.m_mime_tipe = get_file_mime_tipe(uri_to_path);
|
||||||
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
inline std::string simple_http_connection_handler::get_response_header(const http_response_info& response)
|
||||||
|
{
|
||||||
|
std::string buf = "HTTP/1.1 ";
|
||||||
|
buf += boost::lexical_cast<std::string>(response.m_response_code) + " " + response.m_response_comment + "\r\n" +
|
||||||
|
"Server: Siski v0.1\r\n"
|
||||||
|
"Content-Length: ";
|
||||||
|
buf += boost::lexical_cast<std::string>(response.m_body.size()) + "\r\n";
|
||||||
|
buf += "Content-Type: ";
|
||||||
|
buf += response.m_mime_tipe + "\r\n";
|
||||||
|
|
||||||
|
buf += "Last-Modified: ";
|
||||||
|
time_t tm;
|
||||||
|
time(&tm);
|
||||||
|
buf += misc_utils::get_internet_time_str(tm) + "\r\n";
|
||||||
|
buf += "Accept-Ranges: bytes\r\n";
|
||||||
|
//Wed, 01 Dec 2010 03:27:41 GMT"
|
||||||
|
|
||||||
|
string_tools::trim(m_query_info.m_header_info.m_connection);
|
||||||
|
if(m_query_info.m_header_info.m_connection.size())
|
||||||
|
{
|
||||||
|
if(!string_tools::compare_no_case("close", m_query_info.m_header_info.m_connection))
|
||||||
|
{
|
||||||
|
//closing connection after sending
|
||||||
|
buf += "Connection: close\r\n";
|
||||||
|
m_state = http_state_connection_close;
|
||||||
|
m_want_close = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//add additional fields, if it is
|
||||||
|
for(fields_list::const_iterator it = response.m_additional_fields.begin(); it!=response.m_additional_fields.end(); it++)
|
||||||
|
buf += it->first + ":" + it->second + "\r\n";
|
||||||
|
|
||||||
|
buf+="\r\n";
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
inline std::string simple_http_connection_handler::get_file_mime_tipe(const std::string& path)
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
std::string ext = string_tools::get_extension(path);
|
||||||
|
if(!string_tools::compare_no_case(ext, "gif"))
|
||||||
|
result = "image/gif";
|
||||||
|
else if(!string_tools::compare_no_case(ext, "jpg"))
|
||||||
|
result = "image/jpeg";
|
||||||
|
else if(!string_tools::compare_no_case(ext, "html"))
|
||||||
|
result = "text/html";
|
||||||
|
else if(!string_tools::compare_no_case(ext, "htm"))
|
||||||
|
result = "text/html";
|
||||||
|
else if(!string_tools::compare_no_case(ext, "js"))
|
||||||
|
result = "application/x-javascript";
|
||||||
|
else if(!string_tools::compare_no_case(ext, "css"))
|
||||||
|
result = "text/css";
|
||||||
|
else if(!string_tools::compare_no_case(ext, "xml"))
|
||||||
|
result = "application/xml";
|
||||||
|
else if(!string_tools::compare_no_case(ext, "svg"))
|
||||||
|
result = "image/svg+xml";
|
||||||
|
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
inline std::string simple_http_connection_handler::get_not_found_response_body(const std::string& URI)
|
||||||
|
{
|
||||||
|
std::string body =
|
||||||
|
"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
|
||||||
|
"<html><head>\r\n"
|
||||||
|
"<title>404 Not Found</title>\r\n"
|
||||||
|
"</head><body>\r\n"
|
||||||
|
"<h1>Not Found</h1>\r\n"
|
||||||
|
"<p>The requested URL \r\n";
|
||||||
|
body += URI;
|
||||||
|
body += "was not found on this server.</p>\r\n"
|
||||||
|
"</body></html>\r\n";
|
||||||
|
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------
|
||||||
|
inline bool simple_http_connection_handler::slash_to_back_slash(std::string& str)
|
||||||
|
{
|
||||||
|
for(std::string::iterator it = str.begin(); it!=str.end(); it++)
|
||||||
|
if('/' == *it)
|
||||||
|
*it = '\\';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------
|
||||||
|
//--------------------------------------------------------------------------------------------
|
||||||
|
//--------------------------------------------------------------------------------------------
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _HTTP_SERVER_CP_H_
|
||||||
|
#define _HTTP_SERVER_CP_H_
|
||||||
|
|
||||||
|
#include "abstract_tcp_server_cp.h"
|
||||||
|
#include "http_server.h"
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
typedef cp_server_impl<http::simple_http_connection_handler> cp_http_server_file_system;
|
||||||
|
typedef cp_server_impl<http::http_custom_handler> cp_http_server_custum_handling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _HTTP_SERVER_CP2_H_
|
||||||
|
#define _HTTP_SERVER_CP2_H_
|
||||||
|
|
||||||
|
#include "abstract_tcp_server2.h"
|
||||||
|
#include "http_protocol_handler.h"
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
typedef boosted_tcp_server<http::simple_http_connection_handler> boosted_http_server_file_system;
|
||||||
|
typedef boosted_tcp_server<http::http_custom_handler> boosted_http_server_custum_handling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,260 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 "serialization/keyvalue_serialization.h"
|
||||||
|
#include "storages/portable_storage_template_helper.h"
|
||||||
|
#include "http_base.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define CHAIN_HTTP_TO_MAP2() bool handle_http_request(const epee::net_utils::http::http_request_info& query_info, \
|
||||||
|
epee::net_utils::http::http_response_info& response, \
|
||||||
|
const epee::net_utils::connection_context_base& m_conn_context) \
|
||||||
|
{\
|
||||||
|
LOG_PRINT_L2("HTTP [" << epee::string_tools::get_ip_string_from_int32(m_conn_context.m_remote_ip ) << "] " << query_info.m_http_method_str << " " << query_info.m_URI); \
|
||||||
|
response.m_response_code = 200; \
|
||||||
|
response.m_response_comment = "Ok"; \
|
||||||
|
if(!handle_http_request_map(query_info, response, m_conn_context)) \
|
||||||
|
{response.m_response_code = 404;response.m_response_comment = "Not found";} \
|
||||||
|
return true; \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define BEGIN_URI_MAP2() bool handle_http_request_map(const epee::net_utils::http::http_request_info& query_info, \
|
||||||
|
epee::net_utils::http::http_response_info& response_info, \
|
||||||
|
const epee::net_utils::connection_context_base& m_conn_context) { \
|
||||||
|
bool handled = 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
|
||||||
|
|
||||||
|
#define MAP_URI_AUTO_JON2(s_pattern, callback_f, command_type) \
|
||||||
|
else if(query_info.m_URI == s_pattern) \
|
||||||
|
{ \
|
||||||
|
handled = true; \
|
||||||
|
boost::uint64_t ticks = misc_utils::get_tick_count(); \
|
||||||
|
boost::value_initialized<command_type::request> req; \
|
||||||
|
bool parse_res = epee::serialization::load_t_from_json(static_cast<command_type::request&>(req), query_info.m_body); \
|
||||||
|
CHECK_AND_ASSERT_MES(parse_res, false, "Failed to parse json: \r\n" << query_info.m_body); \
|
||||||
|
boost::uint64_t ticks1 = epee::misc_utils::get_tick_count(); \
|
||||||
|
boost::value_initialized<command_type::response> resp;\
|
||||||
|
if(!callback_f(static_cast<command_type::request&>(req), static_cast<command_type::response&>(resp))) \
|
||||||
|
{ \
|
||||||
|
LOG_ERROR("Failed to " << #callback_f << "()"); \
|
||||||
|
response_info.m_response_code = 500; \
|
||||||
|
response_info.m_response_comment = "Internal Server Error"; \
|
||||||
|
return true; \
|
||||||
|
} \
|
||||||
|
boost::uint64_t ticks2 = epee::misc_utils::get_tick_count(); \
|
||||||
|
epee::serialization::store_t_to_json(static_cast<command_type::response&>(resp), response_info.m_body); \
|
||||||
|
boost::uint64_t ticks3 = epee::misc_utils::get_tick_count(); \
|
||||||
|
response_info.m_mime_tipe = "application/json"; \
|
||||||
|
response_info.m_header_info.m_content_type = " application/json"; \
|
||||||
|
LOG_PRINT( s_pattern << " processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAP_URI_AUTO_BIN2(s_pattern, callback_f, command_type) \
|
||||||
|
else if(query_info.m_URI == s_pattern) \
|
||||||
|
{ \
|
||||||
|
handled = true; \
|
||||||
|
boost::uint64_t ticks = misc_utils::get_tick_count(); \
|
||||||
|
boost::value_initialized<command_type::request> req; \
|
||||||
|
bool parse_res = epee::serialization::load_t_from_binary(static_cast<command_type::request&>(req), query_info.m_body); \
|
||||||
|
CHECK_AND_ASSERT_MES(parse_res, false, "Failed to parse bin body data, body size=" << query_info.m_body.size()); \
|
||||||
|
boost::uint64_t ticks1 = misc_utils::get_tick_count(); \
|
||||||
|
boost::value_initialized<command_type::response> resp;\
|
||||||
|
if(!callback_f(static_cast<command_type::request&>(req), static_cast<command_type::response&>(resp))) \
|
||||||
|
{ \
|
||||||
|
LOG_ERROR("Failed to " << #callback_f << "()"); \
|
||||||
|
response_info.m_response_code = 500; \
|
||||||
|
response_info.m_response_comment = "Internal Server Error"; \
|
||||||
|
return true; \
|
||||||
|
} \
|
||||||
|
boost::uint64_t ticks2 = misc_utils::get_tick_count(); \
|
||||||
|
epee::serialization::store_t_to_binary(static_cast<command_type::response&>(resp), response_info.m_body); \
|
||||||
|
boost::uint64_t ticks3 = epee::misc_utils::get_tick_count(); \
|
||||||
|
response_info.m_mime_tipe = " application/octet-stream"; \
|
||||||
|
response_info.m_header_info.m_content_type = " application/octet-stream"; \
|
||||||
|
LOG_PRINT( s_pattern << "() processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CHAIN_URI_MAP2(callback) else {callback(query_info, response_info, m_conn_context);handled = true;}
|
||||||
|
|
||||||
|
#define END_URI_MAP2() return handled;}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace json_rpc
|
||||||
|
{
|
||||||
|
template<typename t_param>
|
||||||
|
struct request
|
||||||
|
{
|
||||||
|
std::string version;
|
||||||
|
std::string method;
|
||||||
|
std::string id;
|
||||||
|
t_param params;
|
||||||
|
|
||||||
|
BEGIN_KV_SERIALIZE_MAP()
|
||||||
|
KV_SERIALIZE(version)
|
||||||
|
KV_SERIALIZE(method)
|
||||||
|
KV_SERIALIZE(id)
|
||||||
|
KV_SERIALIZE(params)
|
||||||
|
END_KV_SERIALIZE_MAP()
|
||||||
|
};
|
||||||
|
|
||||||
|
struct error
|
||||||
|
{
|
||||||
|
int64_t code;
|
||||||
|
std::string message;
|
||||||
|
BEGIN_KV_SERIALIZE_MAP()
|
||||||
|
KV_SERIALIZE(code)
|
||||||
|
KV_SERIALIZE(message)
|
||||||
|
END_KV_SERIALIZE_MAP()
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dummy_error
|
||||||
|
{
|
||||||
|
BEGIN_KV_SERIALIZE_MAP()
|
||||||
|
END_KV_SERIALIZE_MAP()
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename t_param, typename t_error>
|
||||||
|
struct response
|
||||||
|
{
|
||||||
|
t_param result;
|
||||||
|
t_error error;
|
||||||
|
std::string id;
|
||||||
|
|
||||||
|
BEGIN_KV_SERIALIZE_MAP()
|
||||||
|
KV_SERIALIZE(result)
|
||||||
|
KV_SERIALIZE(error)
|
||||||
|
KV_SERIALIZE(id)
|
||||||
|
END_KV_SERIALIZE_MAP()
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef response<std::string, error> error_response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define BEGIN_JSON_RPC_MAP(uri) else if(query_info.m_URI == uri) \
|
||||||
|
{ \
|
||||||
|
boost::uint64_t ticks = epee::misc_utils::get_tick_count(); \
|
||||||
|
epee::serialization::portable_storage ps; \
|
||||||
|
if(!ps.load_from_json(query_info.m_body)) \
|
||||||
|
{ \
|
||||||
|
boost::value_initialized<epee::json_rpc::error_response> rsp; \
|
||||||
|
static_cast<epee::json_rpc::error_response&>(rsp).error.code = -32700; \
|
||||||
|
static_cast<epee::json_rpc::error_response&>(rsp).error.message = "Parse error"; \
|
||||||
|
epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(rsp), response_info.m_body); \
|
||||||
|
return true; \
|
||||||
|
} \
|
||||||
|
std::string callback_name; \
|
||||||
|
if(!ps.get_value("method", callback_name, nullptr)) \
|
||||||
|
{ \
|
||||||
|
epee::json_rpc::error_response rsp; \
|
||||||
|
rsp.error.code = -32600; \
|
||||||
|
rsp.error.message = "Invalid Request"; \
|
||||||
|
epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(rsp), response_info.m_body); \
|
||||||
|
return true; \
|
||||||
|
} \
|
||||||
|
if(false) return true; //just a stub to have "else if"
|
||||||
|
|
||||||
|
#define MAP_JON_RPC_WE(method_name, callback_f, command_type) \
|
||||||
|
else if(callback_name == method_name) \
|
||||||
|
{ \
|
||||||
|
handled = true; \
|
||||||
|
boost::value_initialized<epee::json_rpc::request<command_type::request> > req_; \
|
||||||
|
epee::json_rpc::request<command_type::request>& req = static_cast<epee::json_rpc::request<command_type::request>&>(req_);\
|
||||||
|
req.load(ps); \
|
||||||
|
boost::uint64_t ticks1 = epee::misc_utils::get_tick_count(); \
|
||||||
|
boost::value_initialized<epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> > resp_; \
|
||||||
|
epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error>& resp = static_cast<epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> &>(resp_); \
|
||||||
|
resp.id = req.id; \
|
||||||
|
epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \
|
||||||
|
fail_resp.id = req.id; \
|
||||||
|
if(!callback_f(req.params, resp.result, fail_resp.error)) \
|
||||||
|
{ \
|
||||||
|
epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(fail_resp), response_info.m_body); \
|
||||||
|
return true; \
|
||||||
|
} \
|
||||||
|
boost::uint64_t ticks2 = epee::misc_utils::get_tick_count(); \
|
||||||
|
epee::serialization::store_t_to_json(resp, response_info.m_body); \
|
||||||
|
boost::uint64_t ticks3 = epee::misc_utils::get_tick_count(); \
|
||||||
|
response_info.m_mime_tipe = "application/json"; \
|
||||||
|
response_info.m_header_info.m_content_type = " application/json"; \
|
||||||
|
LOG_PRINT( query_info.m_URI << "[" << method_name << "] processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \
|
||||||
|
return true;\
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define MAP_JON_RPC(method_name, callback_f, command_type) \
|
||||||
|
else if(callback_name == method_name) \
|
||||||
|
{ \
|
||||||
|
handled = true; \
|
||||||
|
boost::value_initialized<epee::json_rpc::request<command_type::request> > req_; \
|
||||||
|
epee::json_rpc::request<command_type::request>& req = static_cast<epee::json_rpc::request<command_type::request>&>(req_);\
|
||||||
|
req.load(ps); \
|
||||||
|
boost::uint64_t ticks1 = epee::misc_utils::get_tick_count(); \
|
||||||
|
boost::value_initialized<epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> > resp_; \
|
||||||
|
epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error>& resp = static_cast<epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> &>(resp_); \
|
||||||
|
resp.id = req.id; \
|
||||||
|
if(!callback_f(req.params, resp.result)) \
|
||||||
|
{ \
|
||||||
|
epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \
|
||||||
|
fail_resp.id = req.id; \
|
||||||
|
fail_resp.error.code = -32603; \
|
||||||
|
fail_resp.error.message = "Internal error"; \
|
||||||
|
epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(fail_resp), response_info.m_body); \
|
||||||
|
return true; \
|
||||||
|
} \
|
||||||
|
boost::uint64_t ticks2 = epee::misc_utils::get_tick_count(); \
|
||||||
|
epee::serialization::store_t_to_json(resp, response_info.m_body); \
|
||||||
|
boost::uint64_t ticks3 = epee::misc_utils::get_tick_count(); \
|
||||||
|
response_info.m_mime_tipe = "application/json"; \
|
||||||
|
response_info.m_header_info.m_content_type = " application/json"; \
|
||||||
|
LOG_PRINT( query_info.m_URI << "[" << method_name << "] processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \
|
||||||
|
return true;\
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define END_JSON_RPC_MAP() \
|
||||||
|
epee::json_rpc::error_response rsp; \
|
||||||
|
rsp.error.code = -32601; \
|
||||||
|
rsp.error.message = "Method not found"; \
|
||||||
|
epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(rsp), response_info.m_body); \
|
||||||
|
return true; \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 <boost/thread.hpp>
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
|
||||||
|
#include "net/http_server_cp2.h"
|
||||||
|
#include "net/http_server_handlers_map2.h"
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
|
||||||
|
template<class t_child_class>
|
||||||
|
class http_server_impl_base: public net_utils::http::i_http_server_handler
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
http_server_impl_base()
|
||||||
|
: m_net_server()
|
||||||
|
{}
|
||||||
|
|
||||||
|
explicit http_server_impl_base(boost::asio::io_service& external_io_service)
|
||||||
|
: m_net_server(external_io_service)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool init(const std::string& bind_port = "0", const std::string& bind_ip = "0.0.0.0")
|
||||||
|
{
|
||||||
|
|
||||||
|
//set self as callback handler
|
||||||
|
m_net_server.get_config_object().m_phandler = static_cast<t_child_class*>(this);
|
||||||
|
|
||||||
|
//here set folder for hosting reqests
|
||||||
|
m_net_server.get_config_object().m_folder = "";
|
||||||
|
|
||||||
|
LOG_PRINT_L0("Binding on " << bind_ip << ":" << bind_port);
|
||||||
|
bool res = m_net_server.init_server(bind_port, bind_ip);
|
||||||
|
if(!res)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to bind server");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool run(size_t threads_count, bool wait = true)
|
||||||
|
{
|
||||||
|
//go to loop
|
||||||
|
LOG_PRINT("Run net_service loop( " << threads_count << " threads)...", LOG_LEVEL_0);
|
||||||
|
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);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool deinit()
|
||||||
|
{
|
||||||
|
return m_net_server.deinit_server();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool timed_wait_server_stop(uint64_t ms)
|
||||||
|
{
|
||||||
|
return m_net_server.timed_wait_server_stop(ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool send_stop_signal()
|
||||||
|
{
|
||||||
|
m_net_server.send_stop_signal();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_binded_port()
|
||||||
|
{
|
||||||
|
return m_net_server.get_binded_port();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
net_utils::boosted_http_server_custum_handling m_net_server;
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _HTTP_SERVER_CP_H_
|
||||||
|
#define _HTTP_SERVER_CP_H_
|
||||||
|
|
||||||
|
#include "abstract_tcp_server.h"
|
||||||
|
#include "http_server.h"
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
typedef abstract_tcp_server<http::simple_http_connection_handler> mt_http_server_file_system;
|
||||||
|
typedef abstract_tcp_server<http::http_custom_handler> mt_http_server_custum_handling;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _LEVIN_BASE_H_
|
||||||
|
#define _LEVIN_BASE_H_
|
||||||
|
|
||||||
|
#include "net_utils_base.h"
|
||||||
|
|
||||||
|
#define LEVIN_SIGNATURE 0x0101010101012101LL //Bender's nightmare
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace levin
|
||||||
|
{
|
||||||
|
#pragma pack(push)
|
||||||
|
#pragma pack(1)
|
||||||
|
struct bucket_head
|
||||||
|
{
|
||||||
|
boost::uint64_t m_signature;
|
||||||
|
boost::uint64_t m_cb;
|
||||||
|
bool m_have_to_return_data;
|
||||||
|
boost::uint32_t m_command;
|
||||||
|
boost::int32_t m_return_code;
|
||||||
|
boost::uint32_t m_reservedA; //probably some flags in future
|
||||||
|
boost::uint32_t m_reservedB; //probably some check sum in future
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
|
||||||
|
#pragma pack(push)
|
||||||
|
#pragma pack(1)
|
||||||
|
struct bucket_head2
|
||||||
|
{
|
||||||
|
boost::uint64_t m_signature;
|
||||||
|
boost::uint64_t m_cb;
|
||||||
|
bool m_have_to_return_data;
|
||||||
|
boost::uint32_t m_command;
|
||||||
|
boost::int32_t m_return_code;
|
||||||
|
boost::uint32_t m_flags;
|
||||||
|
boost::uint32_t m_protocol_version;
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
|
||||||
|
#define LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED 0
|
||||||
|
#define LEVIN_DEFAULT_MAX_PACKET_SIZE 100000000 //100MB by default
|
||||||
|
|
||||||
|
#define LEVIN_PACKET_REQUEST 0x00000001
|
||||||
|
#define LEVIN_PACKET_RESPONSE 0x00000002
|
||||||
|
|
||||||
|
|
||||||
|
#define LEVIN_PROTOCOL_VER_0 0
|
||||||
|
#define LEVIN_PROTOCOL_VER_1 1
|
||||||
|
|
||||||
|
template<class t_connection_context = net_utils::connection_context_base>
|
||||||
|
struct levin_commands_handler
|
||||||
|
{
|
||||||
|
virtual int invoke(int command, const std::string& in_buff, std::string& buff_out, t_connection_context& context)=0;
|
||||||
|
virtual int notify(int command, const std::string& in_buff, t_connection_context& context)=0;
|
||||||
|
virtual void callback(t_connection_context& context){};
|
||||||
|
|
||||||
|
virtual void on_connection_new(t_connection_context& context){};
|
||||||
|
virtual void on_connection_close(t_connection_context& context){};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#define LEVIN_OK 0
|
||||||
|
#define LEVIN_ERROR_CONNECTION -1
|
||||||
|
#define LEVIN_ERROR_CONNECTION_NOT_FOUND -2
|
||||||
|
#define LEVIN_ERROR_CONNECTION_DESTROYED -3
|
||||||
|
#define LEVIN_ERROR_CONNECTION_TIMEDOUT -4
|
||||||
|
#define LEVIN_ERROR_CONNECTION_NO_DUPLEX_PROTOCOL -5
|
||||||
|
#define LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED -6
|
||||||
|
#define LEVIN_ERROR_FORMAT -7
|
||||||
|
|
||||||
|
#define DESCRIBE_RET_CODE(code) case code: return #code;
|
||||||
|
inline
|
||||||
|
const char* get_err_descr(int err)
|
||||||
|
{
|
||||||
|
switch(err)
|
||||||
|
{
|
||||||
|
DESCRIBE_RET_CODE(LEVIN_OK);
|
||||||
|
DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION);
|
||||||
|
DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_NOT_FOUND);
|
||||||
|
DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_DESTROYED);
|
||||||
|
DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_TIMEDOUT);
|
||||||
|
DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_NO_DUPLEX_PROTOCOL);
|
||||||
|
DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED);
|
||||||
|
DESCRIBE_RET_CODE(LEVIN_ERROR_FORMAT);
|
||||||
|
default:
|
||||||
|
return "unknown code";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif //_LEVIN_BASE_H_
|
|
@ -0,0 +1,89 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _LEVIN_CLIENT_H_
|
||||||
|
#define _LEVIN_CLIENT_H_
|
||||||
|
|
||||||
|
#include "net_helper.h"
|
||||||
|
#include "levin_base.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef MAKE_IP
|
||||||
|
#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace levin
|
||||||
|
{
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
class levin_client_impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
levin_client_impl();
|
||||||
|
virtual ~levin_client_impl();
|
||||||
|
|
||||||
|
bool connect(u_long ip, int port, unsigned int timeout, const std::string& bind_ip = "0.0.0.0");
|
||||||
|
bool connect(const std::string& addr, int port, unsigned int timeout, const std::string& bind_ip = "0.0.0.0");
|
||||||
|
bool is_connected();
|
||||||
|
bool disconnect();
|
||||||
|
|
||||||
|
virtual int invoke(int command, const std::string& in_buff, std::string& buff_out);
|
||||||
|
virtual int notify(int command, const std::string& in_buff);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
net_utils::blocked_mode_client m_transport;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
class levin_client_impl2: public levin_client_impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
int invoke(int command, const std::string& in_buff, std::string& buff_out);
|
||||||
|
int notify(int command, const std::string& in_buff);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
typedef levin::levin_client_impl levin_client;
|
||||||
|
typedef levin::levin_client_impl2 levin_client2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "levin_client.inl"
|
||||||
|
|
||||||
|
#endif //_LEVIN_CLIENT_H_
|
|
@ -0,0 +1,194 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#include "string_tools.h"
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace levin
|
||||||
|
{
|
||||||
|
inline
|
||||||
|
bool levin_client_impl::connect(u_long ip, int port, unsigned int timeout, const std::string& bind_ip)
|
||||||
|
{
|
||||||
|
return m_transport.connect(string_tools::get_ip_string_from_int32(ip), port, timeout, timeout, bind_ip);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
bool levin_client_impl::connect(const std::string& addr, int port, unsigned int timeout, const std::string& bind_ip)
|
||||||
|
{
|
||||||
|
return m_transport.connect(addr, port, timeout, timeout, bind_ip);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
bool levin_client_impl::is_connected()
|
||||||
|
{
|
||||||
|
return m_transport.is_connected();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
bool levin_client_impl::disconnect()
|
||||||
|
{
|
||||||
|
return m_transport.disconnect();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
levin_client_impl::levin_client_impl()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
levin_client_impl::~levin_client_impl()
|
||||||
|
{
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
int levin_client_impl::invoke(int command, const std::string& in_buff, std::string& buff_out)
|
||||||
|
{
|
||||||
|
if(!is_connected())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
bucket_head head = {0};
|
||||||
|
head.m_signature = LEVIN_SIGNATURE;
|
||||||
|
head.m_cb = in_buff.size();
|
||||||
|
head.m_have_to_return_data = true;
|
||||||
|
head.m_command = command;
|
||||||
|
if(!m_transport.send(&head, sizeof(head)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if(!m_transport.send(in_buff))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
std::string local_buff;
|
||||||
|
if(!m_transport.recv_n(local_buff, sizeof(bucket_head)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
head = *(bucket_head*)local_buff.data();
|
||||||
|
|
||||||
|
|
||||||
|
if(head.m_signature!=LEVIN_SIGNATURE)
|
||||||
|
{
|
||||||
|
LOG_PRINT_L0("Signature missmatch in response");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!m_transport.recv_n(buff_out, head.m_cb))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return head.m_return_code;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
int levin_client_impl::notify(int command, const std::string& in_buff)
|
||||||
|
{
|
||||||
|
if(!is_connected())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
bucket_head head = {0};
|
||||||
|
head.m_signature = LEVIN_SIGNATURE;
|
||||||
|
head.m_cb = in_buff.size();
|
||||||
|
head.m_have_to_return_data = false;
|
||||||
|
head.m_command = command;
|
||||||
|
|
||||||
|
if(!m_transport.send((const char*)&head, sizeof(head)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if(!m_transport.send(in_buff))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
int levin_client_impl2::invoke(int command, const std::string& in_buff, std::string& buff_out)
|
||||||
|
{
|
||||||
|
if(!is_connected())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
bucket_head2 head = {0};
|
||||||
|
head.m_signature = LEVIN_SIGNATURE;
|
||||||
|
head.m_cb = in_buff.size();
|
||||||
|
head.m_have_to_return_data = true;
|
||||||
|
head.m_command = command;
|
||||||
|
head.m_protocol_version = LEVIN_PROTOCOL_VER_1;
|
||||||
|
head.m_flags = LEVIN_PACKET_REQUEST;
|
||||||
|
if(!m_transport.send(&head, sizeof(head)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if(!m_transport.send(in_buff))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
std::string local_buff;
|
||||||
|
if(!m_transport.recv_n(local_buff, sizeof(bucket_head2)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
head = *(bucket_head2*)local_buff.data();
|
||||||
|
|
||||||
|
|
||||||
|
if(head.m_signature!=LEVIN_SIGNATURE)
|
||||||
|
{
|
||||||
|
LOG_PRINT_L0("Signature missmatch in response");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!m_transport.recv_n(buff_out, head.m_cb))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return head.m_return_code;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
int levin_client_impl2::notify(int command, const std::string& in_buff)
|
||||||
|
{
|
||||||
|
if(!is_connected())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
bucket_head2 head = {0};
|
||||||
|
head.m_signature = LEVIN_SIGNATURE;
|
||||||
|
head.m_cb = in_buff.size();
|
||||||
|
head.m_have_to_return_data = false;
|
||||||
|
head.m_command = command;
|
||||||
|
head.m_protocol_version = LEVIN_PROTOCOL_VER_1;
|
||||||
|
head.m_flags = LEVIN_PACKET_REQUEST;
|
||||||
|
|
||||||
|
if(!m_transport.send((const char*)&head, sizeof(head)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if(!m_transport.send(in_buff))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
|
@ -0,0 +1,577 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 ""
|
||||||
|
#include "net_helper.h"
|
||||||
|
#include "levin_base.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace levin
|
||||||
|
{
|
||||||
|
|
||||||
|
/************************************************************************
|
||||||
|
* levin_client_async - probably it is not really fast implementation,
|
||||||
|
* each handler thread could make up to 30 ms latency.
|
||||||
|
* But, handling events in reader thread will cause dead locks in
|
||||||
|
* case of recursive call (call invoke() to the same connection
|
||||||
|
* on reader thread on remote invoke() handler)
|
||||||
|
***********************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
class levin_client_async
|
||||||
|
{
|
||||||
|
levin_commands_handler* m_pcommands_handler;
|
||||||
|
volatile boost::uint32_t m_is_stop;
|
||||||
|
volatile boost::uint32_t m_threads_count;
|
||||||
|
::critical_section m_send_lock;
|
||||||
|
|
||||||
|
std::string m_local_invoke_buff;
|
||||||
|
::critical_section m_local_invoke_buff_lock;
|
||||||
|
volatile int m_invoke_res;
|
||||||
|
|
||||||
|
volatile boost::uint32_t m_invoke_data_ready;
|
||||||
|
volatile boost::uint32_t m_invoke_is_active;
|
||||||
|
|
||||||
|
boost::mutex m_invoke_event;
|
||||||
|
boost::condition_variable m_invoke_cond;
|
||||||
|
size_t m_timeout;
|
||||||
|
|
||||||
|
::critical_section m_recieved_packets_lock;
|
||||||
|
struct packet_entry
|
||||||
|
{
|
||||||
|
bucket_head m_hd;
|
||||||
|
std::string m_body;
|
||||||
|
boost::uint32_t m_connection_index;
|
||||||
|
};
|
||||||
|
std::list<packet_entry> m_recieved_packets;
|
||||||
|
/*
|
||||||
|
m_current_connection_index needed when some connection was broken and reconnected - in this
|
||||||
|
case we could have some received packets in que, which shoud not be handled
|
||||||
|
*/
|
||||||
|
volatile boost::uint32_t m_current_connection_index;
|
||||||
|
::critical_section m_invoke_lock;
|
||||||
|
::critical_section m_reciev_packet_lock;
|
||||||
|
::critical_section m_connection_lock;
|
||||||
|
net_utils::blocked_mode_client m_transport;
|
||||||
|
public:
|
||||||
|
levin_client_async():m_pcommands_handler(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0)
|
||||||
|
{}
|
||||||
|
levin_client_async(const levin_client_async& /*v*/):m_pcommands_handler(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0)
|
||||||
|
{}
|
||||||
|
~levin_client_async()
|
||||||
|
{
|
||||||
|
boost::interprocess::ipcdetail::atomic_write32(&m_is_stop, 1);
|
||||||
|
disconnect();
|
||||||
|
|
||||||
|
|
||||||
|
while(boost::interprocess::ipcdetail::atomic_read32(&m_threads_count))
|
||||||
|
::Sleep(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_handler(levin_commands_handler* phandler)
|
||||||
|
{
|
||||||
|
m_pcommands_handler = phandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool connect(boost::uint32_t ip, boost::uint32_t port, boost::uint32_t timeout)
|
||||||
|
{
|
||||||
|
loop_call_guard();
|
||||||
|
critical_region cr(m_connection_lock);
|
||||||
|
|
||||||
|
m_timeout = timeout;
|
||||||
|
bool res = false;
|
||||||
|
CRITICAL_REGION_BEGIN(m_reciev_packet_lock);
|
||||||
|
CRITICAL_REGION_BEGIN(m_send_lock);
|
||||||
|
res = levin_client_impl::connect(ip, port, timeout);
|
||||||
|
boost::interprocess::ipcdetail::atomic_inc32(&m_current_connection_index);
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
if(res && !boost::interprocess::ipcdetail::atomic_read32(&m_threads_count) )
|
||||||
|
{
|
||||||
|
//boost::interprocess::ipcdetail::atomic_write32(&m_is_stop, 0);//m_is_stop = false;
|
||||||
|
boost::thread( boost::bind(&levin_duplex_client::reciever_thread, this) );
|
||||||
|
boost::thread( boost::bind(&levin_duplex_client::handler_thread, this) );
|
||||||
|
boost::thread( boost::bind(&levin_duplex_client::handler_thread, this) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
bool is_connected()
|
||||||
|
{
|
||||||
|
loop_call_guard();
|
||||||
|
critical_region cr(m_cs);
|
||||||
|
return levin_client_impl::is_connected();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool check_connection()
|
||||||
|
{
|
||||||
|
loop_call_guard();
|
||||||
|
critical_region cr(m_cs);
|
||||||
|
|
||||||
|
if(!is_connected())
|
||||||
|
{
|
||||||
|
if( !reconnect() )
|
||||||
|
{
|
||||||
|
LOG_ERROR("Reconnect Failed. Failed to invoke() becouse not connected!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
bool recv_n(SOCKET s, char* pbuff, size_t cb)
|
||||||
|
{
|
||||||
|
while(cb)
|
||||||
|
{
|
||||||
|
int res = ::recv(m_socket, pbuff, (int)cb, 0);
|
||||||
|
|
||||||
|
if(SOCKET_ERROR == res)
|
||||||
|
{
|
||||||
|
if(!m_connected)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int err = ::WSAGetLastError();
|
||||||
|
LOG_ERROR("Failed to recv(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
|
||||||
|
disconnect();
|
||||||
|
//reconnect();
|
||||||
|
return false;
|
||||||
|
}else if(res == 0)
|
||||||
|
{
|
||||||
|
disconnect();
|
||||||
|
//reconnect();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LOG_PRINT_L4("[" << m_socket <<"] RECV " << res);
|
||||||
|
cb -= res;
|
||||||
|
pbuff += res;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
bool recv_n(SOCKET s, std::string& buff)
|
||||||
|
{
|
||||||
|
size_t cb_remain = buff.size();
|
||||||
|
char* m_current_ptr = (char*)buff.data();
|
||||||
|
return recv_n(s, m_current_ptr, cb_remain);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool disconnect()
|
||||||
|
{
|
||||||
|
//boost::interprocess::ipcdetail::atomic_write32(&m_is_stop, 1);//m_is_stop = true;
|
||||||
|
loop_call_guard();
|
||||||
|
critical_region cr(m_cs);
|
||||||
|
levin_client_impl::disconnect();
|
||||||
|
|
||||||
|
CRITICAL_REGION_BEGIN(m_local_invoke_buff_lock);
|
||||||
|
m_local_invoke_buff.clear();
|
||||||
|
m_invoke_res = LEVIN_ERROR_CONNECTION_DESTROYED;
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
boost::interprocess::ipcdetail::atomic_write32(&m_invoke_data_ready, 1); //m_invoke_data_ready = true;
|
||||||
|
m_invoke_cond.notify_all();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop_call_guard()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_leave_invoke()
|
||||||
|
{
|
||||||
|
boost::interprocess::ipcdetail::atomic_write32(&m_invoke_is_active, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int invoke(const GUID& target, int command, const std::string& in_buff, std::string& buff_out)
|
||||||
|
{
|
||||||
|
|
||||||
|
critical_region cr_invoke(m_invoke_lock);
|
||||||
|
|
||||||
|
boost::interprocess::ipcdetail::atomic_write32(&m_invoke_is_active, 1);
|
||||||
|
boost::interprocess::ipcdetail::atomic_write32(&m_invoke_data_ready, 0);
|
||||||
|
misc_utils::destr_ptr hdlr = misc_utils::add_exit_scope_handler(boost::bind(&levin_duplex_client::on_leave_invoke, this));
|
||||||
|
|
||||||
|
loop_call_guard();
|
||||||
|
|
||||||
|
if(!check_connection())
|
||||||
|
return LEVIN_ERROR_CONNECTION_DESTROYED;
|
||||||
|
|
||||||
|
|
||||||
|
bucket_head head = {0};
|
||||||
|
head.m_signature = LEVIN_SIGNATURE;
|
||||||
|
head.m_cb = in_buff.size();
|
||||||
|
head.m_have_to_return_data = true;
|
||||||
|
head.m_id = target;
|
||||||
|
#ifdef TRACE_LEVIN_PACKETS_BY_GUIDS
|
||||||
|
::UuidCreate(&head.m_id);
|
||||||
|
#endif
|
||||||
|
head.m_command = command;
|
||||||
|
head.m_protocol_version = LEVIN_PROTOCOL_VER_1;
|
||||||
|
head.m_flags = LEVIN_PACKET_REQUEST;
|
||||||
|
LOG_PRINT("[" << m_socket <<"] Sending invoke data", LOG_LEVEL_4);
|
||||||
|
|
||||||
|
CRITICAL_REGION_BEGIN(m_send_lock);
|
||||||
|
LOG_PRINT_L4("[" << m_socket <<"] SEND " << sizeof(head));
|
||||||
|
int res = ::send(m_socket, (const char*)&head, sizeof(head), 0);
|
||||||
|
if(SOCKET_ERROR == res)
|
||||||
|
{
|
||||||
|
int err = ::WSAGetLastError();
|
||||||
|
LOG_ERROR("Failed to send(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
|
||||||
|
disconnect();
|
||||||
|
return LEVIN_ERROR_CONNECTION_DESTROYED;
|
||||||
|
}
|
||||||
|
LOG_PRINT_L4("[" << m_socket <<"] SEND " << (int)in_buff.size());
|
||||||
|
res = ::send(m_socket, in_buff.data(), (int)in_buff.size(), 0);
|
||||||
|
if(SOCKET_ERROR == res)
|
||||||
|
{
|
||||||
|
int err = ::WSAGetLastError();
|
||||||
|
LOG_ERROR("Failed to send(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
|
||||||
|
disconnect();
|
||||||
|
return LEVIN_ERROR_CONNECTION_DESTROYED;
|
||||||
|
}
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
LOG_PRINT_L4("LEVIN_PACKET_SENT. [len=" << head.m_cb << ", flags=" << head.m_flags << ", is_cmd=" << head.m_have_to_return_data <<", cmd_id = " << head.m_command << ", pr_v=" << head.m_protocol_version << ", uid=" << string_tools::get_str_from_guid_a(head.m_id) << "]");
|
||||||
|
|
||||||
|
//hard coded timeout in 10 minutes for maximum invoke period. if it happens, it could mean only some real troubles.
|
||||||
|
boost::system_time timeout = boost::get_system_time()+ boost::posix_time::milliseconds(100);
|
||||||
|
size_t timeout_count = 0;
|
||||||
|
boost::unique_lock<boost::mutex> lock(m_invoke_event);
|
||||||
|
|
||||||
|
while(!boost::interprocess::ipcdetail::atomic_read32(&m_invoke_data_ready))
|
||||||
|
{
|
||||||
|
if(!m_invoke_cond.timed_wait(lock, timeout))
|
||||||
|
{
|
||||||
|
if(timeout_count < 10)
|
||||||
|
{
|
||||||
|
//workaround to avoid freezing at timed_wait called after notify_all.
|
||||||
|
timeout = boost::get_system_time()+ boost::posix_time::milliseconds(100);
|
||||||
|
++timeout_count;
|
||||||
|
continue;
|
||||||
|
}else if(timeout_count == 10)
|
||||||
|
{
|
||||||
|
//workaround to avoid freezing at timed_wait called after notify_all.
|
||||||
|
timeout = boost::get_system_time()+ boost::posix_time::minutes(10);
|
||||||
|
++timeout_count;
|
||||||
|
continue;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
LOG_PRINT("[" << m_socket <<"] Timeout on waiting invoke result. ", LOG_LEVEL_0);
|
||||||
|
//disconnect();
|
||||||
|
return LEVIN_ERROR_CONNECTION_TIMEDOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CRITICAL_REGION_BEGIN(m_local_invoke_buff_lock);
|
||||||
|
buff_out.swap(m_local_invoke_buff);
|
||||||
|
m_local_invoke_buff.clear();
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
return m_invoke_res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int notify(const GUID& target, int command, const std::string& in_buff)
|
||||||
|
{
|
||||||
|
if(!check_connection())
|
||||||
|
return LEVIN_ERROR_CONNECTION_DESTROYED;
|
||||||
|
|
||||||
|
bucket_head head = {0};
|
||||||
|
head.m_signature = LEVIN_SIGNATURE;
|
||||||
|
head.m_cb = in_buff.size();
|
||||||
|
head.m_have_to_return_data = false;
|
||||||
|
head.m_id = target;
|
||||||
|
#ifdef TRACE_LEVIN_PACKETS_BY_GUIDS
|
||||||
|
::UuidCreate(&head.m_id);
|
||||||
|
#endif
|
||||||
|
head.m_command = command;
|
||||||
|
head.m_protocol_version = LEVIN_PROTOCOL_VER_1;
|
||||||
|
head.m_flags = LEVIN_PACKET_REQUEST;
|
||||||
|
CRITICAL_REGION_BEGIN(m_send_lock);
|
||||||
|
LOG_PRINT_L4("[" << m_socket <<"] SEND " << sizeof(head));
|
||||||
|
int res = ::send(m_socket, (const char*)&head, sizeof(head), 0);
|
||||||
|
if(SOCKET_ERROR == res)
|
||||||
|
{
|
||||||
|
int err = ::WSAGetLastError();
|
||||||
|
LOG_ERROR("Failed to send(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
|
||||||
|
disconnect();
|
||||||
|
return LEVIN_ERROR_CONNECTION_DESTROYED;
|
||||||
|
}
|
||||||
|
LOG_PRINT_L4("[" << m_socket <<"] SEND " << (int)in_buff.size());
|
||||||
|
res = ::send(m_socket, in_buff.data(), (int)in_buff.size(), 0);
|
||||||
|
if(SOCKET_ERROR == res)
|
||||||
|
{
|
||||||
|
int err = ::WSAGetLastError();
|
||||||
|
LOG_ERROR("Failed to send(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
|
||||||
|
disconnect();
|
||||||
|
return LEVIN_ERROR_CONNECTION_DESTROYED;
|
||||||
|
}
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
LOG_PRINT_L4("LEVIN_PACKET_SENT. [len=" << head.m_cb << ", flags=" << head.m_flags << ", is_cmd=" << head.m_have_to_return_data <<", cmd_id = " << head.m_command << ", pr_v=" << head.m_protocol_version << ", uid=" << string_tools::get_str_from_guid_a(head.m_id) << "]");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool have_some_data(SOCKET sock, int interval = 1)
|
||||||
|
{
|
||||||
|
fd_set fds;
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(sock, &fds);
|
||||||
|
|
||||||
|
fd_set fdse;
|
||||||
|
FD_ZERO(&fdse);
|
||||||
|
FD_SET(sock, &fdse);
|
||||||
|
|
||||||
|
|
||||||
|
timeval tv;
|
||||||
|
tv.tv_sec = interval;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
|
||||||
|
int sel_res = select(0, &fds, 0, &fdse, &tv);
|
||||||
|
if(0 == sel_res)
|
||||||
|
return false;
|
||||||
|
else if(sel_res == SOCKET_ERROR)
|
||||||
|
{
|
||||||
|
if(m_is_stop)
|
||||||
|
return false;
|
||||||
|
int err_code = ::WSAGetLastError();
|
||||||
|
LOG_ERROR("Filed to call select, err code = " << err_code);
|
||||||
|
disconnect();
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
if(fds.fd_array[0])
|
||||||
|
{//some read operations was performed
|
||||||
|
return true;
|
||||||
|
}else if(fdse.fd_array[0])
|
||||||
|
{//some error was at the socket
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool reciev_and_process_incoming_data()
|
||||||
|
{
|
||||||
|
bucket_head head = {0};
|
||||||
|
boost::uint32_t conn_index = 0;
|
||||||
|
bool is_request = false;
|
||||||
|
std::string local_buff;
|
||||||
|
CRITICAL_REGION_BEGIN(m_reciev_packet_lock);//to protect from socket reconnect between head and body
|
||||||
|
|
||||||
|
if(!recv_n(m_socket, (char*)&head, sizeof(head)))
|
||||||
|
{
|
||||||
|
if(m_is_stop)
|
||||||
|
return false;
|
||||||
|
LOG_ERROR("Failed to recv_n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
conn_index = boost::interprocess::ipcdetail::atomic_read32(&m_current_connection_index);
|
||||||
|
|
||||||
|
if(head.m_signature!=LEVIN_SIGNATURE)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Signature missmatch in response");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_request = (head.m_protocol_version == LEVIN_PROTOCOL_VER_1 && head.m_flags&LEVIN_PACKET_REQUEST);
|
||||||
|
|
||||||
|
|
||||||
|
local_buff.resize((size_t)head.m_cb);
|
||||||
|
if(!recv_n(m_socket, local_buff))
|
||||||
|
{
|
||||||
|
if(m_is_stop)
|
||||||
|
return false;
|
||||||
|
LOG_ERROR("Filed to reciev");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
|
||||||
|
LOG_PRINT_L4("LEVIN_PACKET_RECIEVED. [len=" << head.m_cb << ", flags=" << head.m_flags << ", is_cmd=" << head.m_have_to_return_data <<", cmd_id = " << head.m_command << ", pr_v=" << head.m_protocol_version << ", uid=" << string_tools::get_str_from_guid_a(head.m_id) << "]");
|
||||||
|
|
||||||
|
if(is_request)
|
||||||
|
{
|
||||||
|
CRITICAL_REGION_BEGIN(m_recieved_packets_lock);
|
||||||
|
m_recieved_packets.resize(m_recieved_packets.size() + 1);
|
||||||
|
m_recieved_packets.back().m_hd = head;
|
||||||
|
m_recieved_packets.back().m_body.swap(local_buff);
|
||||||
|
m_recieved_packets.back().m_connection_index = conn_index;
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
/*
|
||||||
|
|
||||||
|
*/
|
||||||
|
}else
|
||||||
|
{//this is some response
|
||||||
|
|
||||||
|
CRITICAL_REGION_BEGIN(m_local_invoke_buff_lock);
|
||||||
|
m_local_invoke_buff.swap(local_buff);
|
||||||
|
m_invoke_res = head.m_return_code;
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
boost::interprocess::ipcdetail::atomic_write32(&m_invoke_data_ready, 1); //m_invoke_data_ready = true;
|
||||||
|
m_invoke_cond.notify_all();
|
||||||
|
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reciever_thread()
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("[" << m_socket <<"] Socket reciever thread started.[m_threads_count=" << m_threads_count << "]");
|
||||||
|
log_space::log_singletone::set_thread_log_prefix("RECIEVER_WORKER");
|
||||||
|
boost::interprocess::ipcdetail::atomic_inc32(&m_threads_count);
|
||||||
|
|
||||||
|
while(!m_is_stop)
|
||||||
|
{
|
||||||
|
if(!m_connected)
|
||||||
|
{
|
||||||
|
Sleep(100);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(have_some_data(m_socket, 1))
|
||||||
|
{
|
||||||
|
if(!reciev_and_process_incoming_data())
|
||||||
|
{
|
||||||
|
if(m_is_stop)
|
||||||
|
{
|
||||||
|
break;//boost::interprocess::ipcdetail::atomic_dec32(&m_threads_count);
|
||||||
|
//return true;
|
||||||
|
}
|
||||||
|
LOG_ERROR("Failed to reciev_and_process_incoming_data. shutting down");
|
||||||
|
//boost::interprocess::ipcdetail::atomic_dec32(&m_threads_count);
|
||||||
|
//disconnect_no_wait();
|
||||||
|
//break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::interprocess::ipcdetail::atomic_dec32(&m_threads_count);
|
||||||
|
LOG_PRINT_L3("[" << m_socket <<"] Socket reciever thread stopped.[m_threads_count=" << m_threads_count << "]");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool process_recieved_packet(bucket_head& head, const std::string& local_buff, boost::uint32_t conn_index)
|
||||||
|
{
|
||||||
|
|
||||||
|
net_utils::connection_context_base conn_context;
|
||||||
|
conn_context.m_remote_ip = m_ip;
|
||||||
|
conn_context.m_remote_port = m_port;
|
||||||
|
if(head.m_have_to_return_data)
|
||||||
|
{
|
||||||
|
std::string return_buff;
|
||||||
|
if(m_pcommands_handler)
|
||||||
|
head.m_return_code = m_pcommands_handler->invoke(head.m_id, head.m_command, local_buff, return_buff, conn_context);
|
||||||
|
else
|
||||||
|
head.m_return_code = LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
head.m_cb = return_buff.size();
|
||||||
|
head.m_have_to_return_data = false;
|
||||||
|
head.m_protocol_version = LEVIN_PROTOCOL_VER_1;
|
||||||
|
head.m_flags = LEVIN_PACKET_RESPONSE;
|
||||||
|
|
||||||
|
std::string send_buff((const char*)&head, sizeof(head));
|
||||||
|
send_buff += return_buff;
|
||||||
|
CRITICAL_REGION_BEGIN(m_send_lock);
|
||||||
|
if(conn_index != boost::interprocess::ipcdetail::atomic_read32(&m_current_connection_index))
|
||||||
|
{//there was reconnect, send response back is not allowed
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
int res = ::send(m_socket, (const char*)send_buff.data(), send_buff.size(), 0);
|
||||||
|
if(res == SOCKET_ERROR)
|
||||||
|
{
|
||||||
|
int err_code = ::WSAGetLastError();
|
||||||
|
LOG_ERROR("Failed to send, err = " << err_code);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
LOG_PRINT_L4("LEVIN_PACKET_SENT. [len=" << head.m_cb << ", flags=" << head.m_flags << ", is_cmd=" << head.m_have_to_return_data <<", cmd_id = " << head.m_command << ", pr_v=" << head.m_protocol_version << ", uid=" << string_tools::get_str_from_guid_a(head.m_id) << "]");
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(m_pcommands_handler)
|
||||||
|
m_pcommands_handler->notify(head.m_id, head.m_command, local_buff, conn_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool handler_thread()
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("[" << m_socket <<"] Socket handler thread started.[m_threads_count=" << m_threads_count << "]");
|
||||||
|
log_space::log_singletone::set_thread_log_prefix("HANDLER_WORKER");
|
||||||
|
boost::interprocess::ipcdetail::atomic_inc32(&m_threads_count);
|
||||||
|
|
||||||
|
while(!m_is_stop)
|
||||||
|
{
|
||||||
|
bool have_some_work = false;
|
||||||
|
std::string local_buff;
|
||||||
|
bucket_head bh = {0};
|
||||||
|
boost::uint32_t conn_index = 0;
|
||||||
|
|
||||||
|
CRITICAL_REGION_BEGIN(m_recieved_packets_lock);
|
||||||
|
if(m_recieved_packets.size())
|
||||||
|
{
|
||||||
|
bh = m_recieved_packets.begin()->m_hd;
|
||||||
|
conn_index = m_recieved_packets.begin()->m_connection_index;
|
||||||
|
local_buff.swap(m_recieved_packets.begin()->m_body);
|
||||||
|
have_some_work = true;
|
||||||
|
m_recieved_packets.pop_front();
|
||||||
|
}
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
|
||||||
|
if(have_some_work)
|
||||||
|
{
|
||||||
|
process_recieved_packet(bh, local_buff, conn_index);
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
//Idle when no work
|
||||||
|
Sleep(30);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::interprocess::ipcdetail::atomic_dec32(&m_threads_count);
|
||||||
|
LOG_PRINT_L3("[" << m_socket <<"] Socket handler thread stopped.[m_threads_count=" << m_threads_count << "]");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 "levin_base.h"
|
||||||
|
#include "serializeble_struct_helper.h"
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace levin
|
||||||
|
{
|
||||||
|
template<class t_struct>
|
||||||
|
bool pack_struct_to_levin_message(const t_struct& t, std::string& buff, int command_id)
|
||||||
|
{
|
||||||
|
buff.resize(sizeof(levin::bucket_head));
|
||||||
|
levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]);
|
||||||
|
head.m_signature = LEVIN_SIGNATURE;
|
||||||
|
head.m_cb = 0;
|
||||||
|
head.m_have_to_return_data = true;
|
||||||
|
head.m_command = command_id;
|
||||||
|
head.m_return_code = 1;
|
||||||
|
head.m_reservedA = rand(); //probably some flags in future
|
||||||
|
head.m_reservedB = rand(); //probably some check summ in future
|
||||||
|
|
||||||
|
std::string buff_strg;
|
||||||
|
if(!StorageNamed::save_struct_as_storage_to_buff_t<t_struct, StorageNamed::DefaultStorageType>(t, buff_strg))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
head.m_cb = buff_strg.size();
|
||||||
|
buff.append(buff_strg);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool pack_data_to_levin_message(const std::string& data, std::string& buff, int command_id)
|
||||||
|
{
|
||||||
|
buff.resize(sizeof(levin::bucket_head));
|
||||||
|
levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]);
|
||||||
|
head.m_signature = LEVIN_SIGNATURE;
|
||||||
|
head.m_cb = 0;
|
||||||
|
head.m_have_to_return_data = true;
|
||||||
|
head.m_command = command_id;
|
||||||
|
head.m_return_code = 1;
|
||||||
|
head.m_reservedA = rand(); //probably some flags in future
|
||||||
|
head.m_reservedB = rand(); //probably some check summ in future
|
||||||
|
|
||||||
|
head.m_cb = data.size();
|
||||||
|
buff.append(data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool load_levin_data_from_levin_message(std::string& levin_data, const std::string& buff, int& command)
|
||||||
|
{
|
||||||
|
if(buff.size() < sizeof(levin::bucket_head) )
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("size of buff(" << buff.size() << ") is too small, at load_struct_from_levin_message");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]);
|
||||||
|
if(head.m_signature != LEVIN_SIGNATURE)
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("Failed to read signature in levin message, at load_struct_from_levin_message");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(head.m_cb != buff.size()-sizeof(levin::bucket_head))
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("sizes missmatch, at load_struct_from_levin_message");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//std::string buff_strg;
|
||||||
|
levin_data.assign(&buff[sizeof(levin::bucket_head)], buff.size()-sizeof(levin::bucket_head));
|
||||||
|
command = head.m_command;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class t_struct>
|
||||||
|
bool load_struct_from_levin_message(t_struct& t, const std::string& buff, int& command)
|
||||||
|
{
|
||||||
|
if(buff.size() < sizeof(levin::bucket_head) )
|
||||||
|
{
|
||||||
|
LOG_ERROR("size of buff(" << buff.size() << ") is too small, at load_struct_from_levin_message");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]);
|
||||||
|
if(head.m_signature != LEVIN_SIGNATURE)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to read signature in levin message, at load_struct_from_levin_message");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(head.m_cb != buff.size()-sizeof(levin::bucket_head))
|
||||||
|
{
|
||||||
|
LOG_ERROR("sizes missmatch, at load_struct_from_levin_message");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string buff_strg;
|
||||||
|
buff_strg.assign(&buff[sizeof(levin::bucket_head)], buff.size()-sizeof(levin::bucket_head));
|
||||||
|
|
||||||
|
if(!StorageNamed::load_struct_from_storage_buff_t<t_struct, StorageNamed::DefaultStorageType>(t, buff_strg))
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to read storage, at load_struct_from_levin_message");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
command = head.m_command;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,178 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _LEVIN_PROTOCOL_HANDLER_H_
|
||||||
|
#define _LEVIN_PROTOCOL_HANDLER_H_
|
||||||
|
|
||||||
|
#include <boost/uuid/uuid_generators.hpp>
|
||||||
|
#include "levin_base.h"
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace levin
|
||||||
|
{
|
||||||
|
template<class t_connection_context = net_utils::connection_context_base>
|
||||||
|
struct protocl_handler_config
|
||||||
|
{
|
||||||
|
levin_commands_handler<t_connection_context>* m_pcommands_handler;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class t_connection_context = net_utils::connection_context_base>
|
||||||
|
class protocol_handler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef t_connection_context connection_context;
|
||||||
|
typedef protocl_handler_config<t_connection_context> config_type;
|
||||||
|
|
||||||
|
protocol_handler(net_utils::i_service_endpoint* psnd_hndlr, config_type& config, t_connection_context& conn_context);
|
||||||
|
virtual ~protocol_handler(){}
|
||||||
|
|
||||||
|
virtual bool handle_recv(const void* ptr, size_t cb);
|
||||||
|
|
||||||
|
bool after_init_connection(){return true;}
|
||||||
|
private:
|
||||||
|
enum connection_data_state
|
||||||
|
{
|
||||||
|
conn_state_reading_head,
|
||||||
|
conn_state_reading_body
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
config_type& m_config;
|
||||||
|
t_connection_context& m_conn_context;
|
||||||
|
net_utils::i_service_endpoint* m_psnd_hndlr;
|
||||||
|
std::string m_cach_in_buffer;
|
||||||
|
connection_data_state m_state;
|
||||||
|
bucket_head m_current_head;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class t_connection_context>
|
||||||
|
protocol_handler<t_connection_context>::protocol_handler(net_utils::i_service_endpoint* psnd_hndlr, config_type& config, t_connection_context& conn_context):
|
||||||
|
m_config(config),
|
||||||
|
m_conn_context(conn_context),
|
||||||
|
m_psnd_hndlr(psnd_hndlr),
|
||||||
|
m_state(conn_state_reading_head),
|
||||||
|
m_current_head(bucket_head())
|
||||||
|
{}
|
||||||
|
|
||||||
|
template<class t_connection_context>
|
||||||
|
bool protocol_handler<t_connection_context>::handle_recv(const void* ptr, size_t cb)
|
||||||
|
{
|
||||||
|
if(!m_config.m_pcommands_handler)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Command handler not set!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_cach_in_buffer.append((const char*)ptr, cb);
|
||||||
|
|
||||||
|
bool is_continue = true;
|
||||||
|
while(is_continue)
|
||||||
|
{
|
||||||
|
switch(m_state)
|
||||||
|
{
|
||||||
|
case conn_state_reading_head:
|
||||||
|
if(m_cach_in_buffer.size() < sizeof(bucket_head))
|
||||||
|
{
|
||||||
|
if(m_cach_in_buffer.size() >= sizeof(boost::uint64_t) && *((boost::uint64_t*)m_cach_in_buffer.data()) != LEVIN_SIGNATURE)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Signature missmatch on accepted connection");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
is_continue = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
bucket_head* phead = (bucket_head*)m_cach_in_buffer.data();
|
||||||
|
if(LEVIN_SIGNATURE != phead->m_signature)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Signature missmatch on accepted connection");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_current_head = *phead;
|
||||||
|
}
|
||||||
|
m_cach_in_buffer.erase(0, sizeof(bucket_head));
|
||||||
|
m_state = conn_state_reading_body;
|
||||||
|
break;
|
||||||
|
case conn_state_reading_body:
|
||||||
|
if(m_cach_in_buffer.size() < m_current_head.m_cb)
|
||||||
|
{
|
||||||
|
is_continue = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::string buff_to_invoke;
|
||||||
|
if(m_cach_in_buffer.size() == m_current_head.m_cb)
|
||||||
|
buff_to_invoke.swap(m_cach_in_buffer);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buff_to_invoke.assign(m_cach_in_buffer, 0, (std::string::size_type)m_current_head.m_cb);
|
||||||
|
m_cach_in_buffer.erase(0, (std::string::size_type)m_current_head.m_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(m_current_head.m_have_to_return_data)
|
||||||
|
{
|
||||||
|
std::string return_buff;
|
||||||
|
m_current_head.m_return_code = m_config.m_pcommands_handler->invoke(m_current_head.m_command, buff_to_invoke, return_buff, m_conn_context);
|
||||||
|
m_current_head.m_cb = return_buff.size();
|
||||||
|
m_current_head.m_have_to_return_data = false;
|
||||||
|
std::string send_buff((const char*)&m_current_head, sizeof(m_current_head));
|
||||||
|
send_buff += return_buff;
|
||||||
|
|
||||||
|
if(!m_psnd_hndlr->do_send(send_buff.data(), send_buff.size()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_config.m_pcommands_handler->notify(m_current_head.m_command, buff_to_invoke, m_conn_context);
|
||||||
|
}
|
||||||
|
m_state = conn_state_reading_head;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_ERROR("Undefined state in levin_server_impl::connection_handler, m_state=" << m_state);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif //_LEVIN_PROTOCOL_HANDLER_H_
|
||||||
|
|
|
@ -0,0 +1,778 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 <boost/uuid/uuid_generators.hpp>
|
||||||
|
#include <boost/interprocess/detail/atomic.hpp>
|
||||||
|
#include <boost/smart_ptr/make_shared.hpp>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
#include "levin_base.h"
|
||||||
|
#include "misc_language.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace levin
|
||||||
|
{
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
template<class t_connection_context>
|
||||||
|
class async_protocol_handler;
|
||||||
|
|
||||||
|
template<class t_connection_context>
|
||||||
|
class async_protocol_handler_config
|
||||||
|
{
|
||||||
|
typedef std::map<boost::uuids::uuid, async_protocol_handler<t_connection_context>* > connections_map;
|
||||||
|
critical_section m_connects_lock;
|
||||||
|
connections_map m_connects;
|
||||||
|
|
||||||
|
void add_connection(async_protocol_handler<t_connection_context>* pc);
|
||||||
|
void del_connection(async_protocol_handler<t_connection_context>* pc);
|
||||||
|
|
||||||
|
async_protocol_handler<t_connection_context>* find_connection(boost::uuids::uuid connection_id) const;
|
||||||
|
int find_and_lock_connection(boost::uuids::uuid connection_id, async_protocol_handler<t_connection_context>*& aph);
|
||||||
|
|
||||||
|
friend class async_protocol_handler<t_connection_context>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef t_connection_context connection_context;
|
||||||
|
levin_commands_handler<t_connection_context>* m_pcommands_handler;
|
||||||
|
boost::uint64_t m_max_packet_size;
|
||||||
|
boost::uint64_t m_invoke_timeout;
|
||||||
|
|
||||||
|
int invoke(int command, const std::string& in_buff, std::string& buff_out, boost::uuids::uuid connection_id);
|
||||||
|
template<class callback_t>
|
||||||
|
int invoke_async(int command, const std::string& in_buff, boost::uuids::uuid connection_id, callback_t cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED);
|
||||||
|
|
||||||
|
int notify(int command, const std::string& in_buff, boost::uuids::uuid connection_id);
|
||||||
|
bool close(boost::uuids::uuid connection_id);
|
||||||
|
bool update_connection_context(const t_connection_context& contxt);
|
||||||
|
bool request_callback(boost::uuids::uuid connection_id);
|
||||||
|
template<class callback_t>
|
||||||
|
bool foreach_connection(callback_t cb);
|
||||||
|
size_t get_connections_count();
|
||||||
|
|
||||||
|
async_protocol_handler_config():m_pcommands_handler(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
template<class t_connection_context = net_utils::connection_context_base>
|
||||||
|
class async_protocol_handler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef t_connection_context connection_context;
|
||||||
|
typedef async_protocol_handler_config<t_connection_context> config_type;
|
||||||
|
|
||||||
|
enum stream_state
|
||||||
|
{
|
||||||
|
stream_state_head,
|
||||||
|
stream_state_body
|
||||||
|
};
|
||||||
|
|
||||||
|
std::atomic<bool> m_deletion_initiated;
|
||||||
|
std::atomic<bool> m_protocol_released;
|
||||||
|
volatile uint32_t m_invoke_buf_ready;
|
||||||
|
|
||||||
|
volatile int m_invoke_result_code;
|
||||||
|
|
||||||
|
critical_section m_local_inv_buff_lock;
|
||||||
|
std::string m_local_inv_buff;
|
||||||
|
|
||||||
|
critical_section m_send_lock;
|
||||||
|
critical_section m_call_lock;
|
||||||
|
|
||||||
|
volatile uint32_t m_wait_count;
|
||||||
|
volatile uint32_t m_close_called;
|
||||||
|
bucket_head2 m_current_head;
|
||||||
|
net_utils::i_service_endpoint* m_pservice_endpoint;
|
||||||
|
config_type& m_config;
|
||||||
|
t_connection_context& m_connection_context;
|
||||||
|
|
||||||
|
std::string m_cache_in_buffer;
|
||||||
|
stream_state m_state;
|
||||||
|
|
||||||
|
boost::int32_t m_oponent_protocol_ver;
|
||||||
|
bool m_connection_initialized;
|
||||||
|
|
||||||
|
struct invoke_response_handler_base
|
||||||
|
{
|
||||||
|
virtual bool handle(int res, const std::string& buff, connection_context& context)=0;
|
||||||
|
virtual bool is_timer_started() const=0;
|
||||||
|
virtual void cancel()=0;
|
||||||
|
virtual bool cancel_timer()=0;
|
||||||
|
};
|
||||||
|
template <class callback_t>
|
||||||
|
struct anvoke_handler: invoke_response_handler_base
|
||||||
|
{
|
||||||
|
anvoke_handler(const callback_t& cb, uint64_t timeout, async_protocol_handler& con, int command)
|
||||||
|
:m_cb(cb), m_con(con), m_timer(con.m_pservice_endpoint->get_io_service()), m_timer_started(false),
|
||||||
|
m_cancel_timer_called(false), m_timer_cancelled(false), m_command(command)
|
||||||
|
{
|
||||||
|
if(m_con.start_outer_call())
|
||||||
|
{
|
||||||
|
m_timer.expires_from_now(boost::posix_time::milliseconds(timeout));
|
||||||
|
m_timer.async_wait([&con, command, cb](const boost::system::error_code& ec)
|
||||||
|
{
|
||||||
|
if(ec == boost::asio::error::operation_aborted)
|
||||||
|
return;
|
||||||
|
LOG_PRINT_CC(con.get_context_ref(), "Timeout on invoke operation happened, command: " << command, LOG_LEVEL_2);
|
||||||
|
std::string fake;
|
||||||
|
cb(LEVIN_ERROR_CONNECTION_TIMEDOUT, fake, con.get_context_ref());
|
||||||
|
con.close();
|
||||||
|
con.finish_outer_call();
|
||||||
|
});
|
||||||
|
m_timer_started = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual ~anvoke_handler()
|
||||||
|
{}
|
||||||
|
callback_t m_cb;
|
||||||
|
async_protocol_handler& m_con;
|
||||||
|
boost::asio::deadline_timer m_timer;
|
||||||
|
bool m_timer_started;
|
||||||
|
bool m_cancel_timer_called;
|
||||||
|
bool m_timer_cancelled;
|
||||||
|
int m_command;
|
||||||
|
virtual bool handle(int res, const std::string& buff, typename async_protocol_handler::connection_context& context)
|
||||||
|
{
|
||||||
|
if(!cancel_timer())
|
||||||
|
return false;
|
||||||
|
m_cb(res, buff, context);
|
||||||
|
m_con.finish_outer_call();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
virtual bool is_timer_started() const
|
||||||
|
{
|
||||||
|
return m_timer_started;
|
||||||
|
}
|
||||||
|
virtual void cancel()
|
||||||
|
{
|
||||||
|
if(cancel_timer())
|
||||||
|
{
|
||||||
|
std::string fake;
|
||||||
|
m_cb(LEVIN_ERROR_CONNECTION_DESTROYED, fake, m_con.get_context_ref());
|
||||||
|
m_con.finish_outer_call();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual bool cancel_timer()
|
||||||
|
{
|
||||||
|
if(!m_cancel_timer_called)
|
||||||
|
{
|
||||||
|
m_cancel_timer_called = true;
|
||||||
|
boost::system::error_code ignored_ec;
|
||||||
|
m_timer_cancelled = 1 == m_timer.cancel(ignored_ec);
|
||||||
|
}
|
||||||
|
return m_timer_cancelled;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
critical_section m_invoke_response_handlers_lock;
|
||||||
|
std::list<boost::shared_ptr<invoke_response_handler_base> > m_invoke_response_handlers;
|
||||||
|
|
||||||
|
template<class callback_t>
|
||||||
|
bool add_invoke_response_handler(callback_t cb, uint64_t timeout, async_protocol_handler& con, int command)
|
||||||
|
{
|
||||||
|
CRITICAL_REGION_LOCAL(m_invoke_response_handlers_lock);
|
||||||
|
boost::shared_ptr<invoke_response_handler_base> handler(boost::make_shared<anvoke_handler<callback_t>>(cb, timeout, con, command));
|
||||||
|
m_invoke_response_handlers.push_back(handler);
|
||||||
|
return handler->is_timer_started();
|
||||||
|
}
|
||||||
|
template<class callback_t> friend struct anvoke_handler;
|
||||||
|
public:
|
||||||
|
async_protocol_handler(net_utils::i_service_endpoint* psnd_hndlr,
|
||||||
|
config_type& config,
|
||||||
|
t_connection_context& conn_context):
|
||||||
|
m_current_head(bucket_head2()),
|
||||||
|
m_pservice_endpoint(psnd_hndlr),
|
||||||
|
m_config(config),
|
||||||
|
m_connection_context(conn_context),
|
||||||
|
m_state(stream_state_head)
|
||||||
|
{
|
||||||
|
m_close_called = 0;
|
||||||
|
m_deletion_initiated = false;
|
||||||
|
m_protocol_released = false;
|
||||||
|
m_wait_count = 0;
|
||||||
|
m_oponent_protocol_ver = 0;
|
||||||
|
m_connection_initialized = false;
|
||||||
|
}
|
||||||
|
virtual ~async_protocol_handler()
|
||||||
|
{
|
||||||
|
m_deletion_initiated = true;
|
||||||
|
if(m_connection_initialized)
|
||||||
|
{
|
||||||
|
m_config.del_connection(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 60 * 1000 / 100 && 0 != boost::interprocess::ipcdetail::atomic_read32(&m_wait_count); ++i)
|
||||||
|
{
|
||||||
|
misc_utils::sleep_no_w(100);
|
||||||
|
}
|
||||||
|
CHECK_AND_ASSERT_MES_NO_RET(0 == boost::interprocess::ipcdetail::atomic_read32(&m_wait_count), "Failed to wait for operation completion. m_wait_count = " << m_wait_count);
|
||||||
|
|
||||||
|
LOG_PRINT_CC(m_connection_context, "~async_protocol_handler()", LOG_LEVEL_4);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool start_outer_call()
|
||||||
|
{
|
||||||
|
LOG_PRINT_CC_L4(m_connection_context, "[levin_protocol] -->> start_outer_call");
|
||||||
|
if(!m_pservice_endpoint->add_ref())
|
||||||
|
{
|
||||||
|
LOG_PRINT_CC_RED(m_connection_context, "[levin_protocol] -->> start_outer_call failed", LOG_LEVEL_4);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
boost::interprocess::ipcdetail::atomic_inc32(&m_wait_count);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool finish_outer_call()
|
||||||
|
{
|
||||||
|
LOG_PRINT_CC_L4(m_connection_context, "[levin_protocol] <<-- finish_outer_call");
|
||||||
|
boost::interprocess::ipcdetail::atomic_dec32(&m_wait_count);
|
||||||
|
m_pservice_endpoint->release();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool release_protocol()
|
||||||
|
{
|
||||||
|
decltype(m_invoke_response_handlers) local_invoke_response_handlers;
|
||||||
|
CRITICAL_REGION_BEGIN(m_invoke_response_handlers_lock);
|
||||||
|
local_invoke_response_handlers.swap(m_invoke_response_handlers);
|
||||||
|
m_protocol_released = true;
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
|
||||||
|
// Never call callback inside critical section, that can cause deadlock. Callback can be called when
|
||||||
|
// invoke_response_handler_base is cancelled
|
||||||
|
std::for_each(local_invoke_response_handlers.begin(), local_invoke_response_handlers.end(), [](const boost::shared_ptr<invoke_response_handler_base>& pinv_resp_hndlr) {
|
||||||
|
pinv_resp_hndlr->cancel();
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool close()
|
||||||
|
{
|
||||||
|
boost::interprocess::ipcdetail::atomic_inc32(&m_close_called);
|
||||||
|
|
||||||
|
m_pservice_endpoint->close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_connection_context(const connection_context& contxt)
|
||||||
|
{
|
||||||
|
m_connection_context = contxt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void request_callback()
|
||||||
|
{
|
||||||
|
misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler(
|
||||||
|
boost::bind(&async_protocol_handler::finish_outer_call, this));
|
||||||
|
|
||||||
|
m_pservice_endpoint->request_callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_qued_callback()
|
||||||
|
{
|
||||||
|
m_config.m_pcommands_handler->callback(m_connection_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool handle_recv(const void* ptr, size_t cb)
|
||||||
|
{
|
||||||
|
if(boost::interprocess::ipcdetail::atomic_read32(&m_close_called))
|
||||||
|
return false; //closing connections
|
||||||
|
|
||||||
|
if(!m_config.m_pcommands_handler)
|
||||||
|
{
|
||||||
|
LOG_ERROR_CC(m_connection_context, "Commands handler not set!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m_cache_in_buffer.size() + cb > m_config.m_max_packet_size)
|
||||||
|
{
|
||||||
|
LOG_ERROR_CC(m_connection_context, "Maximum packet size exceed!, m_max_packet_size = " << m_config.m_max_packet_size
|
||||||
|
<< ", packet received " << m_cache_in_buffer.size() + cb
|
||||||
|
<< ", connection will be closed.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cache_in_buffer.append((const char*)ptr, cb);
|
||||||
|
|
||||||
|
bool is_continue = true;
|
||||||
|
while(is_continue)
|
||||||
|
{
|
||||||
|
switch(m_state)
|
||||||
|
{
|
||||||
|
case stream_state_body:
|
||||||
|
if(m_cache_in_buffer.size() < m_current_head.m_cb)
|
||||||
|
{
|
||||||
|
is_continue = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::string buff_to_invoke;
|
||||||
|
if(m_cache_in_buffer.size() == m_current_head.m_cb)
|
||||||
|
buff_to_invoke.swap(m_cache_in_buffer);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buff_to_invoke.assign(m_cache_in_buffer, 0, (std::string::size_type)m_current_head.m_cb);
|
||||||
|
m_cache_in_buffer.erase(0, (std::string::size_type)m_current_head.m_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_response = (m_oponent_protocol_ver == LEVIN_PROTOCOL_VER_1 && m_current_head.m_flags&LEVIN_PACKET_RESPONSE);
|
||||||
|
|
||||||
|
LOG_PRINT_CC_L4(m_connection_context, "LEVIN_PACKET_RECIEVED. [len=" << m_current_head.m_cb
|
||||||
|
<< ", flags" << m_current_head.m_flags
|
||||||
|
<< ", r?=" << m_current_head.m_have_to_return_data
|
||||||
|
<<", cmd = " << m_current_head.m_command
|
||||||
|
<< ", v=" << m_current_head.m_protocol_version);
|
||||||
|
|
||||||
|
if(is_response)
|
||||||
|
{//response to some invoke
|
||||||
|
|
||||||
|
epee::critical_region_t<decltype(m_invoke_response_handlers_lock)> invoke_response_handlers_guard(m_invoke_response_handlers_lock);
|
||||||
|
if(!m_invoke_response_handlers.empty())
|
||||||
|
{//async call scenario
|
||||||
|
boost::shared_ptr<invoke_response_handler_base> response_handler = m_invoke_response_handlers.front();
|
||||||
|
bool timer_cancelled = response_handler->cancel_timer();
|
||||||
|
// Don't pop handler, to avoid destroying it
|
||||||
|
if(timer_cancelled)
|
||||||
|
m_invoke_response_handlers.pop_front();
|
||||||
|
invoke_response_handlers_guard.unlock();
|
||||||
|
|
||||||
|
if(timer_cancelled)
|
||||||
|
response_handler->handle(m_current_head.m_command, buff_to_invoke, m_connection_context);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
invoke_response_handlers_guard.unlock();
|
||||||
|
//use sync call scenario
|
||||||
|
if(!boost::interprocess::ipcdetail::atomic_read32(&m_wait_count) && !boost::interprocess::ipcdetail::atomic_read32(&m_close_called))
|
||||||
|
{
|
||||||
|
LOG_ERROR_CC(m_connection_context, "no active invoke when response came, wtf?");
|
||||||
|
return false;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
CRITICAL_REGION_BEGIN(m_local_inv_buff_lock);
|
||||||
|
buff_to_invoke.swap(m_local_inv_buff);
|
||||||
|
buff_to_invoke.clear();
|
||||||
|
m_invoke_result_code = m_current_head.m_return_code;
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
if(m_current_head.m_have_to_return_data)
|
||||||
|
{
|
||||||
|
std::string return_buff;
|
||||||
|
m_current_head.m_return_code = m_config.m_pcommands_handler->invoke(
|
||||||
|
m_current_head.m_command,
|
||||||
|
buff_to_invoke,
|
||||||
|
return_buff,
|
||||||
|
m_connection_context);
|
||||||
|
m_current_head.m_cb = return_buff.size();
|
||||||
|
m_current_head.m_have_to_return_data = false;
|
||||||
|
m_current_head.m_protocol_version = LEVIN_PROTOCOL_VER_1;
|
||||||
|
m_current_head.m_flags = LEVIN_PACKET_RESPONSE;
|
||||||
|
std::string send_buff((const char*)&m_current_head, sizeof(m_current_head));
|
||||||
|
send_buff += return_buff;
|
||||||
|
CRITICAL_REGION_BEGIN(m_send_lock);
|
||||||
|
if(!m_pservice_endpoint->do_send(send_buff.data(), send_buff.size()))
|
||||||
|
return false;
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
LOG_PRINT_CC_L4(m_connection_context, "LEVIN_PACKET_SENT. [len=" << m_current_head.m_cb
|
||||||
|
<< ", flags" << m_current_head.m_flags
|
||||||
|
<< ", r?=" << m_current_head.m_have_to_return_data
|
||||||
|
<<", cmd = " << m_current_head.m_command
|
||||||
|
<< ", ver=" << m_current_head.m_protocol_version);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_config.m_pcommands_handler->notify(m_current_head.m_command, buff_to_invoke, m_connection_context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_state = stream_state_head;
|
||||||
|
break;
|
||||||
|
case stream_state_head:
|
||||||
|
{
|
||||||
|
if(m_cache_in_buffer.size() < sizeof(bucket_head2))
|
||||||
|
{
|
||||||
|
if(m_cache_in_buffer.size() >= sizeof(boost::uint64_t) && *((boost::uint64_t*)m_cache_in_buffer.data()) != LEVIN_SIGNATURE)
|
||||||
|
{
|
||||||
|
LOG_ERROR_CC(m_connection_context, "Signature mismatch, connection will be closed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
is_continue = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket_head2* phead = (bucket_head2*)m_cache_in_buffer.data();
|
||||||
|
if(LEVIN_SIGNATURE != phead->m_signature)
|
||||||
|
{
|
||||||
|
LOG_ERROR_CC(m_connection_context, "Signature mismatch, connection will be closed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_current_head = *phead;
|
||||||
|
|
||||||
|
m_cache_in_buffer.erase(0, sizeof(bucket_head2));
|
||||||
|
m_state = stream_state_body;
|
||||||
|
m_oponent_protocol_ver = m_current_head.m_protocol_version;
|
||||||
|
if(m_current_head.m_cb > m_config.m_max_packet_size)
|
||||||
|
{
|
||||||
|
LOG_ERROR_CC(m_connection_context, "Maximum packet size exceed!, m_max_packet_size = " << m_config.m_max_packet_size
|
||||||
|
<< ", packet header received " << m_current_head.m_cb
|
||||||
|
<< ", connection will be closed.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_ERROR_CC(m_connection_context, "Undefined state in levin_server_impl::connection_handler, m_state=" << m_state);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool after_init_connection()
|
||||||
|
{
|
||||||
|
if (!m_connection_initialized)
|
||||||
|
{
|
||||||
|
m_connection_initialized = true;
|
||||||
|
m_config.add_connection(this);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class callback_t>
|
||||||
|
bool async_invoke(int command, const std::string& in_buff, callback_t cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED)
|
||||||
|
{
|
||||||
|
misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler(
|
||||||
|
boost::bind(&async_protocol_handler::finish_outer_call, this));
|
||||||
|
|
||||||
|
if(timeout == LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED)
|
||||||
|
timeout = m_config.m_invoke_timeout;
|
||||||
|
|
||||||
|
int err_code = LEVIN_OK;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if(m_deletion_initiated)
|
||||||
|
{
|
||||||
|
err_code = LEVIN_ERROR_CONNECTION_DESTROYED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
CRITICAL_REGION_LOCAL(m_call_lock);
|
||||||
|
|
||||||
|
if(m_deletion_initiated)
|
||||||
|
{
|
||||||
|
err_code = LEVIN_ERROR_CONNECTION_DESTROYED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket_head2 head = {0};
|
||||||
|
head.m_signature = LEVIN_SIGNATURE;
|
||||||
|
head.m_cb = in_buff.size();
|
||||||
|
head.m_have_to_return_data = true;
|
||||||
|
|
||||||
|
head.m_flags = LEVIN_PACKET_REQUEST;
|
||||||
|
head.m_command = command;
|
||||||
|
head.m_protocol_version = LEVIN_PROTOCOL_VER_1;
|
||||||
|
|
||||||
|
boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 0);
|
||||||
|
CRITICAL_REGION_BEGIN(m_send_lock);
|
||||||
|
CRITICAL_REGION_LOCAL1(m_invoke_response_handlers_lock);
|
||||||
|
if(!m_pservice_endpoint->do_send(&head, sizeof(head)))
|
||||||
|
{
|
||||||
|
LOG_ERROR_CC(m_connection_context, "Failed to do_send");
|
||||||
|
err_code = LEVIN_ERROR_CONNECTION;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!m_pservice_endpoint->do_send(in_buff.data(), (int)in_buff.size()))
|
||||||
|
{
|
||||||
|
LOG_ERROR_CC(m_connection_context, "Failed to do_send");
|
||||||
|
err_code = LEVIN_ERROR_CONNECTION;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!add_invoke_response_handler(cb, timeout, *this, command))
|
||||||
|
{
|
||||||
|
err_code = LEVIN_ERROR_CONNECTION_DESTROYED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
} while (false);
|
||||||
|
|
||||||
|
if (LEVIN_OK != err_code)
|
||||||
|
{
|
||||||
|
std::string stub_buff;
|
||||||
|
// Never call callback inside critical section, that can cause deadlock
|
||||||
|
cb(err_code, stub_buff, m_connection_context);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int invoke(int command, const std::string& in_buff, std::string& buff_out)
|
||||||
|
{
|
||||||
|
misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler(
|
||||||
|
boost::bind(&async_protocol_handler::finish_outer_call, this));
|
||||||
|
|
||||||
|
if(m_deletion_initiated)
|
||||||
|
return LEVIN_ERROR_CONNECTION_DESTROYED;
|
||||||
|
|
||||||
|
CRITICAL_REGION_LOCAL(m_call_lock);
|
||||||
|
|
||||||
|
if(m_deletion_initiated)
|
||||||
|
return LEVIN_ERROR_CONNECTION_DESTROYED;
|
||||||
|
|
||||||
|
bucket_head2 head = {0};
|
||||||
|
head.m_signature = LEVIN_SIGNATURE;
|
||||||
|
head.m_cb = in_buff.size();
|
||||||
|
head.m_have_to_return_data = true;
|
||||||
|
|
||||||
|
head.m_flags = LEVIN_PACKET_REQUEST;
|
||||||
|
head.m_command = command;
|
||||||
|
head.m_protocol_version = LEVIN_PROTOCOL_VER_1;
|
||||||
|
|
||||||
|
boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 0);
|
||||||
|
CRITICAL_REGION_BEGIN(m_send_lock);
|
||||||
|
if(!m_pservice_endpoint->do_send(&head, sizeof(head)))
|
||||||
|
{
|
||||||
|
LOG_ERROR_CC(m_connection_context, "Failed to do_send");
|
||||||
|
return LEVIN_ERROR_CONNECTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!m_pservice_endpoint->do_send(in_buff.data(), (int)in_buff.size()))
|
||||||
|
{
|
||||||
|
LOG_ERROR_CC(m_connection_context, "Failed to do_send");
|
||||||
|
return LEVIN_ERROR_CONNECTION;
|
||||||
|
}
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
|
||||||
|
LOG_PRINT_CC_L4(m_connection_context, "LEVIN_PACKET_SENT. [len=" << head.m_cb
|
||||||
|
<< ", f=" << head.m_flags
|
||||||
|
<< ", r?=" << head.m_have_to_return_data
|
||||||
|
<< ", cmd = " << head.m_command
|
||||||
|
<< ", ver=" << head.m_protocol_version);
|
||||||
|
|
||||||
|
uint64_t ticks_start = misc_utils::get_tick_count();
|
||||||
|
|
||||||
|
while(!boost::interprocess::ipcdetail::atomic_read32(&m_invoke_buf_ready) && !m_deletion_initiated && !m_protocol_released)
|
||||||
|
{
|
||||||
|
if(misc_utils::get_tick_count() - ticks_start > m_config.m_invoke_timeout)
|
||||||
|
{
|
||||||
|
LOG_PRINT_CC_L2(m_connection_context, "invoke timeout (" << m_config.m_invoke_timeout << "), closing connection ");
|
||||||
|
close();
|
||||||
|
return LEVIN_ERROR_CONNECTION_TIMEDOUT;
|
||||||
|
}
|
||||||
|
if(!m_pservice_endpoint->call_run_once_service_io())
|
||||||
|
return LEVIN_ERROR_CONNECTION_DESTROYED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m_deletion_initiated || m_protocol_released)
|
||||||
|
return LEVIN_ERROR_CONNECTION_DESTROYED;
|
||||||
|
|
||||||
|
CRITICAL_REGION_BEGIN(m_local_inv_buff_lock);
|
||||||
|
buff_out.swap(m_local_inv_buff);
|
||||||
|
m_local_inv_buff.clear();
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
|
||||||
|
return m_invoke_result_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
int notify(int command, const std::string& in_buff)
|
||||||
|
{
|
||||||
|
misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler(
|
||||||
|
boost::bind(&async_protocol_handler::finish_outer_call, this));
|
||||||
|
|
||||||
|
if(m_deletion_initiated)
|
||||||
|
return LEVIN_ERROR_CONNECTION_DESTROYED;
|
||||||
|
|
||||||
|
CRITICAL_REGION_LOCAL(m_call_lock);
|
||||||
|
|
||||||
|
if(m_deletion_initiated)
|
||||||
|
return LEVIN_ERROR_CONNECTION_DESTROYED;
|
||||||
|
|
||||||
|
bucket_head2 head = {0};
|
||||||
|
head.m_signature = LEVIN_SIGNATURE;
|
||||||
|
head.m_have_to_return_data = false;
|
||||||
|
head.m_cb = in_buff.size();
|
||||||
|
|
||||||
|
head.m_command = command;
|
||||||
|
head.m_protocol_version = LEVIN_PROTOCOL_VER_1;
|
||||||
|
head.m_flags = LEVIN_PACKET_REQUEST;
|
||||||
|
CRITICAL_REGION_BEGIN(m_send_lock);
|
||||||
|
if(!m_pservice_endpoint->do_send(&head, sizeof(head)))
|
||||||
|
{
|
||||||
|
LOG_ERROR_CC(m_connection_context, "Failed to do_send()");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!m_pservice_endpoint->do_send(in_buff.data(), (int)in_buff.size()))
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to do_send()");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
LOG_PRINT_CC_L4(m_connection_context, "LEVIN_PACKET_SENT. [len=" << head.m_cb <<
|
||||||
|
", f=" << head.m_flags <<
|
||||||
|
", r?=" << head.m_have_to_return_data <<
|
||||||
|
", cmd = " << head.m_command <<
|
||||||
|
", ver=" << head.m_protocol_version);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------------------
|
||||||
|
boost::uuids::uuid get_connection_id() {return m_connection_context.m_connection_id;}
|
||||||
|
//------------------------------------------------------------------------------------------
|
||||||
|
t_connection_context& get_context_ref() {return m_connection_context;}
|
||||||
|
};
|
||||||
|
//------------------------------------------------------------------------------------------
|
||||||
|
template<class t_connection_context>
|
||||||
|
void async_protocol_handler_config<t_connection_context>::del_connection(async_protocol_handler<t_connection_context>* pconn)
|
||||||
|
{
|
||||||
|
CRITICAL_REGION_BEGIN(m_connects_lock);
|
||||||
|
m_connects.erase(pconn->get_connection_id());
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
m_pcommands_handler->on_connection_close(pconn->m_connection_context);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------------------
|
||||||
|
template<class t_connection_context>
|
||||||
|
void async_protocol_handler_config<t_connection_context>::add_connection(async_protocol_handler<t_connection_context>* pconn)
|
||||||
|
{
|
||||||
|
CRITICAL_REGION_BEGIN(m_connects_lock);
|
||||||
|
m_connects[pconn->get_connection_id()] = pconn;
|
||||||
|
CRITICAL_REGION_END();
|
||||||
|
m_pcommands_handler->on_connection_new(pconn->m_connection_context);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------------------
|
||||||
|
template<class t_connection_context>
|
||||||
|
async_protocol_handler<t_connection_context>* async_protocol_handler_config<t_connection_context>::find_connection(boost::uuids::uuid connection_id) const
|
||||||
|
{
|
||||||
|
auto it = m_connects.find(connection_id);
|
||||||
|
return it == m_connects.end() ? 0 : it->second;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------------------
|
||||||
|
template<class t_connection_context>
|
||||||
|
int async_protocol_handler_config<t_connection_context>::find_and_lock_connection(boost::uuids::uuid connection_id, async_protocol_handler<t_connection_context>*& aph)
|
||||||
|
{
|
||||||
|
CRITICAL_REGION_LOCAL(m_connects_lock);
|
||||||
|
aph = find_connection(connection_id);
|
||||||
|
if(0 == aph)
|
||||||
|
return LEVIN_ERROR_CONNECTION_NOT_FOUND;
|
||||||
|
if(!aph->start_outer_call())
|
||||||
|
return LEVIN_ERROR_CONNECTION_DESTROYED;
|
||||||
|
return LEVIN_OK;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------------------
|
||||||
|
template<class t_connection_context>
|
||||||
|
int async_protocol_handler_config<t_connection_context>::invoke(int command, const std::string& in_buff, std::string& buff_out, boost::uuids::uuid connection_id)
|
||||||
|
{
|
||||||
|
async_protocol_handler<t_connection_context>* aph;
|
||||||
|
int r = find_and_lock_connection(connection_id, aph);
|
||||||
|
return LEVIN_OK == r ? aph->invoke(command, in_buff, buff_out) : r;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------------------
|
||||||
|
template<class t_connection_context> template<class callback_t>
|
||||||
|
int async_protocol_handler_config<t_connection_context>::invoke_async(int command, const std::string& in_buff, boost::uuids::uuid connection_id, callback_t cb, size_t timeout)
|
||||||
|
{
|
||||||
|
async_protocol_handler<t_connection_context>* aph;
|
||||||
|
int r = find_and_lock_connection(connection_id, aph);
|
||||||
|
return LEVIN_OK == r ? aph->async_invoke(command, in_buff, cb, timeout) : r;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------------------
|
||||||
|
template<class t_connection_context> template<class callback_t>
|
||||||
|
bool async_protocol_handler_config<t_connection_context>::foreach_connection(callback_t cb)
|
||||||
|
{
|
||||||
|
CRITICAL_REGION_LOCAL(m_connects_lock);
|
||||||
|
for(auto& c: m_connects)
|
||||||
|
{
|
||||||
|
async_protocol_handler<t_connection_context>* aph = c.second;
|
||||||
|
if(!cb(aph->get_context_ref()))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------------------
|
||||||
|
template<class t_connection_context>
|
||||||
|
size_t async_protocol_handler_config<t_connection_context>::get_connections_count()
|
||||||
|
{
|
||||||
|
CRITICAL_REGION_LOCAL(m_connects_lock);
|
||||||
|
return m_connects.size();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------------------
|
||||||
|
template<class t_connection_context>
|
||||||
|
int async_protocol_handler_config<t_connection_context>::notify(int command, const std::string& in_buff, boost::uuids::uuid connection_id)
|
||||||
|
{
|
||||||
|
async_protocol_handler<t_connection_context>* aph;
|
||||||
|
int r = find_and_lock_connection(connection_id, aph);
|
||||||
|
return LEVIN_OK == r ? aph->notify(command, in_buff) : r;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------------------
|
||||||
|
template<class t_connection_context>
|
||||||
|
bool async_protocol_handler_config<t_connection_context>::close(boost::uuids::uuid connection_id)
|
||||||
|
{
|
||||||
|
CRITICAL_REGION_LOCAL(m_connects_lock);
|
||||||
|
async_protocol_handler<t_connection_context>* aph = find_connection(connection_id);
|
||||||
|
return 0 != aph ? aph->close() : false;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------------------
|
||||||
|
template<class t_connection_context>
|
||||||
|
bool async_protocol_handler_config<t_connection_context>::update_connection_context(const t_connection_context& contxt)
|
||||||
|
{
|
||||||
|
CRITICAL_REGION_LOCAL(m_connects_lock);
|
||||||
|
async_protocol_handler<t_connection_context>* aph = find_connection(contxt.m_connection_id);
|
||||||
|
if(0 == aph)
|
||||||
|
return false;
|
||||||
|
aph->update_connection_context(contxt);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------------------
|
||||||
|
template<class t_connection_context>
|
||||||
|
bool async_protocol_handler_config<t_connection_context>::request_callback(boost::uuids::uuid connection_id)
|
||||||
|
{
|
||||||
|
async_protocol_handler<t_connection_context>* aph;
|
||||||
|
int r = find_and_lock_connection(connection_id, aph);
|
||||||
|
if(LEVIN_OK == r)
|
||||||
|
{
|
||||||
|
aph->request_callback();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _HTTP_SERVER_CP_H_
|
||||||
|
#define _HTTP_SERVER_CP_H_
|
||||||
|
|
||||||
|
#include "abstract_tcp_server_cp.h"
|
||||||
|
#include "levin_protocol_handler.h"
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
typedef cp_server_impl<levin::protocol_handler> cp_levin_server;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _HTTP_SERVER_CP_H_
|
||||||
|
#define _HTTP_SERVER_CP_H_
|
||||||
|
|
||||||
|
#include "abstract_tcp_server2.h"
|
||||||
|
#include "levin_protocol_handler.h"
|
||||||
|
#include "levin_protocol_handler_async.h"
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
typedef boosted_tcp_server<levin::protocol_handler<> > boosted_levin_server;
|
||||||
|
typedef boosted_tcp_server<levin::async_protocol_handler<> > boosted_levin_async_server;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
// Copyright (c) 2006-2013, 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
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
inline
|
||||||
|
bool is_ip_local(boost::uint32_t ip)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
local ip area
|
||||||
|
10.0.0.0 — 10.255.255.255
|
||||||
|
172.16.0.0 — 172.31.255.255
|
||||||
|
192.168.0.0 — 192.168.255.255
|
||||||
|
*/
|
||||||
|
if( (ip | 0xffffff00) == 0xffffff0a)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if( (ip | 0xffff0000) == 0xffffa8c0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if( (ip | 0xffffff00) == 0xffffffac)
|
||||||
|
{
|
||||||
|
uint32_t second_num = (ip << 8) & 0xff000000;
|
||||||
|
if(second_num >= 16 && second_num <= 31 )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
inline
|
||||||
|
bool is_ip_loopback(boost::uint32_t ip)
|
||||||
|
{
|
||||||
|
if( (ip | 0xffffff00) == 0xffffff7f)
|
||||||
|
return true;
|
||||||
|
//MAKE_IP
|
||||||
|
/*
|
||||||
|
loopback ip
|
||||||
|
127.0.0.0 — 127.255.255.255
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _MULTIPROTOCOLS_SERVER_H_
|
||||||
|
#define _MULTIPROTOCOLS_SERVER_H_
|
||||||
|
|
||||||
|
//#include "abstract_tcp_server_cp.h"
|
||||||
|
#include "protocol_switcher.h"
|
||||||
|
#include "abstract_tcp_server2.h"
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
//typedef cp_server_impl<net_utils::protocol_switcher> multiprotocol_server;
|
||||||
|
typedef boosted_tcp_server<net_utils::protocol_switcher> boosted_multiprotocol_server;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif //_MULTIPROTOCOLS_SERVER_H_
|
||||||
|
|
|
@ -0,0 +1,376 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _MUNIN_CONNECTION_HANDLER_H_
|
||||||
|
#define _MUNIN_CONNECTION_HANDLER_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "net_utils_base.h"
|
||||||
|
#include "to_nonconst_iterator.h"
|
||||||
|
#include "http_base.h"
|
||||||
|
#include "reg_exp_definer.h"
|
||||||
|
|
||||||
|
#define MUNIN_ARGS_DEFAULT(vertial_lable_str) "graph_args --base 1000 -l 0 --vertical-label " vertial_lable_str " \n"
|
||||||
|
#define MUNIN_ARGS_FORCE_AUPPER_LIMIT(vertial_lable_str, limit) "graph_args --base 1000 -l 0 --vertical-label " vertial_lable_str " --rigid --upper-limit " limit " \n"
|
||||||
|
#define MUNIN_TITLE(title_str) "graph_title " title_str "\n"
|
||||||
|
#define MUNIN_CATEGORY(category_str) "graph_category " category_str "\n"
|
||||||
|
#define MUNIN_INFO(info_str) "graph_info " info_str "\n"
|
||||||
|
#define MUNIN_ENTRY(var_name) #var_name".label " #var_name "\n" #var_name".info "#var_name".\n"
|
||||||
|
#define MUNIN_ENTRY_AREA(var_name) #var_name".label " #var_name "\n" #var_name".info "#var_name".\n" #var_name".draw AREASTACK\n"
|
||||||
|
#define MUNIN_ENTRY_ALIAS(var_name, alias) #var_name".label " #alias"\n" #var_name".info "#alias".\n"
|
||||||
|
#define BEGIN_MUNIN_SERVICE(servivece_name_str) if(servivece_name_str == pservice->m_service_name) {
|
||||||
|
#define END_MUNIN_SERVICE() }
|
||||||
|
#define MUNIN_SERVICE_PARAM(munin_var_name_str, variable) paramters_text += std::string() + munin_var_name_str ".value " + boost::lexical_cast<std::string>(variable) + "\n"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
namespace munin
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
struct munin_service;
|
||||||
|
|
||||||
|
struct munin_service_data_provider
|
||||||
|
{
|
||||||
|
virtual bool update_service_data(munin_service* pservice, std::string& paramters_text)=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct munin_service
|
||||||
|
{
|
||||||
|
std::string m_service_name;
|
||||||
|
std::string m_service_config_string;
|
||||||
|
munin_service_data_provider* m_pdata_provider;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct node_server_config
|
||||||
|
{
|
||||||
|
std::list<munin_service> m_services;
|
||||||
|
//TODO:
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fake_send_handler: public i_service_endpoint
|
||||||
|
{
|
||||||
|
virtual bool do_send(const void* ptr, size_t cb)
|
||||||
|
{
|
||||||
|
m_cache += std::string((const char*)ptr, cb);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
|
||||||
|
std::string m_cache;
|
||||||
|
};
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
class munin_node_server_connection_handler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef node_server_config config_type;
|
||||||
|
typedef connection_context_base connection_context;
|
||||||
|
|
||||||
|
munin_node_server_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config, const connection_context_base& context):m_psnd_hndlr(psnd_hndlr),
|
||||||
|
m_machine_state(http_state_retriving_comand_line),
|
||||||
|
m_config(config)
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
virtual ~munin_node_server_connection_handler()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool release_protocol()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool after_init_connection()
|
||||||
|
{
|
||||||
|
std::string hello_str = "# munin node at ";
|
||||||
|
hello_str += m_host_name + "\n";
|
||||||
|
send_hook(hello_str);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool thread_init()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool thread_deinit()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_qued_callback()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool handle_recv(const void* ptr, size_t cb)
|
||||||
|
{
|
||||||
|
|
||||||
|
const char* pbuff = (const char*)ptr;
|
||||||
|
std::string recvd_buff(pbuff, cb);
|
||||||
|
LOG_PRINT("munin_recv: \n" << recvd_buff, LOG_LEVEL_3);
|
||||||
|
|
||||||
|
m_cache += recvd_buff;
|
||||||
|
|
||||||
|
bool stop_handling = false;
|
||||||
|
while(!stop_handling)
|
||||||
|
{
|
||||||
|
switch(m_machine_state)
|
||||||
|
{
|
||||||
|
case http_state_retriving_comand_line:
|
||||||
|
{
|
||||||
|
|
||||||
|
std::string::size_type fpos = m_cache.find('\n');
|
||||||
|
if(std::string::npos != fpos )
|
||||||
|
{
|
||||||
|
bool res = handle_command(m_cache);
|
||||||
|
if(!res)
|
||||||
|
return false;
|
||||||
|
m_cache.erase(0, fpos+1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
stop_handling = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case http_state_error:
|
||||||
|
stop_handling = true;
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
LOG_ERROR("Error in munin state machine! Unkonwon state=" << m_machine_state);
|
||||||
|
stop_handling = true;
|
||||||
|
m_machine_state = http_state_error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
|
||||||
|
bool init()
|
||||||
|
{
|
||||||
|
char hostname[64] = {0};
|
||||||
|
int res = gethostname(hostname, 64);
|
||||||
|
hostname[63] = 0;//be happy
|
||||||
|
m_host_name = hostname;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool handle_command(const std::string& command)
|
||||||
|
{
|
||||||
|
// list, nodes, config, fetch, version or quit
|
||||||
|
STATIC_REGEXP_EXPR_1(rexp_match_command_line, "^((list)|(nodes)|(config)|(fetch)|(version)|(quit))(\\s+(\\S+))?", boost::regex::icase | boost::regex::normal);
|
||||||
|
// 12 3 4 5 6 7 8 9
|
||||||
|
size_t match_len = 0;
|
||||||
|
boost::smatch result;
|
||||||
|
if(boost::regex_search(command, result, rexp_match_command_line, boost::match_default) && result[0].matched)
|
||||||
|
{
|
||||||
|
if(result[2].matched)
|
||||||
|
{//list command
|
||||||
|
return handle_list_command();
|
||||||
|
}else if(result[3].matched)
|
||||||
|
{//nodes command
|
||||||
|
return handle_nodes_command();
|
||||||
|
}else if(result[4].matched)
|
||||||
|
{//config command
|
||||||
|
if(result[9].matched)
|
||||||
|
return handle_config_command(result[9]);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
send_hook("Unknown service\n");
|
||||||
|
}
|
||||||
|
}else if(result[5].matched)
|
||||||
|
{//fetch command
|
||||||
|
if(result[9].matched)
|
||||||
|
return handle_fetch_command(result[9]);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
send_hook("Unknown service\n");
|
||||||
|
}
|
||||||
|
}else if(result[6].matched)
|
||||||
|
{//version command
|
||||||
|
return handle_version_command();
|
||||||
|
}else if(result[7].matched)
|
||||||
|
{//quit command
|
||||||
|
return handle_quit_command();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return send_hook("Unknown command. Try list, nodes, config, fetch, version or quit\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return send_hook("Unknown command. Try list, nodes, config, fetch, version or quit\n");;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool handle_list_command()
|
||||||
|
{
|
||||||
|
std::string buff_to_send;
|
||||||
|
for(std::list<munin_service>::const_iterator it = m_config.m_services.begin(); it!=m_config.m_services.end();it++)
|
||||||
|
{
|
||||||
|
buff_to_send += it->m_service_name + " ";
|
||||||
|
}
|
||||||
|
buff_to_send+='\n';
|
||||||
|
return send_hook(buff_to_send);
|
||||||
|
}
|
||||||
|
bool handle_nodes_command()
|
||||||
|
{
|
||||||
|
//supports only one node - host name
|
||||||
|
send_hook(m_host_name + "\n.\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool handle_config_command(const std::string& service_name)
|
||||||
|
{
|
||||||
|
munin_service* psrv = get_service_by_name(service_name);
|
||||||
|
if(!psrv)
|
||||||
|
return send_hook(std::string() + "Unknown service\n");
|
||||||
|
|
||||||
|
|
||||||
|
return send_hook(psrv->m_service_config_string + ".\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool handle_fetch_command(const std::string& service_name)
|
||||||
|
{
|
||||||
|
munin_service* psrv = get_service_by_name(service_name);
|
||||||
|
if(!psrv)
|
||||||
|
return send_hook(std::string() + "Unknown service\n");
|
||||||
|
|
||||||
|
std::string buff;
|
||||||
|
psrv->m_pdata_provider->update_service_data(psrv, buff);
|
||||||
|
|
||||||
|
buff += ".\n";
|
||||||
|
return send_hook(buff);
|
||||||
|
}
|
||||||
|
bool handle_version_command()
|
||||||
|
{
|
||||||
|
return send_hook("Munin node component by Andrey Sabelnikov\n");
|
||||||
|
}
|
||||||
|
bool handle_quit_command()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool send_hook(const std::string& buff)
|
||||||
|
{
|
||||||
|
LOG_PRINT("munin_send: \n" << buff, LOG_LEVEL_3);
|
||||||
|
|
||||||
|
if(m_psnd_hndlr)
|
||||||
|
return m_psnd_hndlr->do_send(buff.data(), buff.size());
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
munin_service* get_service_by_name(const std::string& srv_name)
|
||||||
|
{
|
||||||
|
std::list<munin_service>::iterator it = m_config.m_services.begin();
|
||||||
|
for(; it!=m_config.m_services.end(); it++)
|
||||||
|
if(it->m_service_name == srv_name)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(it==m_config.m_services.end())
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return &(*it);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum machine_state{
|
||||||
|
http_state_retriving_comand_line,
|
||||||
|
http_state_error
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
config_type& m_config;
|
||||||
|
machine_state m_machine_state;
|
||||||
|
std::string m_cache;
|
||||||
|
std::string m_host_name;
|
||||||
|
protected:
|
||||||
|
i_service_endpoint* m_psnd_hndlr;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
inline bool test_self()
|
||||||
|
{
|
||||||
|
/*WSADATA w;
|
||||||
|
::WSAStartup(MAKEWORD(1, 1), &w);
|
||||||
|
node_server_config sc;
|
||||||
|
sc.m_services.push_back(munin_service());
|
||||||
|
sc.m_services.back().m_service_name = "test_service";
|
||||||
|
|
||||||
|
sc.m_services.back().m_service_config_string =
|
||||||
|
"graph_args --base 1000 -l 0 --vertical-label N --upper-limit 329342976\n"
|
||||||
|
"graph_title REPORTS STATICTICS\n"
|
||||||
|
"graph_category bind\n"
|
||||||
|
"graph_info This graph shows how many reports came in fixed time period.\n"
|
||||||
|
"graph_order apps free swap\n"
|
||||||
|
"apps.label apps\n"
|
||||||
|
"apps.draw AREA\n"
|
||||||
|
"apps.info Memory used by user-space applications.\n"
|
||||||
|
"swap.label swap\n"
|
||||||
|
"swap.draw STACK\n"
|
||||||
|
"swap.info Swap space used.\n"
|
||||||
|
"free.label unused\n"
|
||||||
|
"free.draw STACK\n"
|
||||||
|
"free.info Wasted memory. Memory that is not used for anything at all.\n"
|
||||||
|
"committed.label committed\n"
|
||||||
|
"committed.draw LINE2\n"
|
||||||
|
"committed.warn 625410048\n"
|
||||||
|
"committed.info The amount of memory that would be used if all the memory that's been allocated were to be used.\n";
|
||||||
|
|
||||||
|
|
||||||
|
sc.m_services.push_back(munin_service());
|
||||||
|
sc.m_services.back().m_service_name = "test_service1";
|
||||||
|
fake_send_handler fh;
|
||||||
|
munin_node_server_connection_handler mh(&fh, sc);
|
||||||
|
|
||||||
|
std::string buff = "list\n";
|
||||||
|
mh.handle_recv(buff.data(), buff.size());
|
||||||
|
|
||||||
|
|
||||||
|
buff = "nodes\n";
|
||||||
|
mh.handle_recv(buff.data(), buff.size());
|
||||||
|
*/
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif//!_MUNIN_CONNECTION_HANDLER_H_
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _MUNIN_NODE_SERVER_H_
|
||||||
|
#define _MUNIN_NODE_SERVER_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
//#include "net_utils_base.h"
|
||||||
|
#include "munin_connection_handler.h"
|
||||||
|
//#include "abstract_tcp_server.h"
|
||||||
|
//#include "abstract_tcp_server_cp.h"
|
||||||
|
#include "abstract_tcp_server2.h"
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
namespace munin
|
||||||
|
{
|
||||||
|
typedef boosted_tcp_server<munin_node_server_connection_handler> munin_node_server;
|
||||||
|
//typedef cp_server_impl<munin_node_server_connection_handler> munin_node_cp_server;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif//!_MUNIN_NODE_SERVER_H_
|
|
@ -0,0 +1,683 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 <Winsock2.h>
|
||||||
|
//#include <Ws2tcpip.h>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <istream>
|
||||||
|
#include <ostream>
|
||||||
|
#include <string>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/preprocessor/selection/min.hpp>
|
||||||
|
#include <boost/lambda/bind.hpp>
|
||||||
|
#include <boost/lambda/lambda.hpp>
|
||||||
|
#include <boost/interprocess/detail/atomic.hpp>
|
||||||
|
#include "net/net_utils_base.h"
|
||||||
|
#include "misc_language.h"
|
||||||
|
//#include "profile_tools.h"
|
||||||
|
#include "../string_tools.h"
|
||||||
|
|
||||||
|
#ifndef MAKE_IP
|
||||||
|
#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
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_initialized = 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()
|
||||||
|
{
|
||||||
|
//profile_tools::local_coast lc("~blocked_mode_client()", 3);
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
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_socket.close();
|
||||||
|
// Get a list of endpoints corresponding to the server name.
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
//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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
m_deadline.expires_from_now(boost::posix_time::milliseconds(m_connect_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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_socket, 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_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_socket.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;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool recv_n(std::string& buff, boost::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));
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
buff.resize(static_cast<size_t>(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);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Some fatal problems at read.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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 boost::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)
|
||||||
|
{
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool send(const void* data, size_t sz)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
m_send_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_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_socket.write_some(boost::asio::buffer(data, sz), ec);
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,168 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 "http_base.h"
|
||||||
|
#include "reg_exp_definer.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
|
||||||
|
inline bool parse_uri_query(const std::string& query, std::list<std::pair<std::string, std::string> >& params)
|
||||||
|
{
|
||||||
|
enum state
|
||||||
|
{
|
||||||
|
st_param_name,
|
||||||
|
st_param_val
|
||||||
|
};
|
||||||
|
state st = st_param_name;
|
||||||
|
std::string::const_iterator start_it = query.begin();
|
||||||
|
std::pair<std::string, std::string> e;
|
||||||
|
for(std::string::const_iterator it = query.begin(); it != query.end(); it++)
|
||||||
|
{
|
||||||
|
switch(st)
|
||||||
|
{
|
||||||
|
case st_param_name:
|
||||||
|
if(*it == '=')
|
||||||
|
{
|
||||||
|
e.first.assign(start_it, it);
|
||||||
|
start_it = it;++start_it;
|
||||||
|
st = st_param_val;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case st_param_val:
|
||||||
|
if(*it == '&')
|
||||||
|
{
|
||||||
|
e.second.assign(start_it, it);
|
||||||
|
start_it = it;++start_it;
|
||||||
|
params.push_back(e);
|
||||||
|
e.first.clear();e.second.clear();
|
||||||
|
st = st_param_name;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_ERROR("Unknown state " << (int)st);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(st == st_param_name)
|
||||||
|
{
|
||||||
|
if(start_it != query.end())
|
||||||
|
{
|
||||||
|
e.first.assign(start_it, query.end());
|
||||||
|
params.push_back(e);
|
||||||
|
}
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
if(start_it != query.end())
|
||||||
|
e.second.assign(start_it, query.end());
|
||||||
|
|
||||||
|
if(e.first.size())
|
||||||
|
params.push_back(e);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool parse_uri(const std::string uri, http::uri_content& content)
|
||||||
|
{
|
||||||
|
|
||||||
|
///iframe_test.html?api_url=http://api.vk.com/api.php&api_id=3289090&api_settings=1&viewer_id=562964060&viewer_type=0&sid=0aad8d1c5713130f9ca0076f2b7b47e532877424961367d81e7fa92455f069be7e21bc3193cbd0be11895&secret=368ebbc0ef&access_token=668bc03f43981d883f73876ffff4aa8564254b359cc745dfa1b3cde7bdab2e94105d8f6d8250717569c0a7&user_id=0&group_id=0&is_app_user=1&auth_key=d2f7a895ca5ff3fdb2a2a8ae23fe679a&language=0&parent_language=0&ad_info=ElsdCQBaQlxiAQRdFUVUXiN2AVBzBx5pU1BXIgZUJlIEAWcgAUoLQg==&referrer=unknown&lc_name=9834b6a3&hash=
|
||||||
|
content.m_query_params.clear();
|
||||||
|
STATIC_REGEXP_EXPR_1(rexp_match_uri, "^([^?#]*)(\\?([^#]*))?(#(.*))?", boost::regex::icase | boost::regex::normal);
|
||||||
|
|
||||||
|
boost::smatch result;
|
||||||
|
if(!boost::regex_search(uri, result, rexp_match_uri, boost::match_default) && result[0].matched)
|
||||||
|
{
|
||||||
|
LOG_PRINT_L0("[PARSE URI] regex not matched for uri: " << uri);
|
||||||
|
content.m_path = uri;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(result[1].matched)
|
||||||
|
{
|
||||||
|
content.m_path = result[1];
|
||||||
|
}
|
||||||
|
if(result[3].matched)
|
||||||
|
{
|
||||||
|
content.m_query = result[3];
|
||||||
|
}
|
||||||
|
if(result[5].matched)
|
||||||
|
{
|
||||||
|
content.m_fragment = result[5];
|
||||||
|
}
|
||||||
|
if(content.m_query.size())
|
||||||
|
{
|
||||||
|
parse_uri_query(content.m_query, content.m_query_params);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool parse_url(const std::string url_str, http::url_content& content)
|
||||||
|
{
|
||||||
|
|
||||||
|
///iframe_test.html?api_url=http://api.vk.com/api.php&api_id=3289090&api_settings=1&viewer_id=562964060&viewer_type=0&sid=0aad8d1c5713130f9ca0076f2b7b47e532877424961367d81e7fa92455f069be7e21bc3193cbd0be11895&secret=368ebbc0ef&access_token=668bc03f43981d883f73876ffff4aa8564254b359cc745dfa1b3cde7bdab2e94105d8f6d8250717569c0a7&user_id=0&group_id=0&is_app_user=1&auth_key=d2f7a895ca5ff3fdb2a2a8ae23fe679a&language=0&parent_language=0&ad_info=ElsdCQBaQlxiAQRdFUVUXiN2AVBzBx5pU1BXIgZUJlIEAWcgAUoLQg==&referrer=unknown&lc_name=9834b6a3&hash=
|
||||||
|
//STATIC_REGEXP_EXPR_1(rexp_match_uri, "^([^?#]*)(\\?([^#]*))?(#(.*))?", boost::regex::icase | boost::regex::normal);
|
||||||
|
STATIC_REGEXP_EXPR_1(rexp_match_uri, "^((.*?)://)?(([^/:]*)(:(\\d+))?)(.*)?", boost::regex::icase | boost::regex::normal);
|
||||||
|
// 12 34 5 6 7
|
||||||
|
content.port = 0;
|
||||||
|
boost::smatch result;
|
||||||
|
if(!boost::regex_search(url_str, result, rexp_match_uri, boost::match_default) && result[0].matched)
|
||||||
|
{
|
||||||
|
LOG_PRINT_L0("[PARSE URI] regex not matched for uri: " << rexp_match_uri);
|
||||||
|
//content.m_path = uri;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(result[2].matched)
|
||||||
|
{
|
||||||
|
content.schema = result[2];
|
||||||
|
}
|
||||||
|
if(result[4].matched)
|
||||||
|
{
|
||||||
|
content.host = result[4];
|
||||||
|
}
|
||||||
|
if(result[6].matched)
|
||||||
|
{
|
||||||
|
content.port = boost::lexical_cast<boost::uint64_t>(result[6]);
|
||||||
|
}
|
||||||
|
if(result[7].matched)
|
||||||
|
{
|
||||||
|
content.uri = result[7];
|
||||||
|
return parse_uri(result[7], content.m_uri_content);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _NET_UTILS_BASE_H_
|
||||||
|
#define _NET_UTILS_BASE_H_
|
||||||
|
|
||||||
|
#include <boost/uuid/uuid.hpp>
|
||||||
|
#include "string_tools.h"
|
||||||
|
|
||||||
|
#ifndef MAKE_IP
|
||||||
|
#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
struct connection_context_base
|
||||||
|
{
|
||||||
|
const boost::uuids::uuid m_connection_id;
|
||||||
|
const boost::uint32_t m_remote_ip;
|
||||||
|
const boost::uint32_t m_remote_port;
|
||||||
|
const bool m_is_income;
|
||||||
|
|
||||||
|
connection_context_base(boost::uuids::uuid connection_id, long remote_ip, int remote_port, bool is_income):
|
||||||
|
m_connection_id(connection_id),
|
||||||
|
m_remote_ip(remote_ip),
|
||||||
|
m_remote_port(remote_port),
|
||||||
|
m_is_income(is_income)
|
||||||
|
|
||||||
|
|
||||||
|
{}
|
||||||
|
|
||||||
|
connection_context_base(): m_connection_id(),
|
||||||
|
m_remote_ip(0),
|
||||||
|
m_remote_port(0),
|
||||||
|
m_is_income(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
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);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<class t_protocol_handler>
|
||||||
|
friend class connection;
|
||||||
|
void set_details(boost::uuids::uuid connection_id, long remote_ip, int remote_port, bool is_income)
|
||||||
|
{
|
||||||
|
this->~connection_context_base();
|
||||||
|
new(this) connection_context_base(connection_id, remote_ip, remote_port, is_income);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
struct i_service_endpoint
|
||||||
|
{
|
||||||
|
virtual bool do_send(const void* ptr, size_t cb)=0;
|
||||||
|
virtual bool close()=0;
|
||||||
|
virtual bool call_run_once_service_io()=0;
|
||||||
|
virtual bool request_callback()=0;
|
||||||
|
virtual boost::asio::io_service& get_io_service()=0;
|
||||||
|
//protect from deletion connection object(with protocol instance) during external call "invoke"
|
||||||
|
virtual bool add_ref()=0;
|
||||||
|
virtual bool release()=0;
|
||||||
|
protected:
|
||||||
|
virtual ~i_service_endpoint(){}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//some helpers
|
||||||
|
|
||||||
|
|
||||||
|
inline
|
||||||
|
std::string print_connection_context(const connection_context_base& ctx)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << epee::string_tools::get_ip_string_from_int32(ctx.m_remote_ip) << ":" << ctx.m_remote_port << " " << epee::string_tools::get_str_from_guid_a(ctx.m_connection_id) << (ctx.m_is_income ? " INC":" OUT");
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
std::string print_connection_context_short(const connection_context_base& ctx)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << epee::string_tools::get_ip_string_from_int32(ctx.m_remote_ip) << ":" << ctx.m_remote_port << (ctx.m_is_income ? " INC":" OUT");
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LOG_PRINT_CC(ct, message, log_level) LOG_PRINT("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level)
|
||||||
|
#define LOG_PRINT_CC_GREEN(ct, message, log_level) LOG_PRINT_GREEN("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level)
|
||||||
|
#define LOG_PRINT_CC_RED(ct, message, log_level) LOG_PRINT_RED("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level)
|
||||||
|
#define LOG_PRINT_CC_BLUE(ct, message, log_level) LOG_PRINT_BLUE("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level)
|
||||||
|
#define LOG_ERROR_CC(ct, message) LOG_ERROR("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message)
|
||||||
|
|
||||||
|
#define LOG_PRINT_CC_L0(ct, message) LOG_PRINT_L0("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message)
|
||||||
|
#define LOG_PRINT_CC_L1(ct, message) LOG_PRINT_L1("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message)
|
||||||
|
#define LOG_PRINT_CC_L2(ct, message) LOG_PRINT_L2("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message)
|
||||||
|
#define LOG_PRINT_CC_L3(ct, message) LOG_PRINT_L3("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message)
|
||||||
|
#define LOG_PRINT_CC_L4(ct, message) LOG_PRINT_L4("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message)
|
||||||
|
|
||||||
|
#define LOG_PRINT_CCONTEXT_L0(message) LOG_PRINT_CC_L0(context, message)
|
||||||
|
#define LOG_PRINT_CCONTEXT_L1(message) LOG_PRINT_CC_L1(context, message)
|
||||||
|
#define LOG_PRINT_CCONTEXT_L2(message) LOG_PRINT_CC_L2(context, message)
|
||||||
|
#define LOG_PRINT_CCONTEXT_L3(message) LOG_PRINT_CC_L3(context, message)
|
||||||
|
#define LOG_ERROR_CCONTEXT(message) LOG_ERROR_CC(context, message)
|
||||||
|
|
||||||
|
#define LOG_PRINT_CCONTEXT_GREEN(message, log_level) LOG_PRINT_CC_GREEN(context, message, log_level)
|
||||||
|
#define LOG_PRINT_CCONTEXT_RED(message, log_level) LOG_PRINT_CC_RED(context, message, log_level)
|
||||||
|
#define LOG_PRINT_CCONTEXT_BLUE(message, log_level) LOG_PRINT_CC_BLUE(context, message, log_level)
|
||||||
|
|
||||||
|
#define CHECK_AND_ASSERT_MES_CC(condition, return_val, err_message) CHECK_AND_ASSERT_MES(condition, return_val, "[" << epee::net_utils::print_connection_context_short(context) << "]" << err_message)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //_NET_UTILS_BASE_H_
|
|
@ -0,0 +1,121 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _PROTOCOL_SWITCHER_H_
|
||||||
|
#define _PROTOCOL_SWITCHER_H_
|
||||||
|
|
||||||
|
#include "levin_base.h"
|
||||||
|
#include "http_server.h"
|
||||||
|
#include "levin_protocol_handler.h"
|
||||||
|
//#include "abstract_tcp_server.h"
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
struct protocl_switcher_config
|
||||||
|
{
|
||||||
|
http::http_custom_handler::config_type m_http_config;
|
||||||
|
levin::protocol_handler::config_type m_levin_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct i_protocol_handler
|
||||||
|
{
|
||||||
|
virtual bool handle_recv(const void* ptr, size_t cb)=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class t>
|
||||||
|
class t_protocol_handler: public i_protocol_handler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef t t_type;
|
||||||
|
t_protocol_handler(i_service_endpoint* psnd_hndlr, typename t_type::config_type& config, const connection_context& conn_context):m_hadler(psnd_hndlr, config, conn_context)
|
||||||
|
{}
|
||||||
|
private:
|
||||||
|
bool handle_recv(const void* ptr, size_t cb)
|
||||||
|
{
|
||||||
|
return m_hadler.handle_recv(ptr, cb);
|
||||||
|
}
|
||||||
|
t_type m_hadler;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class protocol_switcher
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef protocl_switcher_config config_type;
|
||||||
|
|
||||||
|
protocol_switcher(net_utils::i_service_endpoint* psnd_hndlr, config_type& config, const net_utils::connection_context_base& conn_context);
|
||||||
|
virtual ~protocol_switcher(){}
|
||||||
|
|
||||||
|
virtual bool handle_recv(const void* ptr, size_t cb);
|
||||||
|
|
||||||
|
bool after_init_connection(){return true;}
|
||||||
|
private:
|
||||||
|
t_protocol_handler<http::http_custom_handler> m_http_handler;
|
||||||
|
t_protocol_handler<levin::protocol_handler> m_levin_handler;
|
||||||
|
i_protocol_handler* pcurrent_handler;
|
||||||
|
|
||||||
|
std::string m_cached_buff;
|
||||||
|
};
|
||||||
|
|
||||||
|
protocol_switcher::protocol_switcher(net_utils::i_service_endpoint* psnd_hndlr, config_type& config, const net_utils::connection_context_base& conn_context):m_http_handler(psnd_hndlr, config.m_http_config, conn_context), m_levin_handler(psnd_hndlr, config.m_levin_config, conn_context), pcurrent_handler(NULL)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool protocol_switcher::handle_recv(const void* ptr, size_t cb)
|
||||||
|
{
|
||||||
|
if(pcurrent_handler)
|
||||||
|
return pcurrent_handler->handle_recv(ptr, cb);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_cached_buff.append((const char*)ptr, cb);
|
||||||
|
if(m_cached_buff.size() < sizeof(boost::uint64_t))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if(*((boost::uint64_t*)&m_cached_buff[0]) == LEVIN_SIGNATURE)
|
||||||
|
{
|
||||||
|
pcurrent_handler = &m_levin_handler;
|
||||||
|
return pcurrent_handler->handle_recv(m_cached_buff.data(), m_cached_buff.size());
|
||||||
|
}
|
||||||
|
if(m_cached_buff.substr(0, 4) == "GET " || m_cached_buff.substr(0, 4) == "POST")
|
||||||
|
{
|
||||||
|
pcurrent_handler = &m_http_handler;
|
||||||
|
return pcurrent_handler->handle_recv(m_cached_buff.data(), m_cached_buff.size());
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
LOG_ERROR("Wrong protocol accepted on port...");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif //_PROTOCOL_SWITCHER_H_
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright (c) 2006-2013, 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
|
||||||
|
|
||||||
|
|
||||||
|
#define RPC_METHOD_NAME(name) static inline const char* methodname(){return name;}
|
|
@ -0,0 +1,181 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 <iostream>
|
||||||
|
#include <istream>
|
||||||
|
#include <ostream>
|
||||||
|
#include <string>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
#include <boost/archive/iterators/base64_from_binary.hpp>
|
||||||
|
#include <boost/archive/iterators/transform_width.hpp>
|
||||||
|
#include <boost/archive/iterators/ostream_iterator.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
namespace smtp
|
||||||
|
{
|
||||||
|
|
||||||
|
using boost::asio::ip::tcp;
|
||||||
|
using namespace boost::archive::iterators;
|
||||||
|
typedef base64_from_binary<transform_width<const char *,6,8> > base64_text;
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
class smtp_client
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
smtp_client(std::string pServer,unsigned int pPort,std::string pUser,std::string pPassword):
|
||||||
|
mServer(pServer),mPort(pPort),mUserName(pUser),mPassword(pPassword),mSocket(mIOService),mResolver(mIOService)
|
||||||
|
{
|
||||||
|
tcp::resolver::query qry(mServer,boost::lexical_cast<std::string>( mPort ));
|
||||||
|
mResolver.async_resolve(qry,boost::bind(&smtp_client::handleResolve,this,boost::asio::placeholders::error,
|
||||||
|
boost::asio::placeholders::iterator));
|
||||||
|
}
|
||||||
|
bool Send(std::string pFrom,std::string pTo,std::string pSubject,std::string pMessage)
|
||||||
|
{
|
||||||
|
mHasError = true;
|
||||||
|
mFrom=pFrom;
|
||||||
|
mTo=pTo;
|
||||||
|
mSubject=pSubject;
|
||||||
|
mMessage=pMessage;
|
||||||
|
mIOService.run();
|
||||||
|
return !mHasError;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::string encodeBase64(std::string pData)
|
||||||
|
{
|
||||||
|
std::stringstream os;
|
||||||
|
size_t sz=pData.size();
|
||||||
|
std::copy(base64_text(pData.c_str()),base64_text(pData.c_str()+sz),std::ostream_iterator<char>(os));
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
|
void handleResolve(const boost::system::error_code& err,tcp::resolver::iterator endpoint_iterator)
|
||||||
|
{
|
||||||
|
if(!err)
|
||||||
|
{
|
||||||
|
tcp::endpoint endpoint=*endpoint_iterator;
|
||||||
|
mSocket.async_connect(endpoint,
|
||||||
|
boost::bind(&smtp_client::handleConnect,this,boost::asio::placeholders::error,++endpoint_iterator));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mHasError=true;
|
||||||
|
mErrorMsg= err.message();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void writeLine(std::string pData)
|
||||||
|
{
|
||||||
|
std::ostream req_strm(&mRequest);
|
||||||
|
req_strm << pData << "\r\n";
|
||||||
|
boost::asio::write(mSocket,mRequest);
|
||||||
|
req_strm.clear();
|
||||||
|
}
|
||||||
|
void readLine(std::string& pData)
|
||||||
|
{
|
||||||
|
boost::asio::streambuf response;
|
||||||
|
boost::asio::read_until(mSocket, response, "\r\n");
|
||||||
|
std::istream response_stream(&response);
|
||||||
|
response_stream >> pData;
|
||||||
|
}
|
||||||
|
void handleConnect(const boost::system::error_code& err,tcp::resolver::iterator endpoint_iterator)
|
||||||
|
{
|
||||||
|
if (!err)
|
||||||
|
{
|
||||||
|
std::string read_buff;
|
||||||
|
// The connection was successful. Send the request.
|
||||||
|
std::ostream req_strm(&mRequest);
|
||||||
|
writeLine("EHLO "+mServer);
|
||||||
|
readLine(read_buff);//220
|
||||||
|
writeLine("AUTH LOGIN");
|
||||||
|
readLine(read_buff);//
|
||||||
|
writeLine(encodeBase64(mUserName));
|
||||||
|
readLine(read_buff);
|
||||||
|
writeLine(encodeBase64(mPassword));
|
||||||
|
readLine(read_buff);
|
||||||
|
writeLine( "MAIL FROM:<"+mFrom+">");
|
||||||
|
writeLine( "RCPT TO:<"+mTo+">");
|
||||||
|
writeLine( "DATA");
|
||||||
|
writeLine( "SUBJECT:"+mSubject);
|
||||||
|
writeLine( "From:"+mFrom);
|
||||||
|
writeLine( "To:"+mTo);
|
||||||
|
writeLine( "");
|
||||||
|
writeLine( mMessage );
|
||||||
|
writeLine( "\r\n.\r\n");
|
||||||
|
readLine(read_buff);
|
||||||
|
if(read_buff == "250")
|
||||||
|
mHasError = false;
|
||||||
|
writeLine( "QUIT");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mHasError=true;
|
||||||
|
mErrorMsg= err.message();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::string mServer;
|
||||||
|
std::string mUserName;
|
||||||
|
std::string mPassword;
|
||||||
|
std::string mFrom;
|
||||||
|
std::string mTo;
|
||||||
|
std::string mSubject;
|
||||||
|
std::string mMessage;
|
||||||
|
unsigned int mPort;
|
||||||
|
boost::asio::io_service mIOService;
|
||||||
|
tcp::resolver mResolver;
|
||||||
|
tcp::socket mSocket;
|
||||||
|
boost::asio::streambuf mRequest;
|
||||||
|
boost::asio::streambuf mResponse;
|
||||||
|
bool mHasError;
|
||||||
|
std::string mErrorMsg;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
bool send_mail(const std::string& server, int port, const std::string& login, const std::string& pass, const std::string& from_email, /*"STIL CRAWLER",*/
|
||||||
|
const std::string& maillist, const std::string& subject, const std::string& body)
|
||||||
|
{
|
||||||
|
STD_TRY_BEGIN();
|
||||||
|
//smtp_client mailc("yoursmtpserver.com",25,"user@yourdomain.com","password");
|
||||||
|
//mailc.Send("from@yourdomain.com","to@somewhere.com","subject","Hello from C++ SMTP Client!");
|
||||||
|
smtp_client mailc(server,port,login,pass);
|
||||||
|
return mailc.Send(from_email,maillist,subject,body);
|
||||||
|
STD_TRY_CATCH("at send_mail", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#include "smtp.inl"
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,88 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 "smtp.h"
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
namespace smtp
|
||||||
|
{
|
||||||
|
|
||||||
|
inline bool send_mail(const std::string& server, int port, const std::string& login, const std::string& pass, const std::string& from_addres, const std::string& from_name, const std::string& maillist, const std::string& subject, const std::string& mail_body)
|
||||||
|
{
|
||||||
|
net_utils::smtp::CSMTPClient smtp;
|
||||||
|
|
||||||
|
if ( !smtp.ServerConnect( server.c_str(), port ) )
|
||||||
|
{
|
||||||
|
LOG_PRINT("Reporting: Failed to connect to server " << server <<":"<< port, LOG_LEVEL_0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(login.size() && pass.size())
|
||||||
|
{
|
||||||
|
if ( !smtp.ServerLogin( login.c_str(), pass.c_str()) )
|
||||||
|
{
|
||||||
|
LOG_PRINT("Reporting: Failed to auth on server " << server <<":"<< port, LOG_LEVEL_0);
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !smtp.SendMessage( from_addres.c_str(),
|
||||||
|
from_name.c_str(),
|
||||||
|
maillist.c_str(),
|
||||||
|
subject.c_str(),
|
||||||
|
"bicycle-client",
|
||||||
|
(LPBYTE)mail_body.data(),
|
||||||
|
mail_body.size()))
|
||||||
|
{
|
||||||
|
char *szErrorText = smtp.GetLastErrorText();
|
||||||
|
if ( szErrorText )
|
||||||
|
{
|
||||||
|
LOG_PRINT("Failed to send message, error text: " << szErrorText, LOG_LEVEL_0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_PRINT("Failed to send message, error text: null", LOG_LEVEL_0);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
smtp.ServerDisconnect();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#define PRAGMA_WARNING_PUSH _Pragma("GCC diagnostic push")
|
||||||
|
#define PRAGMA_WARNING_POP _Pragma("GCC diagnostic pop")
|
||||||
|
#define PRAGMA_WARNING_DISABLE_VS(w)
|
||||||
|
#define PRAGMA_GCC(w) _Pragma(w)
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#define PRAGMA_WARNING_PUSH __pragma(warning( push ))
|
||||||
|
#define PRAGMA_WARNING_POP __pragma(warning( pop ))
|
||||||
|
#define PRAGMA_WARNING_DISABLE_VS(w) __pragma( warning ( disable: w ))
|
||||||
|
//#define PRAGMA_WARNING_DISABLE_GCC(w)
|
||||||
|
#define PRAGMA_GCC(w)
|
||||||
|
#endif
|
|
@ -0,0 +1,111 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _PROFILE_TOOLS_H_
|
||||||
|
#define _PROFILE_TOOLS_H_
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef ENABLE_PROFILING
|
||||||
|
#define PROFILE_FUNC(immortal_ptr_str) static profile_tools::local_call_account lcl_acc(immortal_ptr_str); \
|
||||||
|
profile_tools::call_frame cf(lcl_acc);
|
||||||
|
|
||||||
|
#define PROFILE_FUNC_SECOND(immortal_ptr_str) static profile_tools::local_call_account lcl_acc2(immortal_ptr_str); \
|
||||||
|
profile_tools::call_frame cf2(lcl_acc2);
|
||||||
|
|
||||||
|
#define PROFILE_FUNC_THIRD(immortal_ptr_str) static profile_tools::local_call_account lcl_acc3(immortal_ptr_str); \
|
||||||
|
profile_tools::call_frame cf3(lcl_acc3);
|
||||||
|
|
||||||
|
#define PROFILE_FUNC_ACC(acc) \
|
||||||
|
profile_tools::call_frame cf(acc);
|
||||||
|
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define PROFILE_FUNC(immortal_ptr_str)
|
||||||
|
#define PROFILE_FUNC_SECOND(immortal_ptr_str)
|
||||||
|
#define PROFILE_FUNC_THIRD(immortal_ptr_str)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define START_WAY_POINTS() boost::uint64_t _____way_point_time = misc_utils::get_tick_count();
|
||||||
|
#define WAY_POINT(name) {boost::uint64_t delta = misc_utils::get_tick_count()-_____way_point_time; LOG_PRINT("Way point " << name << ": " << delta, LOG_LEVEL_2);_____way_point_time = misc_utils::get_tick_count();}
|
||||||
|
#define WAY_POINT2(name, avrg_obj) {boost::uint64_t delta = misc_utils::get_tick_count()-_____way_point_time; avrg_obj.push(delta); LOG_PRINT("Way point " << name << ": " << delta, LOG_LEVEL_2);_____way_point_time = misc_utils::get_tick_count();}
|
||||||
|
|
||||||
|
|
||||||
|
#define TIME_MEASURE_START(var_name) boost::uint64_t var_name = misc_utils::get_tick_count();
|
||||||
|
#define TIME_MEASURE_FINISH(var_name) var_name = misc_utils::get_tick_count() - var_name;
|
||||||
|
|
||||||
|
namespace profile_tools
|
||||||
|
{
|
||||||
|
struct local_call_account
|
||||||
|
{
|
||||||
|
local_call_account(const char* pstr):m_count_of_call(0), m_summary_time_used(0),m_pname(pstr)
|
||||||
|
{}
|
||||||
|
~local_call_account()
|
||||||
|
{
|
||||||
|
LOG_PRINT2("profile_details.log", "PROFILE "<<m_pname<<":av_time:\t" << (m_count_of_call ? (m_summary_time_used/m_count_of_call):0) <<" sum_time:\t"<<m_summary_time_used<<" call_count:\t" << m_count_of_call, LOG_LEVEL_0);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t m_count_of_call;
|
||||||
|
boost::uint64_t m_summary_time_used;
|
||||||
|
const char* m_pname;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct call_frame
|
||||||
|
{
|
||||||
|
|
||||||
|
call_frame(local_call_account& cc):m_cc(cc)
|
||||||
|
{
|
||||||
|
cc.m_count_of_call++;
|
||||||
|
m_call_time = boost::posix_time::microsec_clock::local_time();
|
||||||
|
//::QueryPerformanceCounter((LARGE_INTEGER *)&m_call_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
~call_frame()
|
||||||
|
{
|
||||||
|
//__int64 ret_time = 0;
|
||||||
|
|
||||||
|
boost::posix_time::ptime now_t(boost::posix_time::microsec_clock::local_time());
|
||||||
|
boost::posix_time::time_duration delta_microsec = now_t - m_call_time;
|
||||||
|
boost::uint64_t miliseconds_used = delta_microsec.total_microseconds();
|
||||||
|
|
||||||
|
//::QueryPerformanceCounter((LARGE_INTEGER *)&ret_time);
|
||||||
|
//m_call_time = (ret_time-m_call_time)/1000;
|
||||||
|
m_cc.m_summary_time_used += miliseconds_used;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
local_call_account& m_cc;
|
||||||
|
boost::posix_time::ptime m_call_time;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif //_PROFILE_TOOLS_H_
|
|
@ -0,0 +1,84 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _REG_EXP_DEFINER_H_
|
||||||
|
#define _REG_EXP_DEFINER_H_
|
||||||
|
|
||||||
|
#include <boost/interprocess/detail/atomic.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
class global_regexp_critical_section
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
mutable critical_section regexp_lock;
|
||||||
|
public:
|
||||||
|
global_regexp_critical_section(){}
|
||||||
|
critical_section& get_lock()const {return regexp_lock;}
|
||||||
|
};
|
||||||
|
|
||||||
|
const static global_regexp_critical_section gregexplock;
|
||||||
|
|
||||||
|
#define STATIC_REGEXP_EXPR_1(var_name, xpr_text, reg_exp_flags) \
|
||||||
|
static volatile boost::uint32_t regexp_initialized_1 = 0;\
|
||||||
|
volatile boost::uint32_t local_is_initialized_1 = regexp_initialized_1;\
|
||||||
|
if(!local_is_initialized_1)\
|
||||||
|
gregexplock.get_lock().lock();\
|
||||||
|
static const boost::regex var_name(xpr_text , reg_exp_flags);\
|
||||||
|
if(!local_is_initialized_1)\
|
||||||
|
{\
|
||||||
|
boost::interprocess::ipcdetail::atomic_write32(®exp_initialized_1, 1);\
|
||||||
|
gregexplock.get_lock().unlock();\
|
||||||
|
}
|
||||||
|
|
||||||
|
#define STATIC_REGEXP_EXPR_2(var_name, xpr_text, reg_exp_flags) \
|
||||||
|
static volatile boost::uint32_t regexp_initialized_2 = 0;\
|
||||||
|
volatile boost::uint32_t local_is_initialized_2 = regexp_initialized_2;\
|
||||||
|
if(!local_is_initialized_2)\
|
||||||
|
gregexplock.get_lock().lock().lock();\
|
||||||
|
static const boost::regex var_name(xpr_text , reg_exp_flags);\
|
||||||
|
if(!local_is_initialized_2)\
|
||||||
|
{\
|
||||||
|
boost::interprocess::ipcdetail::atomic_write32(®exp_initialized_2, 1);\
|
||||||
|
gregexplock.get_lock().lock().unlock();\
|
||||||
|
}
|
||||||
|
|
||||||
|
#define STATIC_REGEXP_EXPR_3(var_name, xpr_text, reg_exp_flags) \
|
||||||
|
static volatile boost::uint32_t regexp_initialized_3 = 0;\
|
||||||
|
volatile boost::uint32_t local_is_initialized_3 = regexp_initialized_3;\
|
||||||
|
if(!local_is_initialized_3)\
|
||||||
|
gregexplock.get_lock().lock().lock();\
|
||||||
|
static const boost::regex var_name(xpr_text , reg_exp_flags);\
|
||||||
|
if(!local_is_initialized_3)\
|
||||||
|
{\
|
||||||
|
boost::interprocess::ipcdetail::atomic_write32(®exp_initialized_3, 1);\
|
||||||
|
gregexplock.get_lock().lock().unlock();\
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //_REG_EXP_DEFINER_H_
|
|
@ -0,0 +1,249 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _MUSC_UTILS_EX_H_
|
||||||
|
#define _MUSC_UTILS_EX_H_
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace reg_utils
|
||||||
|
{
|
||||||
|
//-----------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class T>
|
||||||
|
bool RegSetPODValue(HKEY hParentKey, const char* pSubKey, const char* pValName, const T& valToSave, bool force_create = true)
|
||||||
|
{
|
||||||
|
HKEY hRegKey = 0;
|
||||||
|
DWORD dw = 0;
|
||||||
|
|
||||||
|
if( ::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_WRITE, &hRegKey) != ERROR_SUCCESS )
|
||||||
|
if(force_create && (::RegCreateKeyExA(hParentKey, pSubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hRegKey, &dw) != ERROR_SUCCESS) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
DWORD val_type = (sizeof(valToSave) == sizeof(DWORD)) ? REG_DWORD:REG_BINARY;
|
||||||
|
|
||||||
|
BOOL res = ::RegSetValueExA( hRegKey, pValName, 0, val_type, (LPBYTE)&valToSave, sizeof(valToSave)) == ERROR_SUCCESS;
|
||||||
|
|
||||||
|
::RegCloseKey(hRegKey);
|
||||||
|
return ERROR_SUCCESS==res ? true:false;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class T>
|
||||||
|
bool RegGetPODValue(HKEY hParentKey, const char* pSubKey, const char* pValName, T& valToSave)
|
||||||
|
{
|
||||||
|
HKEY hRegKey = 0;
|
||||||
|
LONG res = 0;
|
||||||
|
|
||||||
|
|
||||||
|
if(::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_READ, &hRegKey) == ERROR_SUCCESS )
|
||||||
|
{
|
||||||
|
DWORD dwType, lSize = 0;
|
||||||
|
res = ::RegQueryValueExA(hRegKey, pValName, 0, &dwType, NULL, &lSize);
|
||||||
|
if(ERROR_SUCCESS!=res || (sizeof(valToSave) < lSize) )
|
||||||
|
{
|
||||||
|
::RegCloseKey(hRegKey);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
res = ::RegQueryValueExA(hRegKey, pValName, 0, &dwType, (LPBYTE)&valToSave, &lSize);
|
||||||
|
}
|
||||||
|
return ERROR_SUCCESS==res ? true:false;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
bool RegSetANSIString(HKEY hParentKey, const char* pSubKey, const char* pValName, const std::string& strToSave)
|
||||||
|
{
|
||||||
|
HKEY hRegKey = 0;
|
||||||
|
DWORD dw = 0;
|
||||||
|
DWORD res_ = 0;
|
||||||
|
if( (res_ = ::RegCreateKeyExA(hParentKey, pSubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hRegKey, &dw)) != ERROR_SUCCESS )
|
||||||
|
if( (res_= ::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_WRITE, &hRegKey)) != ERROR_SUCCESS )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DWORD valType = REG_SZ;
|
||||||
|
const char* pStr = strToSave.c_str();
|
||||||
|
DWORD sizeOfStr = (DWORD)strToSave.size()+1;
|
||||||
|
LSTATUS res = ::RegSetValueExA(hRegKey, pValName, 0, valType, (LPBYTE)pStr, sizeOfStr);
|
||||||
|
|
||||||
|
::RegCloseKey(hRegKey);
|
||||||
|
return ERROR_SUCCESS==res ? true:false;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
bool RegGetANSIString(HKEY hParentKey, const char* pSubKey, const char* pValName, std::string& strToSave)
|
||||||
|
{
|
||||||
|
HKEY hRegKey = 0;
|
||||||
|
LONG res = 0;
|
||||||
|
|
||||||
|
|
||||||
|
if((res = ::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_READ, &hRegKey)) == ERROR_SUCCESS )
|
||||||
|
{
|
||||||
|
DWORD dwType, lSize = 0;
|
||||||
|
res = ::RegQueryValueExA(hRegKey, pValName, 0, &dwType, NULL, &lSize);
|
||||||
|
if(ERROR_SUCCESS!=res)
|
||||||
|
{
|
||||||
|
|
||||||
|
::RegCloseKey(hRegKey);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
char* pTmpStr = new char[lSize+2];
|
||||||
|
memset(pTmpStr, 0, lSize+2);
|
||||||
|
res = ::RegQueryValueExA(hRegKey, pValName, 0, &dwType, (LPBYTE)pTmpStr, &lSize);
|
||||||
|
pTmpStr[lSize+1] = 0; //be happy ;)
|
||||||
|
strToSave = pTmpStr;
|
||||||
|
delete [] pTmpStr;
|
||||||
|
::RegCloseKey(hRegKey);
|
||||||
|
}
|
||||||
|
return ERROR_SUCCESS==res ? true:false;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class TMemoryObject>
|
||||||
|
bool RegSetRAWValue(HKEY hKey, const char* pValName, const TMemoryObject& valToSave, DWORD valType = REG_BINARY)
|
||||||
|
{
|
||||||
|
LONG res = ::RegSetValueExA( hKey, pValName, 0, valType, (CONST BYTE*)valToSave.get(0), (DWORD)valToSave.get_size());
|
||||||
|
|
||||||
|
return ERROR_SUCCESS==res ? true:false;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
bool RegSetRAWValue(HKEY hKey, const char* pValName, const std::string & valToSave, DWORD valType = REG_BINARY)
|
||||||
|
{
|
||||||
|
LONG res = ::RegSetValueExA( hKey, pValName, 0, valType, (CONST BYTE*)valToSave.data(), (DWORD)valToSave.size());
|
||||||
|
|
||||||
|
return ERROR_SUCCESS==res ? true:false;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class TMemoryObject>
|
||||||
|
bool RegGetRAWValue(HKEY hKey, const char* pValName, TMemoryObject& valToSave, DWORD* pRegType)
|
||||||
|
{
|
||||||
|
DWORD dwType, lSize = 0;
|
||||||
|
LONG res = ::RegQueryValueExA(hKey, pValName, 0, &dwType, NULL, &lSize);
|
||||||
|
if(ERROR_SUCCESS!=res || 0 >= lSize)
|
||||||
|
{
|
||||||
|
valToSave.release();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(valToSave.get_size() < lSize)
|
||||||
|
valToSave.alloc_buff(lSize);
|
||||||
|
res = ::RegQueryValueExA(hKey, pValName, 0, &dwType, (LPBYTE)valToSave.get(0), &lSize);
|
||||||
|
if(pRegType) *pRegType = dwType;
|
||||||
|
|
||||||
|
return ERROR_SUCCESS==res ? true:false;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
bool RegGetRAWValue(HKEY hKey, const char* pValName, std::string& valToSave, DWORD* pRegType)
|
||||||
|
{
|
||||||
|
DWORD dwType, lSize = 0;
|
||||||
|
LONG res = ::RegQueryValueExA(hKey, pValName, 0, &dwType, NULL, &lSize);
|
||||||
|
if(ERROR_SUCCESS!=res || 0 >= lSize)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
valToSave.resize(lSize);
|
||||||
|
res = ::RegQueryValueExA(hKey, pValName, 0, &dwType, (LPBYTE)valToSave.data(), &lSize);
|
||||||
|
if(pRegType) *pRegType = dwType;
|
||||||
|
|
||||||
|
return ERROR_SUCCESS==res ? true:false;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class TMemoryObject>
|
||||||
|
bool RegSetRAWValue(HKEY hParentKey, const char* pSubKey, const char* pValName, const TMemoryObject& valToSave, DWORD valType = REG_BINARY)
|
||||||
|
{
|
||||||
|
HKEY hRegKey = 0;
|
||||||
|
DWORD dw = 0;
|
||||||
|
bool res = false;
|
||||||
|
|
||||||
|
if( ::RegCreateKeyExA(hParentKey, pSubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hRegKey, &dw) != ERROR_SUCCESS )
|
||||||
|
if( ::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_WRITE, &hRegKey) != ERROR_SUCCESS )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
res = RegSetRAWValue(hRegKey, pValName, valToSave, valType);
|
||||||
|
|
||||||
|
::RegCloseKey(hRegKey);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
bool RegSetRAWValue(HKEY hParentKey, const char* pSubKey, const char* pValName, const std::string& valToSave, DWORD valType = REG_BINARY)
|
||||||
|
{
|
||||||
|
HKEY hRegKey = 0;
|
||||||
|
DWORD dw = 0;
|
||||||
|
bool res = false;
|
||||||
|
|
||||||
|
if( ::RegCreateKeyExA(hParentKey, pSubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hRegKey, &dw) != ERROR_SUCCESS )
|
||||||
|
if( ::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_WRITE, &hRegKey) != ERROR_SUCCESS )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
res = RegSetRAWValue(hRegKey, pValName, valToSave, valType);
|
||||||
|
|
||||||
|
::RegCloseKey(hRegKey);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class TMemoryObject>
|
||||||
|
bool RegGetRAWValue(HKEY hParentKey, const char* pSubKey, const char* pValName, TMemoryObject& valToSave, DWORD* pRegType)
|
||||||
|
{
|
||||||
|
HKEY hRegKey = 0;
|
||||||
|
bool res = false;
|
||||||
|
|
||||||
|
if(::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_READ, &hRegKey) == ERROR_SUCCESS )
|
||||||
|
{
|
||||||
|
res = RegGetRAWValue(hRegKey, pValName, valToSave, pRegType);
|
||||||
|
::RegCloseKey(hRegKey);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
bool RegGetRAWValue(HKEY hParentKey, const char* pSubKey, const char* pValName, std::string& valToSave, DWORD* pRegType)
|
||||||
|
{
|
||||||
|
HKEY hRegKey = 0;
|
||||||
|
bool res = false;
|
||||||
|
|
||||||
|
if(::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_READ, &hRegKey) == ERROR_SUCCESS )
|
||||||
|
{
|
||||||
|
res = RegGetRAWValue(hRegKey, pValName, valToSave, pRegType);
|
||||||
|
::RegCloseKey(hRegKey);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
bool RegRemoveValue(HKEY hParentKey, const char* pValName)
|
||||||
|
{
|
||||||
|
//CHECK_AND_ASSERT(hParentKey&&pValName, false);
|
||||||
|
return ::RegDeleteValueA(hParentKey, pValName)==ERROR_SUCCESS ? true:false;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
bool RegRemoveKey(HKEY hParentKey, const char* pKeyName)
|
||||||
|
{
|
||||||
|
//CHECK_AND_ASSERT(hParentKey&&pKeyName, false);
|
||||||
|
return ::RegDeleteKeyA(hParentKey, pKeyName)==ERROR_SUCCESS ? true:false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif //_MUSC_UTILS_EX_H_
|
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright (c) 2006-2013, 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
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
|
||||||
|
template<class t_obj>
|
||||||
|
struct enableable
|
||||||
|
{
|
||||||
|
t_obj v;
|
||||||
|
bool enabled;
|
||||||
|
|
||||||
|
enableable()
|
||||||
|
: v(t_obj()), enabled(true)
|
||||||
|
{ // construct from defaults
|
||||||
|
}
|
||||||
|
|
||||||
|
enableable(const t_obj& _v)
|
||||||
|
: v(_v), enabled(true)
|
||||||
|
{ // construct from specified values
|
||||||
|
}
|
||||||
|
|
||||||
|
enableable(const enableable<t_obj>& _v)
|
||||||
|
: v(_v.v), enabled(_v.enabled)
|
||||||
|
{ // construct from specified values
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 <boost/utility/value_init.hpp>
|
||||||
|
#include <boost/foreach.hpp>
|
||||||
|
#include "misc_log_ex.h"
|
||||||
|
#include "enableable.h"
|
||||||
|
#include "keyvalue_serialization_overloads.h"
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
/************************************************************************/
|
||||||
|
/* Serialize map declarations */
|
||||||
|
/************************************************************************/
|
||||||
|
#define BEGIN_KV_SERIALIZE_MAP() \
|
||||||
|
public: \
|
||||||
|
template<class t_storage> \
|
||||||
|
bool store( t_storage& st, typename t_storage::hsection hparent_section = nullptr) const\
|
||||||
|
{\
|
||||||
|
return serialize_map<true>(*this, st, hparent_section);\
|
||||||
|
}\
|
||||||
|
template<class t_storage> \
|
||||||
|
bool _load( t_storage& stg, typename t_storage::hsection hparent_section = nullptr)\
|
||||||
|
{\
|
||||||
|
return serialize_map<false>(*this, stg, hparent_section);\
|
||||||
|
}\
|
||||||
|
template<class t_storage> \
|
||||||
|
bool load( t_storage& stg, typename t_storage::hsection hparent_section = nullptr)\
|
||||||
|
{\
|
||||||
|
try{\
|
||||||
|
return serialize_map<false>(*this, stg, hparent_section);\
|
||||||
|
}\
|
||||||
|
catch(const std::exception& err) \
|
||||||
|
{ \
|
||||||
|
(void)(err); \
|
||||||
|
LOG_ERROR("Exception on unserializing: " << err.what());\
|
||||||
|
return false; \
|
||||||
|
}\
|
||||||
|
}\
|
||||||
|
template<bool is_store, class this_type, class t_storage> \
|
||||||
|
static bool serialize_map(this_type& this_ref, t_storage& stg, typename t_storage::hsection hparent_section) \
|
||||||
|
{
|
||||||
|
|
||||||
|
#define KV_SERIALIZE_N(varialble, val_name) \
|
||||||
|
epee::serialization::selector<is_store>::serialize(this_ref.varialble, stg, hparent_section, val_name);
|
||||||
|
|
||||||
|
#define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name) \
|
||||||
|
epee::serialization::selector<is_store>::serialize_t_val_as_blob(this_ref.varialble, stg, hparent_section, val_name);
|
||||||
|
|
||||||
|
#define KV_SERIALIZE_VAL_POD_AS_BLOB_N(varialble, val_name) \
|
||||||
|
static_assert(std::is_pod<decltype(this_ref.varialble)>::value, "t_type must be a POD type."); \
|
||||||
|
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name)
|
||||||
|
|
||||||
|
#define KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, val_name) \
|
||||||
|
epee::serialization::selector<is_store>::serialize_stl_container_pod_val_as_blob(this_ref.varialble, stg, hparent_section, val_name);
|
||||||
|
|
||||||
|
#define END_KV_SERIALIZE_MAP() return true;}
|
||||||
|
|
||||||
|
#define KV_SERIALIZE(varialble) KV_SERIALIZE_N(varialble, #varialble)
|
||||||
|
#define KV_SERIALIZE_VAL_POD_AS_BLOB(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_N(varialble, #varialble)
|
||||||
|
#define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, #varialble) //skip is_pod compile time check
|
||||||
|
#define KV_SERIALIZE_CONTAINER_POD_AS_BLOB(varialble) KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, #varialble)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,366 @@
|
||||||
|
// Copyright (c) 2006-2013, 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
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace serialization
|
||||||
|
{
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
static bool serialize_t_val(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return stg.set_value(pname, d, hparent_section);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
static bool unserialize_t_val(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return stg.get_value(pname, d, hparent_section);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
static bool serialize_t_val_as_blob(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
std::string blob((const char *)&d, sizeof(d));
|
||||||
|
return stg.set_value(pname, blob, hparent_section);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
static bool unserialize_t_val_as_blob(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
std::string blob;
|
||||||
|
if(!stg.get_value(pname, blob, hparent_section))
|
||||||
|
return false;
|
||||||
|
CHECK_AND_ASSERT_MES(blob.size() == sizeof(d), false, "unserialize_t_val_as_blob: size of " << typeid(t_type).name() << " = " << sizeof(t_type) << ", but stored blod size = " << blob.size() << ", value name = " << pname);
|
||||||
|
d = *(const t_type*)blob.data();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class serializible_type, class t_storage>
|
||||||
|
static bool serialize_t_obj(const serializible_type& obj, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, true);
|
||||||
|
CHECK_AND_ASSERT_MES(hchild_section, false, "serialize_t_obj: failed to open/create section " << pname);
|
||||||
|
return obj.store(stg, hchild_section);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class serializible_type, class t_storage>
|
||||||
|
static bool unserialize_t_obj(serializible_type& obj, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, true);
|
||||||
|
if(!hchild_section) return false;
|
||||||
|
return obj._load(stg, hchild_section);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class serializible_type, class t_storage>
|
||||||
|
static bool serialize_t_obj(enableable<serializible_type>& obj, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
if(!obj.enabled)
|
||||||
|
return true;
|
||||||
|
return serialize_t_obj(obj.v, stg, hparent_section, pname);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class serializible_type, class t_storage>
|
||||||
|
static bool unserialize_t_obj(enableable<serializible_type>& obj, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
obj.enabled = false;
|
||||||
|
typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, true);
|
||||||
|
if(!hchild_section) return false;
|
||||||
|
obj.enabled = true;
|
||||||
|
return obj.v._load(stg, hchild_section);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class stl_container, class t_storage>
|
||||||
|
static bool serialize_stl_container_t_val (const stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
if(!container.size()) return true;
|
||||||
|
typename stl_container::const_iterator it = container.begin();
|
||||||
|
typename t_storage::harray hval_array = stg.insert_first_value(pname, *it, hparent_section);
|
||||||
|
CHECK_AND_ASSERT_MES(hval_array, false, "failed to insert first value to storage");
|
||||||
|
it++;
|
||||||
|
for(;it!= container.end();it++)
|
||||||
|
stg.insert_next_value(hval_array, *it);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class stl_container, class t_storage>
|
||||||
|
static bool unserialize_stl_container_t_val(stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
container.clear();
|
||||||
|
typename stl_container::value_type exchange_val;
|
||||||
|
typename t_storage::harray hval_array = stg.get_first_value(pname, exchange_val, hparent_section);
|
||||||
|
if(!hval_array) return false;
|
||||||
|
container.push_back(std::move(exchange_val));
|
||||||
|
while(stg.get_next_value(hval_array, exchange_val))
|
||||||
|
container.push_back(std::move(exchange_val));
|
||||||
|
return true;
|
||||||
|
}//--------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class stl_container, class t_storage>
|
||||||
|
static bool serialize_stl_container_pod_val_as_blob(const stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
if(!container.size()) return true;
|
||||||
|
typename stl_container::const_iterator it = container.begin();
|
||||||
|
std::string mb;
|
||||||
|
mb.resize(sizeof(typename stl_container::value_type)*container.size());
|
||||||
|
typename stl_container::value_type* p_elem = (typename stl_container::value_type*)mb.data();
|
||||||
|
BOOST_FOREACH(const typename stl_container::value_type& v, container)
|
||||||
|
{
|
||||||
|
*p_elem = v;
|
||||||
|
p_elem++;
|
||||||
|
}
|
||||||
|
return stg.set_value(pname, mb, hparent_section);
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class stl_container, class t_storage>
|
||||||
|
static bool unserialize_stl_container_pod_val_as_blob(stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
container.clear();
|
||||||
|
std::string buff;
|
||||||
|
bool res = stg.get_value(pname, buff, hparent_section);
|
||||||
|
if(res)
|
||||||
|
{
|
||||||
|
size_t loaded_size = buff.size();
|
||||||
|
typename stl_container::value_type* pelem = (typename stl_container::value_type*)buff.data();
|
||||||
|
CHECK_AND_ASSERT_MES(!(loaded_size%sizeof(typename stl_container::value_type)),
|
||||||
|
false,
|
||||||
|
"size in blob " << loaded_size << " not have not zero modulo for sizeof(value_type) = " << sizeof(typename stl_container::value_type));
|
||||||
|
size_t count = (loaded_size/sizeof(typename stl_container::value_type));
|
||||||
|
for(size_t i = 0; i < count; i++)
|
||||||
|
container.push_back(*(pelem++));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class stl_container, class t_storage>
|
||||||
|
static bool serialize_stl_container_t_obj (const stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
bool res = false;
|
||||||
|
if(!container.size()) return true;
|
||||||
|
typename stl_container::const_iterator it = container.begin();
|
||||||
|
typename t_storage::hsection hchild_section = nullptr;
|
||||||
|
typename t_storage::harray hsec_array = stg.insert_first_section(pname, hchild_section, hparent_section);
|
||||||
|
CHECK_AND_ASSERT_MES(hsec_array && hchild_section, false, "failed to insert first section with section name " << pname);
|
||||||
|
res = it->store(stg, hchild_section);
|
||||||
|
it++;
|
||||||
|
for(;it!= container.end();it++)
|
||||||
|
{
|
||||||
|
stg.insert_next_section(hsec_array, hchild_section);
|
||||||
|
res |= it->store(stg, hchild_section);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class stl_container, class t_storage>
|
||||||
|
static bool unserialize_stl_container_t_obj(stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
bool res = false;
|
||||||
|
container.clear();
|
||||||
|
typename stl_container::value_type val = typename stl_container::value_type();
|
||||||
|
typename t_storage::hsection hchild_section = nullptr;
|
||||||
|
typename t_storage::harray hsec_array = stg.get_first_section(pname, hchild_section, hparent_section);
|
||||||
|
if(!hsec_array || !hchild_section) return false;
|
||||||
|
res = val._load(stg, hchild_section);
|
||||||
|
container.push_back(val);
|
||||||
|
while(stg.get_next_section(hsec_array, hchild_section))
|
||||||
|
{
|
||||||
|
typename stl_container::value_type val_l = typename stl_container::value_type();
|
||||||
|
res |= val_l._load(stg, hchild_section);
|
||||||
|
container.push_back(std::move(val_l));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<bool>
|
||||||
|
struct kv_serialization_overloads_impl_is_base_serializable_types;
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct kv_serialization_overloads_impl_is_base_serializable_types<true>
|
||||||
|
{
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
static bool kv_serialize(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return stg.set_value(pname, d, hparent_section);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
static bool kv_unserialize(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return stg.get_value(pname, d, hparent_section);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
static bool kv_serialize(const std::vector<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return serialize_stl_container_t_val(d, stg, hparent_section, pname);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
static bool kv_unserialize(std::vector<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return unserialize_stl_container_t_val(d, stg, hparent_section, pname);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
static bool kv_serialize(const std::list<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return serialize_stl_container_t_val(d, stg, hparent_section, pname);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
static bool kv_unserialize(std::list<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return unserialize_stl_container_t_val(d, stg, hparent_section, pname);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct kv_serialization_overloads_impl_is_base_serializable_types<false>
|
||||||
|
{
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
static bool kv_serialize(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return serialize_t_obj(d, stg, hparent_section, pname);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
static bool kv_unserialize(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return unserialize_t_obj(d, stg, hparent_section, pname);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
static bool kv_serialize(const std::vector<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return serialize_stl_container_t_obj(d, stg, hparent_section, pname);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
static bool kv_unserialize(std::vector<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return unserialize_stl_container_t_obj(d, stg, hparent_section, pname);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
static bool kv_serialize(const std::list<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return serialize_stl_container_t_obj(d, stg, hparent_section, pname);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
static bool kv_unserialize(std::list<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return unserialize_stl_container_t_obj(d, stg, hparent_section, pname);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
typedef boost::mpl::vector<uint64_t, uint32_t, uint16_t, uint8_t, int64_t, int32_t, int16_t, int8_t, double, bool, std::string>::type base_serializable_types;
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<bool> struct selector;
|
||||||
|
template<>
|
||||||
|
struct selector<true>
|
||||||
|
{
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
static bool serialize(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return kv_serialize(d, stg, hparent_section, pname);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
static bool serialize_stl_container_pod_val_as_blob(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return epee::serialization::serialize_stl_container_pod_val_as_blob(d, stg, hparent_section, pname);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
static bool serialize_t_val_as_blob(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return epee::serialization::serialize_t_val_as_blob(d, stg, hparent_section, pname);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct selector<false>
|
||||||
|
{
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
static bool serialize(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return kv_unserialize(d, stg, hparent_section, pname);
|
||||||
|
}
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
static bool serialize_stl_container_pod_val_as_blob(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return epee::serialization::unserialize_stl_container_pod_val_as_blob(d, stg, hparent_section, pname);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
static bool serialize_t_val_as_blob(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return epee::serialization::unserialize_t_val_as_blob(d, stg, hparent_section, pname);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
bool kv_serialize(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types, typename std::remove_const<t_type>::type>::value>::kv_serialize(d, stg, hparent_section, pname);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
bool kv_unserialize(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types, typename std::remove_const<t_type>::type>::value>::kv_unserialize(d, stg, hparent_section, pname);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
bool kv_serialize(const std::vector<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types, typename std::remove_const<t_type>::type>::value>::kv_serialize(d, stg, hparent_section, pname);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
bool kv_unserialize(std::vector<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types, typename std::remove_const<t_type>::type>::value>::kv_unserialize(d, stg, hparent_section, pname);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
bool kv_serialize(const std::list<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types, typename std::remove_const<t_type>::type>::value>::kv_serialize(d, stg, hparent_section, pname);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class t_type, class t_storage>
|
||||||
|
bool kv_unserialize(std::list<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||||
|
{
|
||||||
|
return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types, typename std::remove_const<t_type>::type>::value>::kv_unserialize(d, stg, hparent_section, pname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
#pragma once
|
||||||
|
|
|
@ -0,0 +1,323 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _SERVICE_IMPL_BASE_H_
|
||||||
|
#define _SERVICE_IMPL_BASE_H_
|
||||||
|
|
||||||
|
#pragma comment(lib, "advapi32.lib")
|
||||||
|
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
class service_impl_base {
|
||||||
|
public:
|
||||||
|
service_impl_base();
|
||||||
|
virtual ~service_impl_base();
|
||||||
|
|
||||||
|
virtual const char *get_name() = 0;
|
||||||
|
virtual const char *get_caption() = 0;
|
||||||
|
virtual const char *get_description() = 0;
|
||||||
|
|
||||||
|
bool run_service();
|
||||||
|
virtual bool install();
|
||||||
|
virtual bool remove();
|
||||||
|
virtual bool init();
|
||||||
|
void set_control_accepted(unsigned controls);
|
||||||
|
void set_status(unsigned state, unsigned pending = 0);
|
||||||
|
unsigned get_control_accepted();
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void service_main() = 0;
|
||||||
|
virtual unsigned service_handler(unsigned control, unsigned event_code,
|
||||||
|
void *pdata) = 0;
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
static service_impl_base*& instance();
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
static DWORD __stdcall _service_handler(DWORD control, DWORD event,
|
||||||
|
void *pdata, void *pcontext);
|
||||||
|
static void __stdcall service_entry(DWORD argc, char **pargs);
|
||||||
|
virtual SERVICE_FAILURE_ACTIONSA* get_failure_actions();
|
||||||
|
|
||||||
|
private:
|
||||||
|
SC_HANDLE m_manager;
|
||||||
|
SC_HANDLE m_service;
|
||||||
|
SERVICE_STATUS_HANDLE m_status_handle;
|
||||||
|
DWORD m_accepted_control;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline service_impl_base::service_impl_base() {
|
||||||
|
m_manager = 0;
|
||||||
|
m_service = 0;
|
||||||
|
m_status_handle = 0;
|
||||||
|
m_accepted_control = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN
|
||||||
|
| SERVICE_ACCEPT_PAUSE_CONTINUE;
|
||||||
|
|
||||||
|
instance() = this;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
inline service_impl_base::~service_impl_base() {
|
||||||
|
if (m_service) {
|
||||||
|
::CloseServiceHandle(m_service);
|
||||||
|
}
|
||||||
|
m_service = 0;
|
||||||
|
if (m_manager) {
|
||||||
|
::CloseServiceHandle(m_manager);
|
||||||
|
}
|
||||||
|
m_manager = 0;
|
||||||
|
instance() = 0;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
inline service_impl_base*& service_impl_base::instance() {
|
||||||
|
static service_impl_base *pservice = NULL;
|
||||||
|
return pservice;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
bool service_impl_base::install() {
|
||||||
|
CHECK_AND_ASSERT(!m_service, false);
|
||||||
|
const char *psz_descr = get_description();
|
||||||
|
SERVICE_FAILURE_ACTIONSA* fail_acts = get_failure_actions();
|
||||||
|
|
||||||
|
char sz_path[MAX_PATH];
|
||||||
|
::GetModuleFileNameA(0, sz_path, sizeof(sz_path));
|
||||||
|
::GetShortPathNameA(sz_path, sz_path, sizeof(sz_path));
|
||||||
|
|
||||||
|
while (TRUE) {
|
||||||
|
if (!m_manager) {
|
||||||
|
m_manager = ::OpenSCManager(NULL, NULL, GENERIC_ALL);
|
||||||
|
if (!m_manager) {
|
||||||
|
int err = GetLastError();
|
||||||
|
LOG_ERROR(
|
||||||
|
"Failed to OpenSCManager(), last err="
|
||||||
|
<< log_space::get_win32_err_descr(err));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_service = ::CreateServiceA(m_manager, get_name(), get_caption(),
|
||||||
|
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START,
|
||||||
|
SERVICE_ERROR_IGNORE, sz_path, 0, 0, 0, 0, 0);
|
||||||
|
if (!m_service) {
|
||||||
|
int err = GetLastError();
|
||||||
|
LOG_ERROR(
|
||||||
|
"Failed to CreateService(), last err="
|
||||||
|
<< log_space::get_win32_err_descr(err));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (psz_descr) {
|
||||||
|
SERVICE_DESCRIPTIONA sd = { (char*) psz_descr };
|
||||||
|
if (!::ChangeServiceConfig2A(m_service, SERVICE_CONFIG_DESCRIPTION,
|
||||||
|
&sd)) {
|
||||||
|
int err = GetLastError();
|
||||||
|
LOG_ERROR(
|
||||||
|
"Failed to ChangeServiceConfig2(SERVICE_CONFIG_DESCRIPTION), last err="
|
||||||
|
<< log_space::get_win32_err_descr(err));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fail_acts) {
|
||||||
|
if (!::ChangeServiceConfig2A(m_service, SERVICE_CONFIG_FAILURE_ACTIONS,
|
||||||
|
fail_acts)) {
|
||||||
|
int err = GetLastError();
|
||||||
|
LOG_ERROR(
|
||||||
|
"Failed to ChangeServiceConfig2(SERVICE_CONFIG_FAILURE_ACTIONS), last err="
|
||||||
|
<< log_space::get_win32_err_descr(err));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG_PRINT("Installed succesfully.", LOG_LEVEL_0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
LOG_PRINT("Failed to install.", LOG_LEVEL_0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
bool service_impl_base::remove() {
|
||||||
|
CHECK_AND_ASSERT(!m_service, false);
|
||||||
|
|
||||||
|
while (TRUE) {
|
||||||
|
if (!m_manager) {
|
||||||
|
m_manager = ::OpenSCManager(0, 0, GENERIC_ALL);
|
||||||
|
if (!m_manager) {
|
||||||
|
int err = GetLastError();
|
||||||
|
LOG_ERROR(
|
||||||
|
"Failed to OpenSCManager(), last err="
|
||||||
|
<< log_space::get_win32_err_descr(err));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_service) {
|
||||||
|
m_service = ::OpenServiceA(m_manager, get_name(), SERVICE_STOP | DELETE);
|
||||||
|
if (!m_service) {
|
||||||
|
int err = GetLastError();
|
||||||
|
LOG_ERROR(
|
||||||
|
"Failed to OpenService(), last err="
|
||||||
|
<< log_space::get_win32_err_descr(err));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SERVICE_STATUS status = { };
|
||||||
|
if (!::ControlService(m_service, SERVICE_CONTROL_STOP, &status)) {
|
||||||
|
int err = ::GetLastError();
|
||||||
|
if (err == ERROR_SHUTDOWN_IN_PROGRESS)
|
||||||
|
continue;
|
||||||
|
else if (err != ERROR_SERVICE_NOT_ACTIVE) {
|
||||||
|
LOG_ERROR(
|
||||||
|
"Failed to ControlService(SERVICE_CONTROL_STOP), last err="
|
||||||
|
<< log_space::get_win32_err_descr(err));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!::DeleteService(m_service)) {
|
||||||
|
int err = ::GetLastError();
|
||||||
|
LOG_ERROR(
|
||||||
|
"Failed to ControlService(SERVICE_CONTROL_STOP), last err="
|
||||||
|
<< log_space::get_win32_err_descr(err));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_PRINT("Removed successfully.", LOG_LEVEL_0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
bool service_impl_base::init() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
bool service_impl_base::run_service() {
|
||||||
|
CHECK_AND_ASSERT(!m_service, false);
|
||||||
|
|
||||||
|
long error_code = 0;
|
||||||
|
|
||||||
|
SERVICE_TABLE_ENTRYA service_table[2];
|
||||||
|
ZeroMemory(&service_table, sizeof(service_table));
|
||||||
|
|
||||||
|
service_table->lpServiceName = (char*) get_name();
|
||||||
|
service_table->lpServiceProc = service_entry;
|
||||||
|
|
||||||
|
LOG_PRINT("[+] Start service control dispatcher for \"" << get_name() << "\"",
|
||||||
|
LOG_LEVEL_1);
|
||||||
|
|
||||||
|
error_code = 1;
|
||||||
|
BOOL res = ::StartServiceCtrlDispatcherA(service_table);
|
||||||
|
if (!res) {
|
||||||
|
int err = GetLastError();
|
||||||
|
LOG_PRINT(
|
||||||
|
"[+] Error starting service control dispatcher, err="
|
||||||
|
<< log_space::get_win32_err_descr(err), LOG_LEVEL_1);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
LOG_PRINT("[+] End service control dispatcher for \"" << get_name() << "\"",
|
||||||
|
LOG_LEVEL_1);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
inline DWORD __stdcall service_impl_base::_service_handler(DWORD control,
|
||||||
|
DWORD event, void *pdata, void *pcontext) {
|
||||||
|
CHECK_AND_ASSERT(pcontext, ERROR_CALL_NOT_IMPLEMENTED);
|
||||||
|
|
||||||
|
service_impl_base *pservice = (service_impl_base*) pcontext;
|
||||||
|
return pservice->service_handler(control, event, pdata);
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
void __stdcall service_impl_base::service_entry(DWORD argc, char **pargs) {
|
||||||
|
service_impl_base *pme = instance();
|
||||||
|
LOG_PRINT("instance: " << pme, LOG_LEVEL_4);
|
||||||
|
if (!pme) {
|
||||||
|
LOG_ERROR("Error: at service_entry() pme = NULL");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pme->m_status_handle = ::RegisterServiceCtrlHandlerExA(pme->get_name(),
|
||||||
|
_service_handler, pme);
|
||||||
|
|
||||||
|
pme->set_status(SERVICE_RUNNING);
|
||||||
|
pme->service_main();
|
||||||
|
pme->set_status(SERVICE_STOPPED);
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
void service_impl_base::set_status(unsigned state, unsigned pending) {
|
||||||
|
if (!m_status_handle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SERVICE_STATUS status = { 0 };
|
||||||
|
status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
||||||
|
status.dwCurrentState = state;
|
||||||
|
status.dwControlsAccepted = m_accepted_control;
|
||||||
|
/*status.dwWin32ExitCode = NO_ERROR;
|
||||||
|
status.dwServiceSpecificExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
|
||||||
|
status.dwCheckPoint = 0;
|
||||||
|
status.dwWaitHint = 0;
|
||||||
|
|
||||||
|
status.dwCurrentState = state;*/
|
||||||
|
|
||||||
|
if (state == SERVICE_START_PENDING || state == SERVICE_STOP_PENDING
|
||||||
|
|| state == SERVICE_CONTINUE_PENDING || state == SERVICE_PAUSE_PENDING) {
|
||||||
|
status.dwWaitHint = 2000;
|
||||||
|
status.dwCheckPoint = pending;
|
||||||
|
}
|
||||||
|
::SetServiceStatus(m_status_handle, &status);
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
void service_impl_base::set_control_accepted(unsigned controls) {
|
||||||
|
m_accepted_control = controls;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
unsigned service_impl_base::get_control_accepted() {
|
||||||
|
return m_accepted_control;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------------------
|
||||||
|
inline SERVICE_FAILURE_ACTIONSA* service_impl_base::get_failure_actions() {
|
||||||
|
// first 3 failures in 30 minutes. Service will be restarted.
|
||||||
|
// do nothing for next failures
|
||||||
|
static SC_ACTION sa[] = { { SC_ACTION_RESTART, 3 * 1000 }, {
|
||||||
|
SC_ACTION_RESTART, 3 * 1000 }, { SC_ACTION_RESTART, 3 * 1000 }, {
|
||||||
|
SC_ACTION_NONE, 0 } };
|
||||||
|
|
||||||
|
static SERVICE_FAILURE_ACTIONSA sfa = { 1800, // interval for failures counter - 30 min
|
||||||
|
"", NULL, 4, (SC_ACTION*) &sa };
|
||||||
|
|
||||||
|
// TODO: refactor this code, really unsafe!
|
||||||
|
return &sfa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //_SERVICE_IMPL_BASE_H_
|
|
@ -0,0 +1,51 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) 2011, Micael Hildenborg
|
||||||
|
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 Micael Hildenborg 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 Micael Hildenborg ''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 Micael Hildenborg 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SHA1_DEFINED
|
||||||
|
#define SHA1_DEFINED
|
||||||
|
|
||||||
|
namespace sha1 {
|
||||||
|
|
||||||
|
/**
|
||||||
|
@param src points to any kind of data to be hashed.
|
||||||
|
@param bytelength the number of bytes to hash from the src pointer.
|
||||||
|
@param hash should point to a buffer of at least 20 bytes of size for storing the sha1 result in.
|
||||||
|
*/
|
||||||
|
void calc(const void* src, const int bytelength, unsigned char* hash);
|
||||||
|
|
||||||
|
/**
|
||||||
|
@param hash is 20 bytes of sha1 hash. This is the same data that is the result from the calc function.
|
||||||
|
@param hexstring should point to a buffer of at least 41 bytes of size for storing the hexadecimal representation of the hash. A zero will be written at position 40, so the buffer will be a valid zero ended string.
|
||||||
|
*/
|
||||||
|
void toHexString(const unsigned char* hash, char* hexstring);
|
||||||
|
|
||||||
|
} // namespace sha1
|
||||||
|
|
||||||
|
#include "sha1.inl"
|
||||||
|
|
||||||
|
#endif // SHA1_DEFINED
|
|
@ -0,0 +1,179 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) 2011, Micael Hildenborg
|
||||||
|
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 Micael Hildenborg 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 Micael Hildenborg ''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 Micael Hildenborg 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Contributors:
|
||||||
|
Gustav
|
||||||
|
Several members in the gamedev.se forum.
|
||||||
|
Gregory Petrosyan
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sha1.h"
|
||||||
|
|
||||||
|
namespace sha1 {
|
||||||
|
namespace {// local
|
||||||
|
// Rotate an integer value to left.
|
||||||
|
inline const unsigned int rol(const unsigned int value,
|
||||||
|
const unsigned int steps) {
|
||||||
|
return ((value << steps) | (value >> (32 - steps)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the first 16 integers in the buffert to zero.
|
||||||
|
// Used for clearing the W buffert.
|
||||||
|
inline void clearWBuffert(unsigned int* buffert) {
|
||||||
|
for (int pos = 16; --pos >= 0;)
|
||||||
|
{
|
||||||
|
buffert[pos] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void innerHash(unsigned int* result, unsigned int* w) {
|
||||||
|
unsigned int a = result[0];
|
||||||
|
unsigned int b = result[1];
|
||||||
|
unsigned int c = result[2];
|
||||||
|
unsigned int d = result[3];
|
||||||
|
unsigned int e = result[4];
|
||||||
|
|
||||||
|
int round = 0;
|
||||||
|
|
||||||
|
#define sha1macro(func,val) \
|
||||||
|
{ \
|
||||||
|
const unsigned int t = rol(a, 5) + (func) + e + val + w[round]; \
|
||||||
|
e = d; \
|
||||||
|
d = c; \
|
||||||
|
c = rol(b, 30); \
|
||||||
|
b = a; \
|
||||||
|
a = t; \
|
||||||
|
}
|
||||||
|
|
||||||
|
while (round < 16) {
|
||||||
|
sha1macro((b & c) | (~b & d), 0x5a827999)
|
||||||
|
++round;
|
||||||
|
}
|
||||||
|
while (round < 20) {
|
||||||
|
w[round] = rol(
|
||||||
|
(w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
|
||||||
|
sha1macro((b & c) | (~b & d), 0x5a827999)
|
||||||
|
++round;
|
||||||
|
}
|
||||||
|
while (round < 40) {
|
||||||
|
w[round] = rol(
|
||||||
|
(w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
|
||||||
|
sha1macro(b ^ c ^ d, 0x6ed9eba1)
|
||||||
|
++round;
|
||||||
|
}
|
||||||
|
while (round < 60) {
|
||||||
|
w[round] = rol(
|
||||||
|
(w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
|
||||||
|
sha1macro((b & c) | (b & d) | (c & d), 0x8f1bbcdc)
|
||||||
|
++round;
|
||||||
|
}
|
||||||
|
while (round < 80) {
|
||||||
|
w[round] = rol(
|
||||||
|
(w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
|
||||||
|
sha1macro(b ^ c ^ d, 0xca62c1d6)
|
||||||
|
++round;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef sha1macro
|
||||||
|
|
||||||
|
result[0] += a;
|
||||||
|
result[1] += b;
|
||||||
|
result[2] += c;
|
||||||
|
result[3] += d;
|
||||||
|
result[4] += e;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
inline
|
||||||
|
void calc(const void* src, const int bytelength, unsigned char* hash) {
|
||||||
|
// Init the result array.
|
||||||
|
unsigned int result[5] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476,
|
||||||
|
0xc3d2e1f0 };
|
||||||
|
|
||||||
|
// Cast the void src pointer to be the byte array we can work with.
|
||||||
|
const unsigned char* sarray = (const unsigned char*) src;
|
||||||
|
|
||||||
|
// The reusable round buffer
|
||||||
|
unsigned int w[80];
|
||||||
|
|
||||||
|
// Loop through all complete 64byte blocks.
|
||||||
|
const int endOfFullBlocks = bytelength - 64;
|
||||||
|
int endCurrentBlock;
|
||||||
|
int currentBlock(0);
|
||||||
|
|
||||||
|
while (currentBlock <= endOfFullBlocks) {
|
||||||
|
endCurrentBlock = currentBlock + 64;
|
||||||
|
|
||||||
|
// Init the round buffer with the 64 byte block data.
|
||||||
|
for (int roundPos = 0; currentBlock < endCurrentBlock; currentBlock += 4)
|
||||||
|
{
|
||||||
|
// This line will swap endian on big endian and keep endian on little endian.
|
||||||
|
w[roundPos++] = (unsigned int) sarray[currentBlock + 3]
|
||||||
|
| (((unsigned int) sarray[currentBlock + 2]) << 8)
|
||||||
|
| (((unsigned int) sarray[currentBlock + 1]) << 16)
|
||||||
|
| (((unsigned int) sarray[currentBlock]) << 24);
|
||||||
|
}
|
||||||
|
innerHash(result, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the last and not full 64 byte block if existing.
|
||||||
|
endCurrentBlock = bytelength - currentBlock;
|
||||||
|
clearWBuffert(w);
|
||||||
|
int lastBlockBytes = 0;
|
||||||
|
for (; lastBlockBytes < endCurrentBlock; ++lastBlockBytes) {
|
||||||
|
w[lastBlockBytes >> 2] |= (unsigned int) sarray[lastBlockBytes
|
||||||
|
+ currentBlock] << ((3 - (lastBlockBytes & 3)) << 3);
|
||||||
|
}
|
||||||
|
w[lastBlockBytes >> 2] |= 0x80 << ((3 - (lastBlockBytes & 3)) << 3);
|
||||||
|
if (endCurrentBlock >= 56) {
|
||||||
|
innerHash(result, w);
|
||||||
|
clearWBuffert(w);
|
||||||
|
}
|
||||||
|
w[15] = bytelength << 3;
|
||||||
|
innerHash(result, w);
|
||||||
|
|
||||||
|
// Store hash in result pointer, and make sure we get in in the correct order on both endian models.
|
||||||
|
for (int hashByte = 20; --hashByte >= 0;) {
|
||||||
|
hash[hashByte] = (result[hashByte >> 2] >> (((3 - hashByte) & 0x3) << 3))
|
||||||
|
& 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline
|
||||||
|
void toHexString(const unsigned char* hash, char* hexstring) {
|
||||||
|
const char hexDigits[] = { "0123456789abcdef" };
|
||||||
|
|
||||||
|
for (int hashByte = 20; --hashByte >= 0;)
|
||||||
|
{
|
||||||
|
hexstring[hashByte << 1] = hexDigits[(hash[hashByte] >> 4) & 0xf];
|
||||||
|
hexstring[(hashByte << 1) + 1] = hexDigits[hash[hashByte] & 0xf];
|
||||||
|
}
|
||||||
|
hexstring[40] = 0;
|
||||||
|
}
|
||||||
|
} // namespace sha1
|
|
@ -0,0 +1,142 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 "soci.h"
|
||||||
|
#include "soci-postgresql.h"
|
||||||
|
|
||||||
|
using namespace epee;
|
||||||
|
namespace soci
|
||||||
|
{
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct type_conversion<boost::uint64_t>
|
||||||
|
{
|
||||||
|
typedef long long base_type;
|
||||||
|
|
||||||
|
static void from_base(base_type a_, indicator ind, boost::uint64_t & mi)
|
||||||
|
{
|
||||||
|
if (ind == i_null)
|
||||||
|
{
|
||||||
|
mi = 0;
|
||||||
|
//throw soci_error("Null value not allowed for this type");
|
||||||
|
}
|
||||||
|
mi = (boost::uint64_t)a_;
|
||||||
|
//mi.set(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void to_base(const boost::uint64_t & mi, base_type & i, indicator & ind)
|
||||||
|
{
|
||||||
|
i = (base_type)mi;
|
||||||
|
ind = i_ok;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct type_conversion<bool>
|
||||||
|
{
|
||||||
|
typedef int base_type;
|
||||||
|
|
||||||
|
static void from_base(base_type a_, indicator ind, bool& mi)
|
||||||
|
{
|
||||||
|
if (ind == i_null)
|
||||||
|
{
|
||||||
|
mi = false;
|
||||||
|
//throw soci_error("Null value not allowed for this type");
|
||||||
|
}
|
||||||
|
mi = a_? true:false;
|
||||||
|
//mi.set(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void to_base(const bool & mi, base_type & i, indicator & ind)
|
||||||
|
{
|
||||||
|
i = mi? 1:0;
|
||||||
|
ind = i_ok;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class per_thread_session
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool init(const std::string& connection_string)
|
||||||
|
{
|
||||||
|
m_connection_string = connection_string;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
soci::session& get()
|
||||||
|
{
|
||||||
|
|
||||||
|
//soci::session
|
||||||
|
|
||||||
|
m_db_connections_lock.lock();
|
||||||
|
boost::shared_ptr<soci::session>& conn_ptr = m_db_connections[epee::misc_utils::get_thread_string_id()];
|
||||||
|
m_db_connections_lock.unlock();
|
||||||
|
if(!conn_ptr.get())
|
||||||
|
{
|
||||||
|
conn_ptr.reset(new soci::session(soci::postgresql, m_connection_string));
|
||||||
|
}
|
||||||
|
//init new connection
|
||||||
|
return *conn_ptr.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reopen()
|
||||||
|
{
|
||||||
|
//soci::session
|
||||||
|
|
||||||
|
m_db_connections_lock.lock();
|
||||||
|
boost::shared_ptr<soci::session>& conn_ptr = m_db_connections[misc_utils::get_thread_string_id()];
|
||||||
|
m_db_connections_lock.unlock();
|
||||||
|
if(conn_ptr.get())
|
||||||
|
{
|
||||||
|
conn_ptr->close();
|
||||||
|
conn_ptr.reset(new soci::session(soci::postgresql, m_connection_string));
|
||||||
|
}
|
||||||
|
|
||||||
|
//init new connection
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------------------
|
||||||
|
bool check_status()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
private:
|
||||||
|
std::map<std::string, boost::shared_ptr<soci::session> > m_db_connections;
|
||||||
|
epee::critical_section m_db_connections_lock;
|
||||||
|
std::string m_connection_string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/*}*/
|
|
@ -0,0 +1,82 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _STATIC_INITIALIZER_H_
|
||||||
|
#define _STATIC_INITIALIZER_H_
|
||||||
|
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
/***********************************************************************
|
||||||
|
class initializer - useful to initialize some static classes
|
||||||
|
which have init() and un_init() static members
|
||||||
|
************************************************************************/
|
||||||
|
|
||||||
|
template<class to_initialize>
|
||||||
|
class initializer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
initializer()
|
||||||
|
{
|
||||||
|
to_initialize::init();
|
||||||
|
//get_set_is_initialized(true, true);
|
||||||
|
}
|
||||||
|
~initializer()
|
||||||
|
{
|
||||||
|
to_initialize::un_init();
|
||||||
|
//get_set_is_uninitialized(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*static inline bool is_initialized()
|
||||||
|
{
|
||||||
|
return get_set_is_initialized();
|
||||||
|
}
|
||||||
|
static inline bool is_uninitialized()
|
||||||
|
{
|
||||||
|
return get_set_is_uninitialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static inline bool get_set_is_initialized(bool need_to_set = false, bool val_to_set= false)
|
||||||
|
{
|
||||||
|
static bool val_is_initialized = false;
|
||||||
|
if(need_to_set)
|
||||||
|
val_is_initialized = val_to_set;
|
||||||
|
return val_is_initialized;
|
||||||
|
}
|
||||||
|
static inline bool get_set_is_uninitialized(bool need_to_set = false, bool val_to_set = false)
|
||||||
|
{
|
||||||
|
static bool val_is_uninitialized = false;
|
||||||
|
if(need_to_set)
|
||||||
|
val_is_uninitialized = val_to_set;
|
||||||
|
return val_is_uninitialized;
|
||||||
|
}*/
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif //_STATIC_INITIALIZER_H_
|
|
@ -0,0 +1,132 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 "inmemtoxml.h"
|
||||||
|
|
||||||
|
//#include "levin/levin_server.h"
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
|
||||||
|
class activity_printer_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
activity_printer_base(){}
|
||||||
|
virtual ~activity_printer_base(){}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class A>
|
||||||
|
class notify_activity_printer: public activity_printer_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
notify_activity_printer(int level, A& arg, bool is_notify_mode = true):m_ref_arg(arg), m_level(level), m_is_notify_mode(is_notify_mode)
|
||||||
|
{
|
||||||
|
m_command_name = typeid(m_ref_arg).name();
|
||||||
|
m_command_name.erase(0, 7);
|
||||||
|
m_command_name.erase(m_command_name.size()-10, m_command_name.size()-1);
|
||||||
|
if(level == log_space::get_set_log_detalisation_level())
|
||||||
|
{
|
||||||
|
LOG_PRINT(m_command_name, level);
|
||||||
|
}
|
||||||
|
else if(level+1 == log_space::get_set_log_detalisation_level())
|
||||||
|
{
|
||||||
|
LOG_PRINT(" -->>" << m_command_name, level);
|
||||||
|
}
|
||||||
|
else if(level+2 == log_space::get_set_log_detalisation_level())
|
||||||
|
{
|
||||||
|
LOG_PRINT(" -->>" << m_command_name << "\n" << StorageNamed::xml::get_t_as_xml(m_ref_arg), level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~notify_activity_printer()
|
||||||
|
{
|
||||||
|
if(m_is_notify_mode)
|
||||||
|
{
|
||||||
|
if(m_level+1 == log_space::get_set_log_detalisation_level())
|
||||||
|
{
|
||||||
|
LOG_PRINT(" <<--" << m_command_name, m_level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
std::string m_command_name;
|
||||||
|
A& m_ref_arg;
|
||||||
|
int m_level;
|
||||||
|
bool m_is_notify_mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class A, class R>
|
||||||
|
class command_activity_printer: public notify_activity_printer<A>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
command_activity_printer(int level, A& arg, R& rsp):notify_activity_printer(level, arg, false), m_ref_rsp(rsp)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~command_activity_printer()
|
||||||
|
{
|
||||||
|
if(m_level+1 == log_space::get_set_log_detalisation_level())
|
||||||
|
{
|
||||||
|
LOG_PRINT(" <<--" << m_command_name, m_level);
|
||||||
|
}
|
||||||
|
else if(m_level+2 == log_space::get_set_log_detalisation_level())
|
||||||
|
{
|
||||||
|
LOG_PRINT(" <<--" << m_command_name << "\n" << StorageNamed::trace_as_xml(m_ref_rsp), m_level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
R& m_ref_rsp;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class A, class R>
|
||||||
|
activity_printer_base* create_activity_printer(int level, A& arg, R& rsp)
|
||||||
|
{
|
||||||
|
return new command_activity_printer<A, R>(level, arg, rsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class A>
|
||||||
|
activity_printer_base* create_activity_printer(int level, A& arg)
|
||||||
|
{
|
||||||
|
return new notify_activity_printer<A>(level, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PRINT_COMMAND_ACTIVITY(level) boost::shared_ptr<activity_printer_base> local_activity_printer(create_activity_printer(level, in_struct, out_struct));
|
||||||
|
#define PRINT_NOTIFY_ACTIVITY(level) boost::shared_ptr<activity_printer_base> local_activity_printer(create_activity_printer(level, in_struct));
|
||||||
|
|
||||||
|
#define PRINT_ACTIVITY(level) \
|
||||||
|
{std::string some_str = typeid(in_struct).name(); \
|
||||||
|
some_str.erase(0, 7); \
|
||||||
|
some_str.erase(some_str.size()-10, some_str.size()-1); \
|
||||||
|
LOG_PRINT(some_str, level);}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _CRYPTED_STORAGE_H_
|
||||||
|
#define _CRYPTED_STORAGE_H_
|
||||||
|
|
||||||
|
#include "cryptopp_helper.h"
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
template<class t_base_storage, class crypt_provider, class t_key_provider>
|
||||||
|
class crypted_storage: public t_base_storage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
size_t PackToSolidBuffer(std::string& targetObj)
|
||||||
|
{
|
||||||
|
size_t res = t_base_storage::PackToSolidBuffer(targetObj);
|
||||||
|
if(res <= 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
if(!crypt_provider::encrypt(targetObj, t_key_provider::get_storage_default_key()))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return targetObj.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t LoadFromSolidBuffer(const std::string& pTargetObj)
|
||||||
|
{
|
||||||
|
std::string buff_to_decrypt = pTargetObj;
|
||||||
|
if(crypt_provider::decrypt(buff_to_decrypt, t_key_provider::get_storage_default_key()))
|
||||||
|
return t_base_storage::LoadFromSolidBuffer(buff_to_decrypt);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //_CRYPTED_STORAGE_H_
|
|
@ -0,0 +1,68 @@
|
||||||
|
// Copyright (c) 2006-2013, 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _GZIPPED_INMEMSTORAGE_H_
|
||||||
|
#define _GZIPPED_INMEMSTORAGE_H_
|
||||||
|
|
||||||
|
#include "zlib_helper.h"
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace StorageNamed
|
||||||
|
{
|
||||||
|
|
||||||
|
template<class t_base_storage>
|
||||||
|
class gziped_storage: public t_base_storage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
size_t PackToSolidBuffer(std::string& targetObj)
|
||||||
|
{
|
||||||
|
size_t res = t_base_storage::PackToSolidBuffer(targetObj);
|
||||||
|
if(res <= 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
if(!zlib_helper::pack(targetObj))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return targetObj.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t LoadFromSolidBuffer(const std::string& pTargetObj)
|
||||||
|
{
|
||||||
|
std::string buff_to_ungzip = pTargetObj;
|
||||||
|
if(zlib_helper::unpack(buff_to_ungzip))
|
||||||
|
return t_base_storage::LoadFromSolidBuffer(buff_to_ungzip);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,126 @@
|
||||||
|
|
||||||
|
// Copyright (c) 2006-2013, 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 "portable_storage_template_helper.h"
|
||||||
|
#include "net/http_base.h"
|
||||||
|
#include "net/http_server_handlers_map2.h"
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
template<class t_request, class t_response, class t_transport>
|
||||||
|
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")
|
||||||
|
{
|
||||||
|
std::string req_param;
|
||||||
|
if(!serialization::store_t_to_json(out_struct, req_param))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const http::http_response_info* pri = NULL;
|
||||||
|
if(!invoke_request(url, transport, timeout, &pri, method, req_param))
|
||||||
|
{
|
||||||
|
LOG_PRINT_L1("Failed to invoke http request to " << url);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!pri->m_response_code)
|
||||||
|
{
|
||||||
|
LOG_PRINT_L1("Failed to invoke http request to " << url << ", internal error (null response ptr)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pri->m_response_code != 200)
|
||||||
|
{
|
||||||
|
LOG_PRINT_L1("Failed to invoke http request to " << url << ", wrong response code: " << pri->m_response_code);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return serialization::load_t_from_json(result_struct, pri->m_body);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template<class t_request, class t_response, class t_transport>
|
||||||
|
bool invoke_http_bin_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")
|
||||||
|
{
|
||||||
|
std::string req_param;
|
||||||
|
if(!serialization::store_t_to_binary(out_struct, req_param))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const http::http_response_info* pri = NULL;
|
||||||
|
if(!invoke_request(url, transport, timeout, &pri, method, req_param))
|
||||||
|
{
|
||||||
|
LOG_PRINT_L1("Failed to invoke http request to " << url);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!pri->m_response_code)
|
||||||
|
{
|
||||||
|
LOG_PRINT_L1("Failed to invoke http request to " << url << ", internal error (null response ptr)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pri->m_response_code != 200)
|
||||||
|
{
|
||||||
|
LOG_PRINT_L1("Failed to invoke http request to " << url << ", wrong response code: " << pri->m_response_code);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return serialization::load_t_from_binary(result_struct, pri->m_body);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class t_request, class t_response, class t_transport>
|
||||||
|
bool invoke_http_json_rpc(const std::string& url, const std::string& method_name, t_request& out_struct, t_response& result_struct, t_transport& transport, unsigned int timeout = 5000, const std::string& http_method = "GET", const std::string& req_id = "0")
|
||||||
|
{
|
||||||
|
epee::json_rpc::request<t_request> req_t = AUTO_VAL_INIT(req_t);
|
||||||
|
req_t.params = out_struct;
|
||||||
|
req_t.id = req_id;
|
||||||
|
req_t.method = method_name;
|
||||||
|
req_t.version = "2.0";
|
||||||
|
epee::json_rpc::response<t_response, epee::json_rpc::error> resp_t = AUTO_VAL_INIT(resp_t);
|
||||||
|
if(!epee::net_utils::invoke_http_json_remote_command2(url, req_t, resp_t, transport, timeout, http_method))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(resp_t.error.code || resp_t.error.message.size())
|
||||||
|
{
|
||||||
|
LOG_ERROR("RPC call of \"" << method_name << "\" returned error: " << resp_t.error.code << ", message: " << resp_t.error.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
result_struct = resp_t.result;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class t_command, class t_transport>
|
||||||
|
bool invoke_http_json_rpc(const std::string& url, typename t_command::request& out_struct, typename t_command::response& result_struct, t_transport& transport, unsigned int timeout = 5000, const std::string& http_method = "GET", const std::string& req_id = "0")
|
||||||
|
{
|
||||||
|
return invoke_http_json_rpc(url, t_command::methodname(), out_struct, result_struct, transport, timeout, http_method, req_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,289 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 "portable_storage_template_helper.h"
|
||||||
|
#include <boost/utility/value_init.hpp>
|
||||||
|
#include "net/levin_base.h"
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace net_utils
|
||||||
|
{
|
||||||
|
template<class t_arg, class t_result, class t_transport>
|
||||||
|
bool invoke_remote_command2(int command, const t_arg& out_struct, t_result& result_struct, t_transport& transport)
|
||||||
|
{
|
||||||
|
if(!transport.is_connected())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
serialization::portable_storage stg;
|
||||||
|
out_struct.store(stg);
|
||||||
|
std::string buff_to_send, buff_to_recv;
|
||||||
|
stg.store_to_binary(buff_to_send);
|
||||||
|
|
||||||
|
int res = transport.invoke(command, buff_to_send, buff_to_recv);
|
||||||
|
if( res <=0 )
|
||||||
|
{
|
||||||
|
LOG_PRINT_RED("Failed to invoke command " << command << " return code " << res, LOG_LEVEL_1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
serialization::portable_storage stg_ret;
|
||||||
|
if(!stg_ret.load_from_binary(buff_to_recv))
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to load_from_binary on command " << command);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
result_struct.load(stg_ret);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class t_arg, class t_transport>
|
||||||
|
bool notify_remote_command2(int command, const t_arg& out_struct, t_transport& transport)
|
||||||
|
{
|
||||||
|
if(!transport.is_connected())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
serialization::portable_storage stg;
|
||||||
|
out_struct.store(&stg);
|
||||||
|
std::string buff_to_send;
|
||||||
|
stg.store_to_binary(buff_to_send);
|
||||||
|
|
||||||
|
int res = transport.notify(command, buff_to_send);
|
||||||
|
if(res <=0 )
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to notify command " << command << " return code " << res);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class t_arg, class t_result, class t_transport>
|
||||||
|
bool invoke_remote_command2(boost::uuids::uuid conn_id, int command, const t_arg& out_struct, t_result& result_struct, t_transport& transport)
|
||||||
|
{
|
||||||
|
|
||||||
|
typename serialization::portable_storage stg;
|
||||||
|
out_struct.store(stg);
|
||||||
|
std::string buff_to_send, buff_to_recv;
|
||||||
|
stg.store_to_binary(buff_to_send);
|
||||||
|
|
||||||
|
int res = transport.invoke(command, buff_to_send, buff_to_recv, conn_id);
|
||||||
|
if( res <=0 )
|
||||||
|
{
|
||||||
|
LOG_PRINT_L1("Failed to invoke command " << command << " return code " << res);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
typename serialization::portable_storage stg_ret;
|
||||||
|
if(!stg_ret.load_from_binary(buff_to_recv))
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to load_from_binary on command " << command);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
result_struct.load(stg_ret);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class t_result, class t_arg, class callback_t, class t_transport>
|
||||||
|
bool async_invoke_remote_command2(boost::uuids::uuid conn_id, int command, const t_arg& out_struct, t_transport& transport, callback_t cb, size_t inv_timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED)
|
||||||
|
{
|
||||||
|
typename serialization::portable_storage stg;
|
||||||
|
const_cast<t_arg&>(out_struct).store(stg);//TODO: add true const support to searilzation
|
||||||
|
std::string buff_to_send, buff_to_recv;
|
||||||
|
stg.store_to_binary(buff_to_send);
|
||||||
|
int res = transport.invoke_async(command, buff_to_send, conn_id, [cb, command](int code, const std::string& buff, typename t_transport::connection_context& context)->bool
|
||||||
|
{
|
||||||
|
t_result result_struct = AUTO_VAL_INIT(result_struct);
|
||||||
|
if( code <=0 )
|
||||||
|
{
|
||||||
|
LOG_PRINT_L1("Failed to invoke command " << command << " return code " << code);
|
||||||
|
cb(code, result_struct, context);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
serialization::portable_storage stg_ret;
|
||||||
|
if(!stg_ret.load_from_binary(buff))
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to load_from_binary on command " << command);
|
||||||
|
cb(LEVIN_ERROR_FORMAT, result_struct, context);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
result_struct.load(stg_ret);
|
||||||
|
cb(code, result_struct, context);
|
||||||
|
return true;
|
||||||
|
}, inv_timeout);
|
||||||
|
if( res <=0 )
|
||||||
|
{
|
||||||
|
LOG_PRINT_L1("Failed to invoke command " << command << " return code " << res);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class t_arg, class t_transport>
|
||||||
|
bool notify_remote_command2(boost::uuids::uuid conn_id, int command, const t_arg& out_struct, t_transport& transport)
|
||||||
|
{
|
||||||
|
|
||||||
|
serialization::portable_storage stg;
|
||||||
|
out_struct.store(stg);
|
||||||
|
std::string buff_to_send, buff_to_recv;
|
||||||
|
stg.store_to_binary(buff_to_send);
|
||||||
|
|
||||||
|
int res = transport.notify(command, buff_to_send, conn_id);
|
||||||
|
if(res <=0 )
|
||||||
|
{
|
||||||
|
LOG_PRINT_RED_L0("Failed to notify command " << command << " return code " << res);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
template<class t_owner, class t_in_type, class t_out_type, class t_context, class callback_t>
|
||||||
|
int buff_to_t_adapter(int command, const std::string& in_buff, std::string& buff_out, callback_t cb, t_context& context )
|
||||||
|
{
|
||||||
|
serialization::portable_storage strg;
|
||||||
|
if(!strg.load_from_binary(in_buff))
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to load_from_binary in command " << command);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
boost::value_initialized<t_in_type> in_struct;
|
||||||
|
boost::value_initialized<t_out_type> out_struct;
|
||||||
|
|
||||||
|
static_cast<t_in_type&>(in_struct).load(strg);
|
||||||
|
int res = cb(command, static_cast<t_in_type&>(in_struct), static_cast<t_out_type&>(out_struct), context);
|
||||||
|
serialization::portable_storage strg_out;
|
||||||
|
static_cast<t_out_type&>(out_struct).store(strg_out);
|
||||||
|
|
||||||
|
if(!strg_out.store_to_binary(buff_out))
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to store_to_binary in command" << command);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class t_owner, class t_in_type, class t_context, class callback_t>
|
||||||
|
int buff_to_t_adapter(t_owner* powner, int command, const std::string& in_buff, callback_t cb, t_context& context)
|
||||||
|
{
|
||||||
|
serialization::portable_storage strg;
|
||||||
|
if(!strg.load_from_binary(in_buff))
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to load_from_binary in notify " << command);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
boost::value_initialized<t_in_type> in_struct;
|
||||||
|
static_cast<t_in_type&>(in_struct).load(strg);
|
||||||
|
return cb(command, in_struct, context);
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CHAIN_LEVIN_INVOKE_MAP2(context_type) \
|
||||||
|
int invoke(int command, const std::string& in_buff, std::string& buff_out, context_type& context) \
|
||||||
|
{ \
|
||||||
|
bool handled = false; \
|
||||||
|
return handle_invoke_map(false, command, in_buff, buff_out, context, handled); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CHAIN_LEVIN_NOTIFY_MAP2(context_type) \
|
||||||
|
int notify(int command, const std::string& in_buff, context_type& context) \
|
||||||
|
{ \
|
||||||
|
bool handled = false; std::string fake_str;\
|
||||||
|
return handle_invoke_map(true, command, in_buff, fake_str, context, handled); \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define CHAIN_LEVIN_INVOKE_MAP() \
|
||||||
|
int invoke(int command, const std::string& in_buff, std::string& buff_out, epee::net_utils::connection_context_base& context) \
|
||||||
|
{ \
|
||||||
|
bool handled = false; \
|
||||||
|
return handle_invoke_map(false, command, in_buff, buff_out, context, handled); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CHAIN_LEVIN_NOTIFY_MAP() \
|
||||||
|
int notify(int command, const std::string& in_buff, epee::net_utils::connection_context_base& context) \
|
||||||
|
{ \
|
||||||
|
bool handled = false; std::string fake_str;\
|
||||||
|
return handle_invoke_map(true, command, in_buff, fake_str, context, handled); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CHAIN_LEVIN_NOTIFY_STUB() \
|
||||||
|
int notify(int command, const std::string& in_buff, epee::net_utils::connection_context_base& context) \
|
||||||
|
{ \
|
||||||
|
return -1; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BEGIN_INVOKE_MAP2(owner_type) \
|
||||||
|
template <class t_context> int handle_invoke_map(bool is_notify, int command, const std::string& in_buff, std::string& buff_out, t_context& context, bool& handled) \
|
||||||
|
{ \
|
||||||
|
typedef owner_type internal_owner_type_name;
|
||||||
|
|
||||||
|
#define HANDLE_INVOKE2(command_id, func, type_name_in, typename_out) \
|
||||||
|
if(!is_notify && command_id == command) \
|
||||||
|
{handled=true;return epee::net_utils::buff_to_t_adapter<internal_owner_type_name, type_name_in, typename_out>(this, command, in_buff, buff_out, boost::bind(func, this, _1, _2, _3, _4), context);}
|
||||||
|
|
||||||
|
#define HANDLE_INVOKE_T2(COMMAND, func) \
|
||||||
|
if(!is_notify && COMMAND::ID == command) \
|
||||||
|
{handled=true;return epee::net_utils::buff_to_t_adapter<internal_owner_type_name, typename COMMAND::request, typename COMMAND::response>(command, in_buff, buff_out, boost::bind(func, this, _1, _2, _3, _4), context);}
|
||||||
|
|
||||||
|
|
||||||
|
#define HANDLE_NOTIFY2(command_id, func, type_name_in) \
|
||||||
|
if(is_notify && command_id == command) \
|
||||||
|
{handled=true;return epee::net_utils::buff_to_t_adapter<internal_owner_type_name, type_name_in>(this, command, in_buff, boost::bind(func, this, _1, _2, _3), context);}
|
||||||
|
|
||||||
|
#define HANDLE_NOTIFY_T2(NOTIFY, func) \
|
||||||
|
if(is_notify && NOTIFY::ID == command) \
|
||||||
|
{handled=true;return epee::net_utils::buff_to_t_adapter<internal_owner_type_name, typename NOTIFY::request>(this, command, in_buff, boost::bind(func, this, _1, _2, _3), context);}
|
||||||
|
|
||||||
|
|
||||||
|
#define CHAIN_INVOKE_MAP2(func) \
|
||||||
|
{ \
|
||||||
|
int res = func(is_notify, command, in_buff, buff_out, context, handled); \
|
||||||
|
if(handled) \
|
||||||
|
return res; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CHAIN_INVOKE_MAP_TO_OBJ2(obj) \
|
||||||
|
{ \
|
||||||
|
int res = obj.handle_invoke_map(is_notify, command, in_buff, buff_out, context, handled); \
|
||||||
|
if(handled) \
|
||||||
|
return res; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CHAIN_INVOKE_MAP_TO_OBJ_FORCE_CONTEXT(obj, context_type) \
|
||||||
|
{ \
|
||||||
|
int res = obj.handle_invoke_map(is_notify, command, in_buff, buff_out, static_cast<context_type>(context), handled); \
|
||||||
|
if(handled) return res; \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define END_INVOKE_MAP2() \
|
||||||
|
LOG_ERROR("Unkonown command:" << command); \
|
||||||
|
return LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED; \
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,260 @@
|
||||||
|
// Copyright (c) 2006-2013, 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
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace misc_utils
|
||||||
|
{
|
||||||
|
namespace parse
|
||||||
|
{
|
||||||
|
inline std::string transform_to_escape_sequence(const std::string& src)
|
||||||
|
{
|
||||||
|
//std::stringstream res;
|
||||||
|
std::string res;
|
||||||
|
for(std::string::const_iterator it = src.begin(); it!=src.end(); ++it)
|
||||||
|
{
|
||||||
|
switch(*it)
|
||||||
|
{
|
||||||
|
case '\b': //Backspace (ascii code 08)
|
||||||
|
res+="\\b"; break;
|
||||||
|
case '\f': //Form feed (ascii code 0C)
|
||||||
|
res+="\\f"; break;
|
||||||
|
case '\n': //New line
|
||||||
|
res+="\\n"; break;
|
||||||
|
case '\r': //Carriage return
|
||||||
|
res+="\\r"; break;
|
||||||
|
case '\t': //Tab
|
||||||
|
res+="\\t"; break;
|
||||||
|
case '\v': //Vertical tab
|
||||||
|
res+="\\v"; break;
|
||||||
|
//case '\'': //Apostrophe or single quote
|
||||||
|
// res+="\\'"; break;
|
||||||
|
case '"': //Double quote
|
||||||
|
res+="\\\""; break;
|
||||||
|
case '\\': //Backslash caracter
|
||||||
|
res+="\\\\"; break;
|
||||||
|
case '/': //Backslash caracter
|
||||||
|
res+="\\/"; break;
|
||||||
|
default:
|
||||||
|
res.push_back(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
|
||||||
|
\b Backspace (ascii code 08)
|
||||||
|
\f Form feed (ascii code 0C)
|
||||||
|
\n New line
|
||||||
|
\r Carriage return
|
||||||
|
\t Tab
|
||||||
|
\v Vertical tab
|
||||||
|
\' Apostrophe or single quote
|
||||||
|
\" Double quote
|
||||||
|
\\ Backslash character
|
||||||
|
|
||||||
|
*/
|
||||||
|
inline void match_string2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val)
|
||||||
|
{
|
||||||
|
val.clear();
|
||||||
|
bool escape_mode = false;
|
||||||
|
std::string::const_iterator it = star_end_string;
|
||||||
|
++it;
|
||||||
|
for(;it != buf_end;it++)
|
||||||
|
{
|
||||||
|
if(escape_mode/*prev_ch == '\\'*/)
|
||||||
|
{
|
||||||
|
switch(*it)
|
||||||
|
{
|
||||||
|
case 'b': //Backspace (ascii code 08)
|
||||||
|
val.push_back(0x08);break;
|
||||||
|
case 'f': //Form feed (ascii code 0C)
|
||||||
|
val.push_back(0x0C);break;
|
||||||
|
case 'n': //New line
|
||||||
|
val.push_back('\n');break;
|
||||||
|
case 'r': //Carriage return
|
||||||
|
val.push_back('\r');break;
|
||||||
|
case 't': //Tab
|
||||||
|
val.push_back('\t');break;
|
||||||
|
case 'v': //Vertical tab
|
||||||
|
val.push_back('\v');break;
|
||||||
|
case '\'': //Apostrophe or single quote
|
||||||
|
val.push_back('\'');break;
|
||||||
|
case '"': //Double quote
|
||||||
|
val.push_back('"');break;
|
||||||
|
case '\\': //Backslash character
|
||||||
|
val.push_back('\\');break;
|
||||||
|
case '/': //Slash character
|
||||||
|
val.push_back('/');break;
|
||||||
|
default:
|
||||||
|
val.push_back(*it);
|
||||||
|
LOG_PRINT_L0("Unknown escape sequence :\"\\" << *it << "\"");
|
||||||
|
}
|
||||||
|
escape_mode = false;
|
||||||
|
}else if(*it == '"')
|
||||||
|
{
|
||||||
|
star_end_string = it;
|
||||||
|
return;
|
||||||
|
}else if(*it == '\\')
|
||||||
|
{
|
||||||
|
escape_mode = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
val.push_back(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ASSERT_MES_AND_THROW("Failed to match string in json entry: " << std::string(star_end_string, buf_end));
|
||||||
|
}
|
||||||
|
inline bool match_string(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
match_string2(star_end_string, buf_end, val);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline void match_number2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val, bool& is_float_val, bool& is_signed_val)
|
||||||
|
{
|
||||||
|
val.clear();
|
||||||
|
is_float_val = false;
|
||||||
|
for(std::string::const_iterator it = star_end_string;it != buf_end;it++)
|
||||||
|
{
|
||||||
|
if(isdigit(*it) || (it == star_end_string && *it == '-') || (val.size() && *it == '.' ) || (is_float_val && (*it == 'e' || *it == 'E' || *it == '-' || *it == '+' )) )
|
||||||
|
{
|
||||||
|
if(!val.size() && *it == '-')
|
||||||
|
is_signed_val = true;
|
||||||
|
if(*it == '.' )
|
||||||
|
is_float_val = true;
|
||||||
|
val.push_back(*it);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(val.size())
|
||||||
|
{
|
||||||
|
star_end_string = --it;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ASSERT_MES_AND_THROW("wrong number in json entry: " << std::string(star_end_string, buf_end));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ASSERT_MES_AND_THROW("wrong number in json entry: " << std::string(star_end_string, buf_end));
|
||||||
|
}
|
||||||
|
inline bool match_number(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bool is_v_float = false;bool is_signed_val = false;
|
||||||
|
match_number2(star_end_string, buf_end, val, is_v_float, is_signed_val);
|
||||||
|
return !is_v_float;
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline void match_word2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val)
|
||||||
|
{
|
||||||
|
val.clear();
|
||||||
|
|
||||||
|
for(std::string::const_iterator it = star_end_string;it != buf_end;it++)
|
||||||
|
{
|
||||||
|
if(!isalpha(*it))
|
||||||
|
{
|
||||||
|
val.assign(star_end_string, it);
|
||||||
|
if(val.size())
|
||||||
|
{
|
||||||
|
star_end_string = --it;
|
||||||
|
return;
|
||||||
|
}else
|
||||||
|
ASSERT_MES_AND_THROW("failed to match word number in json entry: " << std::string(star_end_string, buf_end));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ASSERT_MES_AND_THROW("failed to match word number in json entry: " << std::string(star_end_string, buf_end));
|
||||||
|
}
|
||||||
|
inline bool match_word(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
match_word2(star_end_string, buf_end, val);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline bool match_word_with_extrasymb(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val)
|
||||||
|
{
|
||||||
|
val.clear();
|
||||||
|
|
||||||
|
for(std::string::const_iterator it = star_end_string;it != buf_end;it++)
|
||||||
|
{
|
||||||
|
if(!isalnum(*it) && *it != '-' && *it != '_')
|
||||||
|
{
|
||||||
|
val.assign(star_end_string, it);
|
||||||
|
if(val.size())
|
||||||
|
{
|
||||||
|
star_end_string = --it;
|
||||||
|
return true;
|
||||||
|
}else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
inline bool match_word_til_equal_mark(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string::const_iterator& word_end)
|
||||||
|
{
|
||||||
|
word_end = star_end_string;
|
||||||
|
|
||||||
|
for(std::string::const_iterator it = star_end_string;it != buf_end;it++)
|
||||||
|
{
|
||||||
|
if(isspace(*it))
|
||||||
|
{
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}else if( *it == '=' )
|
||||||
|
{
|
||||||
|
star_end_string = it;
|
||||||
|
word_end = it;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,463 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 "misc_language.h"
|
||||||
|
#include "portable_storage_base.h"
|
||||||
|
#include "portable_storage_to_bin.h"
|
||||||
|
#include "portable_storage_from_bin.h"
|
||||||
|
#include "portable_storage_to_json.h"
|
||||||
|
#include "portable_storage_from_json.h"
|
||||||
|
#include "portable_storage_val_converters.h"
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace serialization
|
||||||
|
{
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
class portable_storage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef epee::serialization::hsection hsection;
|
||||||
|
typedef epee::serialization::harray harray;
|
||||||
|
|
||||||
|
portable_storage(){}
|
||||||
|
virtual ~portable_storage(){}
|
||||||
|
hsection open_section(const std::string& section_name, hsection hparent_section, bool create_if_notexist = false);
|
||||||
|
template<class t_value>
|
||||||
|
bool get_value(const std::string& value_name, t_value& val, hsection hparent_section);
|
||||||
|
template<class t_value>
|
||||||
|
bool set_value(const std::string& value_name, const t_value& target, hsection hparent_section);
|
||||||
|
|
||||||
|
//serial access for arrays of values --------------------------------------
|
||||||
|
//values
|
||||||
|
template<class t_value>
|
||||||
|
harray get_first_value(const std::string& value_name, t_value& target, hsection hparent_section);
|
||||||
|
template<class t_value>
|
||||||
|
bool get_next_value(harray hval_array, t_value& target);
|
||||||
|
template<class t_value>
|
||||||
|
harray insert_first_value(const std::string& value_name, const t_value& target, hsection hparent_section);
|
||||||
|
template<class t_value>
|
||||||
|
bool insert_next_value(harray hval_array, const t_value& target);
|
||||||
|
//sections
|
||||||
|
harray get_first_section(const std::string& pSectionName, hsection& h_child_section, hsection hparent_section);
|
||||||
|
bool get_next_section(harray hSecArray, hsection& h_child_section);
|
||||||
|
harray insert_first_section(const std::string& pSectionName, hsection& hinserted_childsection, hsection hparent_section);
|
||||||
|
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 store_to_binary(binarybuffer& target);
|
||||||
|
bool load_from_binary(const binarybuffer& target);
|
||||||
|
template<class trace_policy>
|
||||||
|
bool dump_as_xml(std::string& targetObj, const std::string& root_name = "");
|
||||||
|
bool dump_as_json(std::string& targetObj, size_t indent = 0);
|
||||||
|
bool load_from_json(const std::string& source);
|
||||||
|
|
||||||
|
private:
|
||||||
|
section m_root;
|
||||||
|
hsection get_root_section() {return &m_root;}
|
||||||
|
storage_entry* find_storage_entry(const std::string& pentry_name, hsection psection);
|
||||||
|
template<class entry_type>
|
||||||
|
storage_entry* insert_new_entry_get_storage_entry(const std::string& pentry_name, hsection psection, const entry_type& entry);
|
||||||
|
|
||||||
|
hsection insert_new_section(const std::string& pentry_name, hsection psection);
|
||||||
|
|
||||||
|
#pragma pack(push)
|
||||||
|
#pragma pack(1)
|
||||||
|
struct storage_block_header
|
||||||
|
{
|
||||||
|
uint32_t m_signature_a;
|
||||||
|
uint32_t m_signature_b;
|
||||||
|
uint8_t m_ver;
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
};
|
||||||
|
inline
|
||||||
|
bool portable_storage::dump_as_json(std::string& buff, size_t indent)
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
std::stringstream ss;
|
||||||
|
epee::serialization::dump_as_json(ss, m_root, indent);
|
||||||
|
buff = ss.str();
|
||||||
|
return true;
|
||||||
|
CATCH_ENTRY("portable_storage::dump_as_json", false)
|
||||||
|
}
|
||||||
|
inline
|
||||||
|
bool portable_storage::load_from_json(const std::string& source)
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
return json::load_from_json(source, *this);
|
||||||
|
CATCH_ENTRY("portable_storage::load_from_json", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class trace_policy>
|
||||||
|
bool portable_storage::dump_as_xml(std::string& targetObj, const std::string& root_name)
|
||||||
|
{
|
||||||
|
return false;//TODO: don't think i ever again will use xml - ambiguous and "overtagged" format
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool portable_storage::store_to_binary(binarybuffer& target)
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
std::stringstream ss;
|
||||||
|
storage_block_header sbh = AUTO_VAL_INIT(sbh);
|
||||||
|
sbh.m_signature_a = PORTABLE_STORAGE_SIGNATUREA;
|
||||||
|
sbh.m_signature_b = PORTABLE_STORAGE_SIGNATUREB;
|
||||||
|
sbh.m_ver = PORTABLE_STORAGE_FORMAT_VER;
|
||||||
|
ss.write((const char*)&sbh, sizeof(storage_block_header));
|
||||||
|
pack_entry_to_buff(ss, m_root);
|
||||||
|
target = ss.str();
|
||||||
|
return true;
|
||||||
|
CATCH_ENTRY("portable_storage::store_to_binary", false)
|
||||||
|
}
|
||||||
|
inline
|
||||||
|
bool portable_storage::load_from_binary(const binarybuffer& source)
|
||||||
|
{
|
||||||
|
m_root.m_entries.clear();
|
||||||
|
if(source.size() < sizeof(storage_block_header))
|
||||||
|
{
|
||||||
|
LOG_ERROR("portable_storage: wrong binary format, packet size = " << source.size() << " less than expected sizeof(storage_block_header)=" << sizeof(storage_block_header));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
storage_block_header* pbuff = (storage_block_header*)source.data();
|
||||||
|
if(pbuff->m_signature_a != PORTABLE_STORAGE_SIGNATUREA ||
|
||||||
|
pbuff->m_signature_b != PORTABLE_STORAGE_SIGNATUREB
|
||||||
|
)
|
||||||
|
{
|
||||||
|
LOG_ERROR("portable_storage: wrong binary format - signature missmatch");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(pbuff->m_ver != PORTABLE_STORAGE_FORMAT_VER)
|
||||||
|
{
|
||||||
|
LOG_ERROR("portable_storage: wrong binary format - unknown format ver = " << pbuff->m_ver);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
TRY_ENTRY();
|
||||||
|
throwable_buffer_reader buf_reader(source.data()+sizeof(storage_block_header), source.size()-sizeof(storage_block_header));
|
||||||
|
buf_reader.read(m_root);
|
||||||
|
return true;//TODO:
|
||||||
|
CATCH_ENTRY("portable_storage::load_from_binary", false);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
hsection portable_storage::open_section(const std::string& section_name, hsection hparent_section, bool create_if_notexist)
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
hparent_section = hparent_section ? hparent_section:&m_root;
|
||||||
|
storage_entry* pentry = find_storage_entry(section_name, hparent_section);
|
||||||
|
if(!pentry)
|
||||||
|
{
|
||||||
|
if(!create_if_notexist)
|
||||||
|
return nullptr;
|
||||||
|
return insert_new_section(section_name, hparent_section);
|
||||||
|
}
|
||||||
|
CHECK_AND_ASSERT(pentry , nullptr);
|
||||||
|
//check that section_entry we find is real "CSSection"
|
||||||
|
if(pentry->type() != typeid(section))
|
||||||
|
{
|
||||||
|
if(create_if_notexist)
|
||||||
|
*pentry = storage_entry(section());//replace
|
||||||
|
else
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return &boost::get<section>(*pentry);
|
||||||
|
CATCH_ENTRY("portable_storage::open_section", nullptr);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class to_type>
|
||||||
|
struct get_value_visitor: boost::static_visitor<void>
|
||||||
|
{
|
||||||
|
to_type& m_target;
|
||||||
|
get_value_visitor(to_type& target):m_target(target){}
|
||||||
|
template<class from_type>
|
||||||
|
void operator()(const from_type& v){convert_t(v, m_target);}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class t_value>
|
||||||
|
bool portable_storage::get_value(const std::string& value_name, t_value& val, hsection hparent_section)
|
||||||
|
{
|
||||||
|
BOOST_MPL_ASSERT(( boost::mpl::contains<storage_entry::types, t_value> ));
|
||||||
|
//TRY_ENTRY();
|
||||||
|
if(!hparent_section) hparent_section = &m_root;
|
||||||
|
storage_entry* pentry = find_storage_entry(value_name, hparent_section);
|
||||||
|
if(!pentry)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
get_value_visitor<t_value> gvv(val);
|
||||||
|
boost::apply_visitor(gvv, *pentry);
|
||||||
|
return true;
|
||||||
|
//CATCH_ENTRY("portable_storage::template<>get_value", false);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class t_value>
|
||||||
|
bool portable_storage::set_value(const std::string& value_name, const t_value& v, hsection hparent_section)
|
||||||
|
{
|
||||||
|
BOOST_MPL_ASSERT(( boost::mpl::contains<storage_entry::types, t_value> ));
|
||||||
|
TRY_ENTRY();
|
||||||
|
if(!hparent_section)
|
||||||
|
hparent_section = &m_root;
|
||||||
|
storage_entry* pentry = find_storage_entry(value_name, hparent_section);
|
||||||
|
if(!pentry)
|
||||||
|
{
|
||||||
|
pentry = insert_new_entry_get_storage_entry(value_name, hparent_section, v);
|
||||||
|
if(!pentry)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
*pentry = storage_entry(v);
|
||||||
|
return true;
|
||||||
|
CATCH_ENTRY("portable_storage::template<>set_value", false);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
storage_entry* portable_storage::find_storage_entry(const std::string& pentry_name, hsection psection)
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
CHECK_AND_ASSERT(psection, nullptr);
|
||||||
|
auto it = psection->m_entries.find(pentry_name);
|
||||||
|
if(it == psection->m_entries.end())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return &it->second;
|
||||||
|
CATCH_ENTRY("portable_storage::find_storage_entry", nullptr);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class entry_type>
|
||||||
|
storage_entry* portable_storage::insert_new_entry_get_storage_entry(const std::string& pentry_name, hsection psection, const entry_type& entry)
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
CHECK_AND_ASSERT(psection, nullptr);
|
||||||
|
auto ins_res = psection->m_entries.insert(std::pair<std::string, storage_entry>(pentry_name, entry));
|
||||||
|
return &ins_res.first->second;
|
||||||
|
CATCH_ENTRY("portable_storage::insert_new_entry_get_storage_entry", nullptr);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
hsection portable_storage::insert_new_section(const std::string& pentry_name, hsection psection)
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
storage_entry* pse = insert_new_entry_get_storage_entry(pentry_name, psection, section());
|
||||||
|
if(!pse) return nullptr;
|
||||||
|
return &boost::get<section>(*pse);
|
||||||
|
CATCH_ENTRY("portable_storage::insert_new_section", nullptr);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class to_type>
|
||||||
|
struct get_first_value_visitor: boost::static_visitor<bool>
|
||||||
|
{
|
||||||
|
to_type& m_target;
|
||||||
|
get_first_value_visitor(to_type& target):m_target(target){}
|
||||||
|
template<class from_type>
|
||||||
|
bool operator()(const array_entry_t<from_type>& a)
|
||||||
|
{
|
||||||
|
const from_type* pv = a.get_first_val();
|
||||||
|
if(!pv)
|
||||||
|
return false;
|
||||||
|
convert_t(*pv, m_target);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//---------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class t_value>
|
||||||
|
harray portable_storage::get_first_value(const std::string& value_name, t_value& target, hsection hparent_section)
|
||||||
|
{
|
||||||
|
BOOST_MPL_ASSERT(( boost::mpl::contains<storage_entry::types, t_value> ));
|
||||||
|
//TRY_ENTRY();
|
||||||
|
if(!hparent_section) hparent_section = &m_root;
|
||||||
|
storage_entry* pentry = find_storage_entry(value_name, hparent_section);
|
||||||
|
if(!pentry)
|
||||||
|
return nullptr;
|
||||||
|
if(pentry->type() != typeid(array_entry))
|
||||||
|
return nullptr;
|
||||||
|
array_entry& ar_entry = boost::get<array_entry>(*pentry);
|
||||||
|
|
||||||
|
get_first_value_visitor<t_value> gfv(target);
|
||||||
|
if(!boost::apply_visitor(gfv, ar_entry))
|
||||||
|
return nullptr;
|
||||||
|
return &ar_entry;
|
||||||
|
//CATCH_ENTRY("portable_storage::get_first_value", nullptr);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class to_type>
|
||||||
|
struct get_next_value_visitor: boost::static_visitor<bool>
|
||||||
|
{
|
||||||
|
to_type& m_target;
|
||||||
|
get_next_value_visitor(to_type& target):m_target(target){}
|
||||||
|
template<class from_type>
|
||||||
|
bool operator()(const array_entry_t<from_type>& a)
|
||||||
|
{
|
||||||
|
//TODO: optimize code here: work without get_next_val function
|
||||||
|
const from_type* pv = a.get_next_val();
|
||||||
|
if(!pv)
|
||||||
|
return false;
|
||||||
|
convert_t(*pv, m_target);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template<class t_value>
|
||||||
|
bool portable_storage::get_next_value(harray hval_array, t_value& target)
|
||||||
|
{
|
||||||
|
BOOST_MPL_ASSERT(( boost::mpl::contains<storage_entry::types, t_value> ));
|
||||||
|
//TRY_ENTRY();
|
||||||
|
CHECK_AND_ASSERT(hval_array, false);
|
||||||
|
array_entry& ar_entry = *hval_array;
|
||||||
|
get_next_value_visitor<t_value> gnv(target);
|
||||||
|
if(!boost::apply_visitor(gnv, ar_entry))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
//CATCH_ENTRY("portable_storage::get_next_value", false);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class t_value>
|
||||||
|
harray portable_storage::insert_first_value(const std::string& value_name, const t_value& target, hsection hparent_section)
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
if(!hparent_section) hparent_section = &m_root;
|
||||||
|
storage_entry* pentry = find_storage_entry(value_name, hparent_section);
|
||||||
|
if(!pentry)
|
||||||
|
{
|
||||||
|
pentry = insert_new_entry_get_storage_entry(value_name, hparent_section, array_entry(array_entry_t<t_value>()));
|
||||||
|
if(!pentry)
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if(pentry->type() != typeid(array_entry))
|
||||||
|
*pentry = storage_entry(array_entry(array_entry_t<t_value>()));
|
||||||
|
|
||||||
|
array_entry& arr = boost::get<array_entry>(*pentry);
|
||||||
|
if(arr.type() != typeid(array_entry_t<t_value>))
|
||||||
|
arr = array_entry(array_entry_t<t_value>());
|
||||||
|
|
||||||
|
array_entry_t<t_value>& arr_typed = boost::get<array_entry_t<t_value> >(arr);
|
||||||
|
arr_typed.insert_first_val(target);
|
||||||
|
return &arr;
|
||||||
|
CATCH_ENTRY("portable_storage::insert_first_value", nullptr);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------------------------------------
|
||||||
|
template<class t_value>
|
||||||
|
bool portable_storage::insert_next_value(harray hval_array, const t_value& target)
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
CHECK_AND_ASSERT(hval_array, false);
|
||||||
|
|
||||||
|
CHECK_AND_ASSERT_MES(hval_array->type() == typeid(array_entry_t<t_value>),
|
||||||
|
false, "unexpected type in insert_next_value: " << typeid(array_entry_t<t_value>).name());
|
||||||
|
|
||||||
|
array_entry_t<t_value>& arr_typed = boost::get<array_entry_t<t_value> >(*hval_array);
|
||||||
|
arr_typed.insert_next_value(target);
|
||||||
|
return true;
|
||||||
|
CATCH_ENTRY("portable_storage::insert_next_value", false);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------------------------------------
|
||||||
|
//sections
|
||||||
|
inline
|
||||||
|
harray portable_storage::get_first_section(const std::string& sec_name, hsection& h_child_section, hsection hparent_section)
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
if(!hparent_section) hparent_section = &m_root;
|
||||||
|
storage_entry* pentry = find_storage_entry(sec_name, hparent_section);
|
||||||
|
if(!pentry)
|
||||||
|
return nullptr;
|
||||||
|
if(pentry->type() != typeid(array_entry))
|
||||||
|
return nullptr;
|
||||||
|
array_entry& ar_entry = boost::get<array_entry>(*pentry);
|
||||||
|
if(ar_entry.type() != typeid(array_entry_t<section>))
|
||||||
|
return nullptr;
|
||||||
|
array_entry_t<section>& sec_array = boost::get<array_entry_t<section>>(ar_entry);
|
||||||
|
section* psec = sec_array.get_first_val();
|
||||||
|
if(!psec)
|
||||||
|
return nullptr;
|
||||||
|
h_child_section = psec;
|
||||||
|
return &ar_entry;
|
||||||
|
CATCH_ENTRY("portable_storage::get_first_section", nullptr);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
bool portable_storage::get_next_section(harray hsec_array, hsection& h_child_section)
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
CHECK_AND_ASSERT(hsec_array, false);
|
||||||
|
if(hsec_array->type() != typeid(array_entry_t<section>))
|
||||||
|
return nullptr;
|
||||||
|
array_entry_t<section>& sec_array = boost::get<array_entry_t<section>>(*hsec_array);
|
||||||
|
h_child_section = sec_array.get_next_val();
|
||||||
|
if(!h_child_section)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
CATCH_ENTRY("portable_storage::get_next_section", false);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
harray portable_storage::insert_first_section(const std::string& sec_name, hsection& hinserted_childsection, hsection hparent_section)
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
if(!hparent_section) hparent_section = &m_root;
|
||||||
|
storage_entry* pentry = find_storage_entry(sec_name, hparent_section);
|
||||||
|
if(!pentry)
|
||||||
|
{
|
||||||
|
pentry = insert_new_entry_get_storage_entry(sec_name, hparent_section, array_entry(array_entry_t<section>()));
|
||||||
|
if(!pentry)
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if(pentry->type() != typeid(array_entry))
|
||||||
|
*pentry = storage_entry(array_entry(array_entry_t<section>()));
|
||||||
|
|
||||||
|
array_entry& ar_entry = boost::get<array_entry>(*pentry);
|
||||||
|
if(ar_entry.type() != typeid(array_entry_t<section>))
|
||||||
|
ar_entry = array_entry(array_entry_t<section>());
|
||||||
|
|
||||||
|
array_entry_t<section>& sec_array = boost::get<array_entry_t<section>>(ar_entry);
|
||||||
|
hinserted_childsection = &sec_array.insert_first_val(section());
|
||||||
|
return &ar_entry;
|
||||||
|
CATCH_ENTRY("portable_storage::insert_first_section", nullptr);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------------------------------------
|
||||||
|
inline
|
||||||
|
bool portable_storage::insert_next_section(harray hsec_array, hsection& hinserted_childsection)
|
||||||
|
{
|
||||||
|
TRY_ENTRY();
|
||||||
|
CHECK_AND_ASSERT(hsec_array, false);
|
||||||
|
CHECK_AND_ASSERT_MES(hsec_array->type() == typeid(array_entry_t<section>),
|
||||||
|
false, "unexpected type(not 'section') in insert_next_section, type: " << hsec_array->type().name());
|
||||||
|
|
||||||
|
array_entry_t<section>& sec_array = boost::get<array_entry_t<section>>(*hsec_array);
|
||||||
|
hinserted_childsection = &sec_array.insert_next_value(section());
|
||||||
|
return true;
|
||||||
|
CATCH_ENTRY("portable_storage::insert_next_section", false);
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------------------------------------
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,160 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 <boost/variant.hpp>
|
||||||
|
#include <boost/any.hpp>
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
#define PORTABLE_STORAGE_SIGNATUREA 0x01011101
|
||||||
|
#define PORTABLE_STORAGE_SIGNATUREB 0x01020101 // bender's nightmare
|
||||||
|
#define PORTABLE_STORAGE_FORMAT_VER 1
|
||||||
|
|
||||||
|
#define PORTABLE_RAW_SIZE_MARK_MASK 0x03
|
||||||
|
#define PORTABLE_RAW_SIZE_MARK_BYTE 0
|
||||||
|
#define PORTABLE_RAW_SIZE_MARK_WORD 1
|
||||||
|
#define PORTABLE_RAW_SIZE_MARK_DWORD 2
|
||||||
|
#define PORTABLE_RAW_SIZE_MARK_INT64 3
|
||||||
|
|
||||||
|
#ifndef MAX_STRING_LEN_POSSIBLE
|
||||||
|
#define MAX_STRING_LEN_POSSIBLE 2000000000 //do not let string be so big
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//data types
|
||||||
|
#define SERIALIZE_TYPE_INT64 1
|
||||||
|
#define SERIALIZE_TYPE_INT32 2
|
||||||
|
#define SERIALIZE_TYPE_INT16 3
|
||||||
|
#define SERIALIZE_TYPE_INT8 4
|
||||||
|
#define SERIALIZE_TYPE_UINT64 5
|
||||||
|
#define SERIALIZE_TYPE_UINT32 6
|
||||||
|
#define SERIALIZE_TYPE_UINT16 7
|
||||||
|
#define SERIALIZE_TYPE_UINT8 8
|
||||||
|
#define SERIALIZE_TYPE_DUOBLE 9
|
||||||
|
#define SERIALIZE_TYPE_STRING 10
|
||||||
|
#define SERIALIZE_TYPE_BOOL 11
|
||||||
|
#define SERIALIZE_TYPE_OBJECT 12
|
||||||
|
#define SERIALIZE_TYPE_ARRAY 13
|
||||||
|
|
||||||
|
#define SERIALIZE_FLAG_ARRAY 0x80
|
||||||
|
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace serialization
|
||||||
|
{
|
||||||
|
struct section;
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
template<class t_entry_type>
|
||||||
|
struct array_entry_t
|
||||||
|
{
|
||||||
|
array_entry_t():m_it(m_array.end()){}
|
||||||
|
|
||||||
|
const t_entry_type* get_first_val() const
|
||||||
|
{
|
||||||
|
m_it = m_array.begin();
|
||||||
|
return get_next_val();
|
||||||
|
}
|
||||||
|
|
||||||
|
t_entry_type* get_first_val()
|
||||||
|
{
|
||||||
|
m_it = m_array.begin();
|
||||||
|
return get_next_val();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const t_entry_type* get_next_val() const
|
||||||
|
{
|
||||||
|
if(m_it == m_array.end())
|
||||||
|
return nullptr;
|
||||||
|
return &(*(m_it++));
|
||||||
|
}
|
||||||
|
|
||||||
|
t_entry_type* get_next_val()
|
||||||
|
{
|
||||||
|
if(m_it == m_array.end())
|
||||||
|
return nullptr;
|
||||||
|
return (t_entry_type*)&(*(m_it++));//fuckoff
|
||||||
|
}
|
||||||
|
|
||||||
|
t_entry_type& insert_first_val(const t_entry_type& v)
|
||||||
|
{
|
||||||
|
m_array.clear();
|
||||||
|
m_it = m_array.end();
|
||||||
|
return insert_next_value(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
t_entry_type& insert_next_value(const t_entry_type& v)
|
||||||
|
{
|
||||||
|
m_array.push_back(v);
|
||||||
|
return m_array.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<t_entry_type> m_array;
|
||||||
|
mutable typename std::list<t_entry_type>::const_iterator m_it;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef boost::make_recursive_variant<
|
||||||
|
array_entry_t<section>,
|
||||||
|
array_entry_t<uint64_t>,
|
||||||
|
array_entry_t<uint32_t>,
|
||||||
|
array_entry_t<uint16_t>,
|
||||||
|
array_entry_t<uint8_t>,
|
||||||
|
array_entry_t<int64_t>,
|
||||||
|
array_entry_t<int32_t>,
|
||||||
|
array_entry_t<int16_t>,
|
||||||
|
array_entry_t<int8_t>,
|
||||||
|
array_entry_t<double>,
|
||||||
|
array_entry_t<bool>,
|
||||||
|
array_entry_t<std::string>,
|
||||||
|
array_entry_t<section>,
|
||||||
|
array_entry_t<boost::recursive_variant_>
|
||||||
|
>::type array_entry;
|
||||||
|
|
||||||
|
typedef boost::variant<uint64_t, uint32_t, uint16_t, uint8_t, int64_t, int32_t, int16_t, int8_t, double, bool, std::string, section, array_entry> storage_entry;
|
||||||
|
|
||||||
|
typedef std::string binarybuffer;//it's ok
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
struct section
|
||||||
|
{
|
||||||
|
std::map<std::string, storage_entry> m_entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
//handle-like aliases
|
||||||
|
typedef section* hsection;
|
||||||
|
typedef array_entry* harray;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,281 @@
|
||||||
|
// Copyright (c) 2006-2013, 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 "misc_language.h"
|
||||||
|
#include "portable_storage_base.h"
|
||||||
|
|
||||||
|
#ifdef EPEE_PORTABLE_STORAGE_RECURSION_LIMIT
|
||||||
|
#define EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL EPEE_PORTABLE_STORAGE_RECURSION_LIMIT
|
||||||
|
#else
|
||||||
|
#define EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL 100
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace epee
|
||||||
|
{
|
||||||
|
namespace serialization
|
||||||
|
{
|
||||||
|
struct throwable_buffer_reader
|
||||||
|
{
|
||||||
|
throwable_buffer_reader(const void* ptr, size_t sz);
|
||||||
|
void read(void* target, size_t count);
|
||||||
|
void read_sec_name(std::string& sce_name);
|
||||||
|
template<class t_pod_type>
|
||||||
|
void read(t_pod_type& pod_val);
|
||||||
|
template<class t_type>
|
||||||
|
t_type read();
|
||||||
|
template<class type_name>
|
||||||
|
storage_entry read_ae();
|
||||||
|
storage_entry load_storage_array_entry(uint8_t type);
|
||||||
|
size_t read_varint();
|
||||||
|
template<class t_type>
|
||||||
|
storage_entry read_se();
|
||||||
|
storage_entry load_storage_entry();
|
||||||
|
void read(section& sec);
|
||||||
|
void read(std::string& str);
|
||||||
|
private:
|
||||||
|
struct recursuion_limitation_guard
|
||||||
|
{
|
||||||
|
size_t& m_counter_ref;
|
||||||
|
recursuion_limitation_guard(size_t& counter):m_counter_ref(counter)
|
||||||
|
{
|
||||||
|
++m_counter_ref;
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(m_counter_ref < EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL, "Wrong blob data in portable storage: recursion limitation (" << EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL << ") exceeded");
|
||||||
|
}
|
||||||
|
~recursuion_limitation_guard()
|
||||||
|
{
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(m_counter_ref != 0, "Internal error: m_counter_ref == 0 while ~recursuion_limitation_guard()");
|
||||||
|
--m_counter_ref;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#define RECURSION_LIMITATION() recursuion_limitation_guard rl(m_recursion_count)
|
||||||
|
|
||||||
|
const uint8_t* m_ptr;
|
||||||
|
size_t m_count;
|
||||||
|
size_t m_recursion_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline throwable_buffer_reader::throwable_buffer_reader(const void* ptr, size_t sz)
|
||||||
|
{
|
||||||
|
if(!ptr)
|
||||||
|
throw std::runtime_error("throwable_buffer_reader: ptr==nullptr");
|
||||||
|
if(!sz)
|
||||||
|
throw std::runtime_error("throwable_buffer_reader: sz==0");
|
||||||
|
m_ptr = (uint8_t*)ptr;
|
||||||
|
m_count = sz;
|
||||||
|
m_recursion_count = 0;
|
||||||
|
}
|
||||||
|
inline
|
||||||
|
void throwable_buffer_reader::read(void* target, size_t count)
|
||||||
|
{
|
||||||
|
RECURSION_LIMITATION();
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(m_count >= count, " attempt to read " << count << " bytes from buffer with " << m_count << " bytes remained");
|
||||||
|
memcpy(target, m_ptr, count);
|
||||||
|
m_ptr += count;
|
||||||
|
m_count -= count;
|
||||||
|
}
|
||||||
|
inline
|
||||||
|
void throwable_buffer_reader::read_sec_name(std::string& sce_name)
|
||||||
|
{
|
||||||
|
RECURSION_LIMITATION();
|
||||||
|
uint8_t name_len = 0;
|
||||||
|
read(name_len);
|
||||||
|
sce_name.resize(name_len);
|
||||||
|
read((void*)sce_name.data(), name_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class t_pod_type>
|
||||||
|
void throwable_buffer_reader::read(t_pod_type& pod_val)
|
||||||
|
{
|
||||||
|
RECURSION_LIMITATION();
|
||||||
|
read(&pod_val, sizeof(pod_val));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class t_type>
|
||||||
|
t_type throwable_buffer_reader::read()
|
||||||
|
{
|
||||||
|
RECURSION_LIMITATION();
|
||||||
|
t_type v;
|
||||||
|
read(v);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class type_name>
|
||||||
|
storage_entry throwable_buffer_reader::read_ae()
|
||||||
|
{
|
||||||
|
RECURSION_LIMITATION();
|
||||||
|
//for pod types
|
||||||
|
array_entry_t<type_name> sa;
|
||||||
|
size_t size = read_varint();
|
||||||
|
//TODO: add some optimization here later
|
||||||
|
while(size--)
|
||||||
|
sa.m_array.push_back(read<type_name>());
|
||||||
|
return storage_entry(array_entry(sa));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
storage_entry throwable_buffer_reader::load_storage_array_entry(uint8_t type)
|
||||||
|
{
|
||||||
|
RECURSION_LIMITATION();
|
||||||
|
type &= ~SERIALIZE_FLAG_ARRAY;
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case SERIALIZE_TYPE_INT64: return read_ae<int64_t>();
|
||||||
|
case SERIALIZE_TYPE_INT32: return read_ae<int32_t>();
|
||||||
|
case SERIALIZE_TYPE_INT16: return read_ae<int16_t>();
|
||||||
|
case SERIALIZE_TYPE_INT8: return read_ae<int8_t>();
|
||||||
|
case SERIALIZE_TYPE_UINT64: return read_ae<uint64_t>();
|
||||||
|
case SERIALIZE_TYPE_UINT32: return read_ae<uint32_t>();
|
||||||
|
case SERIALIZE_TYPE_UINT16: return read_ae<uint16_t>();
|
||||||
|
case SERIALIZE_TYPE_UINT8: return read_ae<uint8_t>();
|
||||||
|
case SERIALIZE_TYPE_DUOBLE: return read_ae<double>();
|
||||||
|
case SERIALIZE_TYPE_BOOL: return read_ae<bool>();
|
||||||
|
case SERIALIZE_TYPE_STRING: return read_ae<std::string>();
|
||||||
|
case SERIALIZE_TYPE_OBJECT: return read_ae<section>();
|
||||||
|
case SERIALIZE_TYPE_ARRAY: return read_ae<array_entry>();
|
||||||
|
default:
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(false, "unknown entry_type code = " << type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
size_t throwable_buffer_reader::read_varint()
|
||||||
|
{
|
||||||
|
RECURSION_LIMITATION();
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(m_count >= 1, "empty buff, expected place for varint");
|
||||||
|
size_t v = 0;
|
||||||
|
uint8_t size_mask = (*(uint8_t*)m_ptr) &PORTABLE_RAW_SIZE_MARK_MASK;
|
||||||
|
switch (size_mask)
|
||||||
|
{
|
||||||
|
case PORTABLE_RAW_SIZE_MARK_BYTE: v = read<uint8_t>();break;
|
||||||
|
case PORTABLE_RAW_SIZE_MARK_WORD: v = read<uint16_t>();break;
|
||||||
|
case PORTABLE_RAW_SIZE_MARK_DWORD: v = read<uint32_t>();break;
|
||||||
|
case PORTABLE_RAW_SIZE_MARK_INT64: v = read<uint64_t>();break;
|
||||||
|
default:
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(false, "unknown varint size_mask = " << size_mask);
|
||||||
|
}
|
||||||
|
v >>= 2;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class t_type>
|
||||||
|
storage_entry throwable_buffer_reader::read_se()
|
||||||
|
{
|
||||||
|
RECURSION_LIMITATION();
|
||||||
|
t_type v;
|
||||||
|
read(v);
|
||||||
|
return storage_entry(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
inline storage_entry throwable_buffer_reader::read_se<std::string>()
|
||||||
|
{
|
||||||
|
RECURSION_LIMITATION();
|
||||||
|
return storage_entry(read<std::string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<>
|
||||||
|
inline storage_entry throwable_buffer_reader::read_se<section>()
|
||||||
|
{
|
||||||
|
RECURSION_LIMITATION();
|
||||||
|
section s;//use extra variable due to vs bug, line "storage_entry se(section()); " can't be compiled in visual studio
|
||||||
|
storage_entry se(s);
|
||||||
|
section& section_entry = boost::get<section>(se);
|
||||||
|
read(section_entry);
|
||||||
|
return se;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
inline storage_entry throwable_buffer_reader::read_se<array_entry>()
|
||||||
|
{
|
||||||
|
RECURSION_LIMITATION();
|
||||||
|
uint8_t ent_type = 0;
|
||||||
|
read(ent_type);
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(ent_type&SERIALIZE_FLAG_ARRAY, "wrong type sequenses");
|
||||||
|
return load_storage_array_entry(ent_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
storage_entry throwable_buffer_reader::load_storage_entry()
|
||||||
|
{
|
||||||
|
RECURSION_LIMITATION();
|
||||||
|
uint8_t ent_type = 0;
|
||||||
|
read(ent_type);
|
||||||
|
if(ent_type&SERIALIZE_FLAG_ARRAY)
|
||||||
|
return load_storage_array_entry(ent_type);
|
||||||
|
|
||||||
|
switch(ent_type)
|
||||||
|
{
|
||||||
|
case SERIALIZE_TYPE_INT64: return read_se<int64_t>();
|
||||||
|
case SERIALIZE_TYPE_INT32: return read_se<int32_t>();
|
||||||
|
case SERIALIZE_TYPE_INT16: return read_se<int16_t>();
|
||||||
|
case SERIALIZE_TYPE_INT8: return read_se<int8_t>();
|
||||||
|
case SERIALIZE_TYPE_UINT64: return read_se<uint64_t>();
|
||||||
|
case SERIALIZE_TYPE_UINT32: return read_se<uint32_t>();
|
||||||
|
case SERIALIZE_TYPE_UINT16: return read_se<uint16_t>();
|
||||||
|
case SERIALIZE_TYPE_UINT8: return read_se<uint8_t>();
|
||||||
|
case SERIALIZE_TYPE_DUOBLE: return read_se<double>();
|
||||||
|
case SERIALIZE_TYPE_BOOL: return read_se<bool>();
|
||||||
|
case SERIALIZE_TYPE_STRING: return read_se<std::string>();
|
||||||
|
case SERIALIZE_TYPE_OBJECT: return read_se<section>();
|
||||||
|
case SERIALIZE_TYPE_ARRAY: return read_se<array_entry>();
|
||||||
|
default:
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(false, "unknown entry_type code = " << ent_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline
|
||||||
|
void throwable_buffer_reader::read(section& sec)
|
||||||
|
{
|
||||||
|
RECURSION_LIMITATION();
|
||||||
|
sec.m_entries.clear();
|
||||||
|
size_t count = read_varint();
|
||||||
|
while(count--)
|
||||||
|
{
|
||||||
|
//read section name string
|
||||||
|
std::string sec_name;
|
||||||
|
read_sec_name(sec_name);
|
||||||
|
sec.m_entries.insert(std::make_pair(sec_name, load_storage_entry()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline
|
||||||
|
void throwable_buffer_reader::read(std::string& str)
|
||||||
|
{
|
||||||
|
RECURSION_LIMITATION();
|
||||||
|
size_t len = read_varint();
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(len < MAX_STRING_LEN_POSSIBLE, "to big string len value in storage: " << len);
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(m_count >= len, "string len count value " << len << " goes out of remain storage len " << m_count);
|
||||||
|
//do this manually to avoid double memory write in huge strings (first time at resize, second at read)
|
||||||
|
str.assign((const char*)m_ptr, len);
|
||||||
|
m_ptr+=len;
|
||||||
|
m_count -= len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue