mirror of
https://github.com/drogonframework/drogon.git
synced 2025-08-29 00:02:44 -04:00
Add redis support (#719)
This commit is contained in:
parent
df04c47f74
commit
6d9aa3b44c
6
.github/workflows/cmake.yml
vendored
6
.github/workflows/cmake.yml
vendored
@ -45,7 +45,8 @@ jobs:
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
brew install jsoncpp ossp-uuid brotli zlib
|
||||
brew install openssl lz4 mariadb sqlite3 postgresql
|
||||
brew install openssl lz4 mariadb sqlite3 postgresql hiredis
|
||||
brew install redis
|
||||
|
||||
- name: (Linux) Install dependencies
|
||||
if: runner.os == 'Linux'
|
||||
@ -112,6 +113,7 @@ jobs:
|
||||
brew tap homebrew/services;
|
||||
brew services restart postgresql;
|
||||
brew services start mariadb;
|
||||
brew services start redis;
|
||||
sleep 4;
|
||||
mariadb -e "SET PASSWORD FOR 'root'@'localhost' = PASSWORD('')";
|
||||
mariadb -e "GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost'";
|
||||
@ -135,7 +137,7 @@ jobs:
|
||||
run: ./test.sh -t
|
||||
|
||||
- name: Lint
|
||||
if: runner.os == 'Linux'
|
||||
if: matrix.os == 'ubuntu-20.04'
|
||||
working-directory: ${{env.GITHUB_WORKSPACE}}
|
||||
shell: bash
|
||||
run: ./format.sh && git diff --exit-code
|
||||
|
730
CMakeLists.txt
730
CMakeLists.txt
@ -4,19 +4,18 @@ project(drogon)
|
||||
|
||||
message(STATUS "compiler: " ${CMAKE_CXX_COMPILER_ID})
|
||||
include(CheckCXXSourceRuns)
|
||||
check_cxx_source_runs(
|
||||
"int main(){return 0;}"
|
||||
not_cross_compiling)
|
||||
check_cxx_source_runs("int main(){return 0;}" not_cross_compiling)
|
||||
|
||||
if(not_cross_compiling)
|
||||
set(BUILD_PROGRAMS ON)
|
||||
else()
|
||||
set(BUILD_PROGRAMS OFF)
|
||||
endif()
|
||||
if (not_cross_compiling)
|
||||
set(BUILD_PROGRAMS ON)
|
||||
else ()
|
||||
set(BUILD_PROGRAMS OFF)
|
||||
endif ()
|
||||
|
||||
option(BUILD_CTL "Build drogon_ctl" ${BUILD_PROGRAMS})
|
||||
option(BUILD_EXAMPLES "Build examples" ${BUILD_PROGRAMS})
|
||||
option(BUILD_ORM "Build orm" ON)
|
||||
option(BUILD_REDIS "Build redis client" ON)
|
||||
option(COZ_PROFILING "Use coz for profiling" OFF)
|
||||
option(LIBPQ_BATCH_MODE "Use batch mode for libpq" ON)
|
||||
option(BUILD_DROGON_SHARED "Build drogon as a shared lib" OFF)
|
||||
@ -25,39 +24,39 @@ set(DROGON_MAJOR_VERSION 1)
|
||||
set(DROGON_MINOR_VERSION 4)
|
||||
set(DROGON_PATCH_VERSION 1)
|
||||
set(DROGON_VERSION
|
||||
${DROGON_MAJOR_VERSION}.${DROGON_MINOR_VERSION}.${DROGON_PATCH_VERSION})
|
||||
${DROGON_MAJOR_VERSION}.${DROGON_MINOR_VERSION}.${DROGON_PATCH_VERSION})
|
||||
set(DROGON_VERSION_STRING "${DROGON_VERSION}")
|
||||
|
||||
# Offer the user the choice of overriding the installation directories
|
||||
set(INSTALL_LIB_DIR lib CACHE PATH "Installation directory for libraries")
|
||||
set(INSTALL_BIN_DIR bin CACHE PATH "Installation directory for executables")
|
||||
set(INSTALL_INCLUDE_DIR
|
||||
include
|
||||
CACHE PATH "Installation directory for header files")
|
||||
include
|
||||
CACHE PATH "Installation directory for header files")
|
||||
set(DEF_INSTALL_DROGON_CMAKE_DIR lib/cmake/Drogon)
|
||||
set(INSTALL_DROGON_CMAKE_DIR
|
||||
${DEF_INSTALL_DROGON_CMAKE_DIR}
|
||||
CACHE PATH "Installation directory for cmake files")
|
||||
${DEF_INSTALL_DROGON_CMAKE_DIR}
|
||||
CACHE PATH "Installation directory for cmake files")
|
||||
|
||||
if(BUILD_DROGON_SHARED)
|
||||
set(BUILD_TRANTOR_SHARED TRUE)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
|
||||
set(CMAKE_THREAD_LIBS_INIT "-lpthread")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
|
||||
set(CMAKE_HAVE_THREADS_LIBRARY 1)
|
||||
set(CMAKE_USE_WIN32_THREADS_INIT 0)
|
||||
set(CMAKE_USE_PTHREADS_INIT 1)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
# set(BUILD_EXAMPLES FALSE)
|
||||
list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES
|
||||
if (BUILD_DROGON_SHARED)
|
||||
set(BUILD_TRANTOR_SHARED TRUE)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
|
||||
set(CMAKE_THREAD_LIBS_INIT "-lpthread")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
|
||||
set(CMAKE_HAVE_THREADS_LIBRARY 1)
|
||||
set(CMAKE_USE_WIN32_THREADS_INIT 0)
|
||||
set(CMAKE_USE_PTHREADS_INIT 1)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
# set(BUILD_EXAMPLES FALSE)
|
||||
list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES
|
||||
"${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR}" isSystemDir)
|
||||
if("${isSystemDir}" STREQUAL "-1")
|
||||
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR}")
|
||||
endif("${isSystemDir}" STREQUAL "-1")
|
||||
add_library(${PROJECT_NAME} SHARED)
|
||||
else(BUILD_DROGON_SHARED)
|
||||
add_library(${PROJECT_NAME} STATIC)
|
||||
endif(BUILD_DROGON_SHARED)
|
||||
if ("${isSystemDir}" STREQUAL "-1")
|
||||
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR}")
|
||||
endif ("${isSystemDir}" STREQUAL "-1")
|
||||
add_library(${PROJECT_NAME} SHARED)
|
||||
else (BUILD_DROGON_SHARED)
|
||||
add_library(${PROJECT_NAME} STATIC)
|
||||
endif (BUILD_DROGON_SHARED)
|
||||
|
||||
include(cmake/DrogonUtilities.cmake)
|
||||
include(CheckIncludeFileCXX)
|
||||
@ -65,52 +64,53 @@ include(CheckIncludeFileCXX)
|
||||
check_include_file_cxx(any HAS_ANY)
|
||||
check_include_file_cxx(string_view HAS_STRING_VIEW)
|
||||
check_include_file_cxx(coroutine HAS_COROUTINE)
|
||||
if(HAS_ANY AND HAS_STRING_VIEW AND HAS_COROUTINE)
|
||||
set(DROGON_CXX_STANDARD 20)
|
||||
elseif(HAS_ANY AND HAS_STRING_VIEW)
|
||||
set(DROGON_CXX_STANDARD 17)
|
||||
else()
|
||||
set(DROGON_CXX_STANDARD 14)
|
||||
endif()
|
||||
if (HAS_ANY AND HAS_STRING_VIEW AND HAS_COROUTINE)
|
||||
set(DROGON_CXX_STANDARD 20)
|
||||
elseif (HAS_ANY AND HAS_STRING_VIEW)
|
||||
set(DROGON_CXX_STANDARD 17)
|
||||
else ()
|
||||
set(DROGON_CXX_STANDARD 14)
|
||||
endif ()
|
||||
|
||||
target_include_directories(
|
||||
${PROJECT_NAME}
|
||||
PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/lib/inc>
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/orm_lib/inc>
|
||||
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/trantor>
|
||||
$<INSTALL_INTERFACE:${INSTALL_INCLUDE_DIR}>)
|
||||
${PROJECT_NAME}
|
||||
PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/lib/inc>
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/orm_lib/inc>
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/nosql_lib/redis/inc>
|
||||
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/trantor>
|
||||
$<INSTALL_INTERFACE:${INSTALL_INCLUDE_DIR}>)
|
||||
|
||||
if(WIN32)
|
||||
target_include_directories(
|
||||
${PROJECT_NAME}
|
||||
PRIVATE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/third_party/mman-win32>)
|
||||
endif(WIN32)
|
||||
if (WIN32)
|
||||
target_include_directories(
|
||||
${PROJECT_NAME}
|
||||
PRIVATE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/third_party/mman-win32>)
|
||||
endif (WIN32)
|
||||
|
||||
add_subdirectory(trantor)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC trantor)
|
||||
|
||||
if(NOT WIN32)
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE dl)
|
||||
endif(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
|
||||
else(NOT WIN32)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE shlwapi)
|
||||
endif(NOT WIN32)
|
||||
if (NOT WIN32)
|
||||
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE dl)
|
||||
endif (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
|
||||
else (NOT WIN32)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE shlwapi)
|
||||
endif (NOT WIN32)
|
||||
|
||||
if(DROGON_CXX_STANDARD EQUAL 14)
|
||||
# With C++14, use boost to support any and string_view
|
||||
message(STATUS "use c++14")
|
||||
find_package(Boost 1.61.0 REQUIRED)
|
||||
message(STATUS "boost include dir:" ${Boost_INCLUDE_DIR})
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC Boost::boost)
|
||||
list(APPEND INCLUDE_DIRS_FOR_DYNAMIC_VIEW ${Boost_INCLUDE_DIR})
|
||||
elseif(DROGON_CXX_STANDARD EQUAL 17)
|
||||
message(STATUS "use c++17")
|
||||
else()
|
||||
message(STATUS "use c++20")
|
||||
endif()
|
||||
if (DROGON_CXX_STANDARD EQUAL 14)
|
||||
# With C++14, use boost to support any and string_view
|
||||
message(STATUS "use c++14")
|
||||
find_package(Boost 1.61.0 REQUIRED)
|
||||
message(STATUS "boost include dir:" ${Boost_INCLUDE_DIR})
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC Boost::boost)
|
||||
list(APPEND INCLUDE_DIRS_FOR_DYNAMIC_VIEW ${Boost_INCLUDE_DIR})
|
||||
elseif (DROGON_CXX_STANDARD EQUAL 17)
|
||||
message(STATUS "use c++17")
|
||||
else ()
|
||||
message(STATUS "use c++20")
|
||||
endif ()
|
||||
|
||||
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules/)
|
||||
|
||||
@ -119,261 +119,293 @@ find_package(Jsoncpp REQUIRED)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC Jsoncpp_lib)
|
||||
list(APPEND INCLUDE_DIRS_FOR_DYNAMIC_VIEW ${JSONCPP_INCLUDE_DIRS})
|
||||
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD" AND NOT WIN32)
|
||||
find_package(UUID REQUIRED)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE UUID_lib)
|
||||
if (NOT
|
||||
${CMAKE_SYSTEM_NAME}
|
||||
STREQUAL
|
||||
"FreeBSD"
|
||||
AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD"
|
||||
AND NOT WIN32)
|
||||
find_package(UUID REQUIRED)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE UUID_lib)
|
||||
|
||||
try_compile(normal_uuid ${CMAKE_BINARY_DIR}/cmaketest
|
||||
${PROJECT_SOURCE_DIR}/cmake/tests/normal_uuid_lib_test.cc
|
||||
LINK_LIBRARIES UUID_lib)
|
||||
try_compile(ossp_uuid ${CMAKE_BINARY_DIR}/cmaketest
|
||||
${PROJECT_SOURCE_DIR}/cmake/tests/ossp_uuid_lib_test.cc
|
||||
LINK_LIBRARIES UUID_lib)
|
||||
if(normal_uuid)
|
||||
add_definitions(-DUSE_OSSP_UUID=0)
|
||||
elseif(ossp_uuid)
|
||||
add_definitions(-DUSE_OSSP_UUID=1)
|
||||
else()
|
||||
message(FATAL_ERROR "uuid lib error")
|
||||
endif()
|
||||
endif(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD" AND NOT WIN32)
|
||||
try_compile(normal_uuid ${CMAKE_BINARY_DIR}/cmaketest
|
||||
${PROJECT_SOURCE_DIR}/cmake/tests/normal_uuid_lib_test.cc
|
||||
LINK_LIBRARIES UUID_lib)
|
||||
try_compile(ossp_uuid ${CMAKE_BINARY_DIR}/cmaketest
|
||||
${PROJECT_SOURCE_DIR}/cmake/tests/ossp_uuid_lib_test.cc
|
||||
LINK_LIBRARIES UUID_lib)
|
||||
if (normal_uuid)
|
||||
add_definitions(-DUSE_OSSP_UUID=0)
|
||||
elseif (ossp_uuid)
|
||||
add_definitions(-DUSE_OSSP_UUID=1)
|
||||
else ()
|
||||
message(FATAL_ERROR "uuid lib error")
|
||||
endif ()
|
||||
endif (NOT
|
||||
${CMAKE_SYSTEM_NAME}
|
||||
STREQUAL
|
||||
"FreeBSD"
|
||||
AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD"
|
||||
AND NOT WIN32)
|
||||
|
||||
find_package(Brotli)
|
||||
if(Brotli_FOUND)
|
||||
message(STATUS "Brotli found")
|
||||
add_definitions(-DUSE_BROTLI)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Brotli_lib)
|
||||
endif(Brotli_FOUND)
|
||||
if (Brotli_FOUND)
|
||||
message(STATUS "Brotli found")
|
||||
add_definitions(-DUSE_BROTLI)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Brotli_lib)
|
||||
endif (Brotli_FOUND)
|
||||
|
||||
set(DROGON_SOURCES
|
||||
lib/src/AOPAdvice.cc
|
||||
lib/src/CacheFile.cc
|
||||
lib/src/ConfigLoader.cc
|
||||
lib/src/Cookie.cc
|
||||
lib/src/DrClassMap.cc
|
||||
lib/src/DrTemplateBase.cc
|
||||
lib/src/FiltersFunction.cc
|
||||
lib/src/HttpAppFrameworkImpl.cc
|
||||
lib/src/HttpClientImpl.cc
|
||||
lib/src/HttpControllersRouter.cc
|
||||
lib/src/HttpFileImpl.cc
|
||||
lib/src/HttpFileUploadRequest.cc
|
||||
lib/src/HttpRequestImpl.cc
|
||||
lib/src/HttpRequestParser.cc
|
||||
lib/src/HttpResponseImpl.cc
|
||||
lib/src/HttpResponseParser.cc
|
||||
lib/src/HttpServer.cc
|
||||
lib/src/HttpSimpleControllersRouter.cc
|
||||
lib/src/HttpUtils.cc
|
||||
lib/src/HttpViewData.cc
|
||||
lib/src/IntranetIpFilter.cc
|
||||
lib/src/ListenerManager.cc
|
||||
lib/src/LocalHostFilter.cc
|
||||
lib/src/MultiPart.cc
|
||||
lib/src/NotFound.cc
|
||||
lib/src/PluginsManager.cc
|
||||
lib/src/SecureSSLRedirector.cc
|
||||
lib/src/SessionManager.cc
|
||||
lib/src/StaticFileRouter.cc
|
||||
lib/src/Utilities.cc
|
||||
lib/src/WebSocketClientImpl.cc
|
||||
lib/src/WebSocketConnectionImpl.cc
|
||||
lib/src/WebsocketControllersRouter.cc)
|
||||
lib/src/AOPAdvice.cc
|
||||
lib/src/CacheFile.cc
|
||||
lib/src/ConfigLoader.cc
|
||||
lib/src/Cookie.cc
|
||||
lib/src/DrClassMap.cc
|
||||
lib/src/DrTemplateBase.cc
|
||||
lib/src/FiltersFunction.cc
|
||||
lib/src/HttpAppFrameworkImpl.cc
|
||||
lib/src/HttpClientImpl.cc
|
||||
lib/src/HttpControllersRouter.cc
|
||||
lib/src/HttpFileImpl.cc
|
||||
lib/src/HttpFileUploadRequest.cc
|
||||
lib/src/HttpRequestImpl.cc
|
||||
lib/src/HttpRequestParser.cc
|
||||
lib/src/HttpResponseImpl.cc
|
||||
lib/src/HttpResponseParser.cc
|
||||
lib/src/HttpServer.cc
|
||||
lib/src/HttpSimpleControllersRouter.cc
|
||||
lib/src/HttpUtils.cc
|
||||
lib/src/HttpViewData.cc
|
||||
lib/src/IntranetIpFilter.cc
|
||||
lib/src/ListenerManager.cc
|
||||
lib/src/LocalHostFilter.cc
|
||||
lib/src/MultiPart.cc
|
||||
lib/src/NotFound.cc
|
||||
lib/src/PluginsManager.cc
|
||||
lib/src/SecureSSLRedirector.cc
|
||||
lib/src/SessionManager.cc
|
||||
lib/src/StaticFileRouter.cc
|
||||
lib/src/Utilities.cc
|
||||
lib/src/WebSocketClientImpl.cc
|
||||
lib/src/WebSocketConnectionImpl.cc
|
||||
lib/src/WebsocketControllersRouter.cc)
|
||||
|
||||
if(NOT WIN32)
|
||||
set(DROGON_SOURCES ${DROGON_SOURCES} lib/src/SharedLibManager.cc)
|
||||
else(NOT WIN32)
|
||||
set(DROGON_SOURCES ${DROGON_SOURCES} third_party/mman-win32/mman.c)
|
||||
endif(NOT WIN32)
|
||||
if (NOT WIN32)
|
||||
set(DROGON_SOURCES ${DROGON_SOURCES} lib/src/SharedLibManager.cc)
|
||||
else (NOT WIN32)
|
||||
set(DROGON_SOURCES ${DROGON_SOURCES} third_party/mman-win32/mman.c)
|
||||
endif (NOT WIN32)
|
||||
|
||||
if(BUILD_ORM)
|
||||
# find postgres
|
||||
find_package(pg)
|
||||
if(pg_FOUND)
|
||||
message(STATUS "libpq inc path:" ${PG_INCLUDE_DIRS})
|
||||
message(STATUS "libpq lib:" ${PG_LIBRARIES})
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE pg_lib)
|
||||
set(DROGON_SOURCES ${DROGON_SOURCES}
|
||||
orm_lib/src/postgresql_impl/PostgreSQLResultImpl.cc)
|
||||
if(LIBPQ_BATCH_MODE)
|
||||
try_compile(libpq_supports_batch ${CMAKE_BINARY_DIR}/cmaketest
|
||||
${PROJECT_SOURCE_DIR}/cmake/tests/test_libpq_batch_mode.cc
|
||||
LINK_LIBRARIES ${PostgreSQL_LIBRARIES}
|
||||
CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${PostgreSQL_INCLUDE_DIR}")
|
||||
endif(LIBPQ_BATCH_MODE)
|
||||
if(libpq_supports_batch)
|
||||
message(STATUS "The libpq supports batch mode")
|
||||
option(LIBPQ_SUPPORTS_BATCH_MODE "libpq batch mode" ON)
|
||||
set(DROGON_SOURCES ${DROGON_SOURCES}
|
||||
orm_lib/src/postgresql_impl/PgBatchConnection.cc)
|
||||
else(libpq_supports_batch)
|
||||
option(LIBPQ_SUPPORTS_BATCH_MODE "libpq batch mode" OFF)
|
||||
set(DROGON_SOURCES ${DROGON_SOURCES}
|
||||
orm_lib/src/postgresql_impl/PgConnection.cc)
|
||||
endif(libpq_supports_batch)
|
||||
endif(pg_FOUND)
|
||||
if (BUILD_ORM)
|
||||
# find postgres
|
||||
find_package(pg)
|
||||
if (pg_FOUND)
|
||||
message(STATUS "libpq inc path:" ${PG_INCLUDE_DIRS})
|
||||
message(STATUS "libpq lib:" ${PG_LIBRARIES})
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE pg_lib)
|
||||
set(DROGON_SOURCES ${DROGON_SOURCES}
|
||||
orm_lib/src/postgresql_impl/PostgreSQLResultImpl.cc)
|
||||
if (LIBPQ_BATCH_MODE)
|
||||
try_compile(libpq_supports_batch ${CMAKE_BINARY_DIR}/cmaketest
|
||||
${PROJECT_SOURCE_DIR}/cmake/tests/test_libpq_batch_mode.cc
|
||||
LINK_LIBRARIES ${PostgreSQL_LIBRARIES}
|
||||
CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${PostgreSQL_INCLUDE_DIR}")
|
||||
endif (LIBPQ_BATCH_MODE)
|
||||
if (libpq_supports_batch)
|
||||
message(STATUS "The libpq supports batch mode")
|
||||
option(LIBPQ_SUPPORTS_BATCH_MODE "libpq batch mode" ON)
|
||||
set(DROGON_SOURCES ${DROGON_SOURCES}
|
||||
orm_lib/src/postgresql_impl/PgBatchConnection.cc)
|
||||
else (libpq_supports_batch)
|
||||
option(LIBPQ_SUPPORTS_BATCH_MODE "libpq batch mode" OFF)
|
||||
set(DROGON_SOURCES ${DROGON_SOURCES}
|
||||
orm_lib/src/postgresql_impl/PgConnection.cc)
|
||||
endif (libpq_supports_batch)
|
||||
endif (pg_FOUND)
|
||||
|
||||
# Find mysql, only mariadb client liberary is supported
|
||||
find_package(MySQL)
|
||||
if(MySQL_FOUND)
|
||||
message(STATUS "Ok! We find the mariadb!")
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE MySQL_lib)
|
||||
set(DROGON_SOURCES ${DROGON_SOURCES}
|
||||
orm_lib/src/mysql_impl/MysqlConnection.cc
|
||||
orm_lib/src/mysql_impl/MysqlResultImpl.cc)
|
||||
endif(MySQL_FOUND)
|
||||
# Find mysql, only mariadb client liberary is supported
|
||||
find_package(MySQL)
|
||||
if (MySQL_FOUND)
|
||||
message(STATUS "Ok! We find the mariadb!")
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE MySQL_lib)
|
||||
set(DROGON_SOURCES ${DROGON_SOURCES}
|
||||
orm_lib/src/mysql_impl/MysqlConnection.cc
|
||||
orm_lib/src/mysql_impl/MysqlResultImpl.cc)
|
||||
endif (MySQL_FOUND)
|
||||
|
||||
# Find sqlite3.
|
||||
find_package(SQLite3)
|
||||
if(SQLite3_FOUND)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE SQLite3_lib)
|
||||
set(DROGON_SOURCES ${DROGON_SOURCES}
|
||||
orm_lib/src/sqlite3_impl/Sqlite3Connection.cc
|
||||
orm_lib/src/sqlite3_impl/Sqlite3ResultImpl.cc)
|
||||
endif(SQLite3_FOUND)
|
||||
endif(BUILD_ORM)
|
||||
# Find sqlite3.
|
||||
find_package(SQLite3)
|
||||
if (SQLite3_FOUND)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE SQLite3_lib)
|
||||
set(DROGON_SOURCES ${DROGON_SOURCES}
|
||||
orm_lib/src/sqlite3_impl/Sqlite3Connection.cc
|
||||
orm_lib/src/sqlite3_impl/Sqlite3ResultImpl.cc)
|
||||
endif (SQLite3_FOUND)
|
||||
endif (BUILD_ORM)
|
||||
|
||||
find_package(ZLIB REQUIRED)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE ZLIB::ZLIB)
|
||||
|
||||
find_package(OpenSSL)
|
||||
if(OpenSSL_FOUND)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE OpenSSL::SSL OpenSSL::Crypto)
|
||||
else(OpenSSL_FOUND)
|
||||
set(DROGON_SOURCES ${DROGON_SOURCES} lib/src/ssl_funcs/Md5.cc
|
||||
lib/src/ssl_funcs/Sha1.cc)
|
||||
endif(OpenSSL_FOUND)
|
||||
if (OpenSSL_FOUND)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE OpenSSL::SSL OpenSSL::Crypto)
|
||||
else (OpenSSL_FOUND)
|
||||
set(DROGON_SOURCES ${DROGON_SOURCES} lib/src/ssl_funcs/Md5.cc
|
||||
lib/src/ssl_funcs/Sha1.cc)
|
||||
endif (OpenSSL_FOUND)
|
||||
|
||||
if (BUILD_REDIS)
|
||||
find_package(Hiredis)
|
||||
if (Hiredis_FOUND)
|
||||
add_definitions(-DUSE_REDIS)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Hiredis_lib)
|
||||
set(DROGON_SOURCES
|
||||
${DROGON_SOURCES}
|
||||
nosql_lib/redis/src/RedisClientImpl.cc
|
||||
nosql_lib/redis/src/RedisConnection.cc
|
||||
nosql_lib/redis/src/RedisResult.cc
|
||||
nosql_lib/redis/src/RedisClientLockFree.cc
|
||||
nosql_lib/redis/src/RedisClientManager.cc
|
||||
nosql_lib/redis/src/RedisTransactionImpl.cc)
|
||||
|
||||
if (BUILD_TESTING)
|
||||
add_subdirectory(nosql_lib/redis/tests)
|
||||
endif (BUILD_TESTING)
|
||||
else ()
|
||||
set(DROGON_SOURCES ${DROGON_SOURCES} lib/src/RedisClientManagerSkipped.cc)
|
||||
endif (Hiredis_FOUND)
|
||||
endif (BUILD_REDIS)
|
||||
|
||||
execute_process(COMMAND "git" rev-parse HEAD
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE GIT_SHA1
|
||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE GIT_SHA1
|
||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
configure_file("${PROJECT_SOURCE_DIR}/cmake/templates/version.h.in"
|
||||
"${PROJECT_SOURCE_DIR}/lib/inc/drogon/version.h" @ONLY)
|
||||
"${PROJECT_SOURCE_DIR}/lib/inc/drogon/version.h" @ONLY)
|
||||
|
||||
if(DROGON_CXX_STANDARD EQUAL 20)
|
||||
option(USE_COROUTINE "Enable C++20 coroutine support" ON)
|
||||
else(DROGON_CXX_STANDARD EQUAL 20)
|
||||
option(USE_COROUTINE "Enable C++20 coroutine support" OFF)
|
||||
endif(DROGON_CXX_STANDARD EQUAL 20)
|
||||
if (DROGON_CXX_STANDARD EQUAL 20)
|
||||
option(USE_COROUTINE "Enable C++20 coroutine support" ON)
|
||||
else (DROGON_CXX_STANDARD EQUAL 20)
|
||||
option(USE_COROUTINE "Enable C++20 coroutine support" OFF)
|
||||
endif (DROGON_CXX_STANDARD EQUAL 20)
|
||||
|
||||
if(BUILD_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
endif(BUILD_EXAMPLES)
|
||||
if (BUILD_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
endif (BUILD_EXAMPLES)
|
||||
|
||||
if(BUILD_CTL)
|
||||
add_subdirectory(drogon_ctl)
|
||||
endif(BUILD_CTL)
|
||||
if (BUILD_CTL)
|
||||
add_subdirectory(drogon_ctl)
|
||||
endif (BUILD_CTL)
|
||||
|
||||
if(COZ_PROFILING)
|
||||
find_package(coz-profiler REQUIRED)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE -DCOZ_PROFILING=1)
|
||||
# If linked will not need to be ran with `coz run --- [executable]` to run the
|
||||
# profiler, but drogon_ctl currently won't build because it doesn't find debug
|
||||
# information while trying to generate it's own sources
|
||||
# target_link_libraries(${PROJECT_NAME} PUBLIC coz::coz)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${COZ_INCLUDE_DIRS})
|
||||
endif(COZ_PROFILING)
|
||||
if (COZ_PROFILING)
|
||||
find_package(coz-profiler REQUIRED)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE -DCOZ_PROFILING=1)
|
||||
# If linked will not need to be ran with `coz run --- [executable]` to run the
|
||||
# profiler, but drogon_ctl currently won't build because it doesn't find debug
|
||||
# information while trying to generate it's own sources
|
||||
# target_link_libraries(${PROJECT_NAME} PUBLIC coz::coz)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${COZ_INCLUDE_DIRS})
|
||||
endif (COZ_PROFILING)
|
||||
|
||||
if(pg_FOUND OR MySQL_FOUND OR SQLite3_FOUND)
|
||||
set(DROGON_SOURCES
|
||||
${DROGON_SOURCES}
|
||||
orm_lib/src/ArrayParser.cc
|
||||
orm_lib/src/Criteria.cc
|
||||
orm_lib/src/DbClient.cc
|
||||
orm_lib/src/DbClientImpl.cc
|
||||
orm_lib/src/DbClientLockFree.cc
|
||||
orm_lib/src/DbClientManager.cc
|
||||
orm_lib/src/DbConnection.cc
|
||||
orm_lib/src/Exception.cc
|
||||
orm_lib/src/Field.cc
|
||||
orm_lib/src/Result.cc
|
||||
orm_lib/src/Row.cc
|
||||
orm_lib/src/SqlBinder.cc
|
||||
orm_lib/src/TransactionImpl.cc
|
||||
orm_lib/src/RestfulController.cc)
|
||||
else(pg_FOUND OR MySQL_FOUND OR SQLite3_FOUND)
|
||||
set(DROGON_SOURCES ${DROGON_SOURCES} lib/src/DbClientManagerSkipped.cc)
|
||||
endif(pg_FOUND OR MySQL_FOUND OR SQLite3_FOUND)
|
||||
if (pg_FOUND OR MySQL_FOUND OR SQLite3_FOUND)
|
||||
set(DROGON_SOURCES
|
||||
${DROGON_SOURCES}
|
||||
orm_lib/src/ArrayParser.cc
|
||||
orm_lib/src/Criteria.cc
|
||||
orm_lib/src/DbClient.cc
|
||||
orm_lib/src/DbClientImpl.cc
|
||||
orm_lib/src/DbClientLockFree.cc
|
||||
orm_lib/src/DbClientManager.cc
|
||||
orm_lib/src/DbConnection.cc
|
||||
orm_lib/src/Exception.cc
|
||||
orm_lib/src/Field.cc
|
||||
orm_lib/src/Result.cc
|
||||
orm_lib/src/Row.cc
|
||||
orm_lib/src/SqlBinder.cc
|
||||
orm_lib/src/TransactionImpl.cc
|
||||
orm_lib/src/RestfulController.cc)
|
||||
else (pg_FOUND OR MySQL_FOUND OR SQLite3_FOUND)
|
||||
set(DROGON_SOURCES ${DROGON_SOURCES} lib/src/DbClientManagerSkipped.cc)
|
||||
endif (pg_FOUND OR MySQL_FOUND OR SQLite3_FOUND)
|
||||
|
||||
target_sources(${PROJECT_NAME} PRIVATE ${DROGON_SOURCES})
|
||||
|
||||
set_target_properties(${PROJECT_NAME}
|
||||
PROPERTIES CXX_STANDARD ${DROGON_CXX_STANDARD})
|
||||
PROPERTIES CXX_STANDARD ${DROGON_CXX_STANDARD})
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD_REQUIRED ON)
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES CXX_EXTENSIONS OFF)
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES EXPORT_NAME Drogon)
|
||||
|
||||
if(pg_FOUND OR MySQL_FOUND OR SQLite3_FOUND)
|
||||
if(pg_FOUND)
|
||||
option(USE_POSTGRESQL "Enable PostgreSQL" ON)
|
||||
else(pg_FOUND)
|
||||
option(USE_POSTGRESQL "Disable PostgreSQL" OFF)
|
||||
endif(pg_FOUND)
|
||||
if (pg_FOUND OR MySQL_FOUND OR SQLite3_FOUND)
|
||||
if (pg_FOUND)
|
||||
option(USE_POSTGRESQL "Enable PostgreSQL" ON)
|
||||
else (pg_FOUND)
|
||||
option(USE_POSTGRESQL "Disable PostgreSQL" OFF)
|
||||
endif (pg_FOUND)
|
||||
|
||||
if(MySQL_FOUND)
|
||||
option(USE_MYSQL "Enable Mysql" ON)
|
||||
else(MySQL_FOUND)
|
||||
option(USE_MYSQL "DisableMysql" OFF)
|
||||
endif(MySQL_FOUND)
|
||||
if (MySQL_FOUND)
|
||||
option(USE_MYSQL "Enable Mysql" ON)
|
||||
else (MySQL_FOUND)
|
||||
option(USE_MYSQL "DisableMysql" OFF)
|
||||
endif (MySQL_FOUND)
|
||||
|
||||
if(SQLite3_FOUND)
|
||||
option(USE_SQLITE3 "Enable Sqlite3" ON)
|
||||
else(SQLite3_FOUND)
|
||||
option(USE_SQLITE3 "Disable Sqlite3" OFF)
|
||||
endif(SQLite3_FOUND)
|
||||
endif(pg_FOUND OR MySQL_FOUND OR SQLite3_FOUND)
|
||||
if (SQLite3_FOUND)
|
||||
option(USE_SQLITE3 "Enable Sqlite3" ON)
|
||||
else (SQLite3_FOUND)
|
||||
option(USE_SQLITE3 "Disable Sqlite3" OFF)
|
||||
endif (SQLite3_FOUND)
|
||||
endif (pg_FOUND OR MySQL_FOUND OR SQLite3_FOUND)
|
||||
|
||||
set(COMPILER_COMMAND ${CMAKE_CXX_COMPILER})
|
||||
set(COMPILER_ID ${CMAKE_CXX_COMPILER_ID})
|
||||
|
||||
if(CMAKE_BUILD_TYPE)
|
||||
string(TOLOWER ${CMAKE_BUILD_TYPE} _type)
|
||||
if(_type STREQUAL release)
|
||||
set(COMPILATION_FLAGS "${CMAKE_CXX_FLAGS_RELEASE} -std=c++")
|
||||
elseif(_type STREQUAL debug)
|
||||
set(COMPILATION_FLAGS "${CMAKE_CXX_FLAGS_DEBUG} -std=c++")
|
||||
else()
|
||||
if (CMAKE_BUILD_TYPE)
|
||||
string(TOLOWER ${CMAKE_BUILD_TYPE} _type)
|
||||
if (_type STREQUAL release)
|
||||
set(COMPILATION_FLAGS "${CMAKE_CXX_FLAGS_RELEASE} -std=c++")
|
||||
elseif (_type STREQUAL debug)
|
||||
set(COMPILATION_FLAGS "${CMAKE_CXX_FLAGS_DEBUG} -std=c++")
|
||||
else ()
|
||||
set(COMPILATION_FLAGS "-std=c++")
|
||||
endif ()
|
||||
else (CMAKE_BUILD_TYPE)
|
||||
set(COMPILATION_FLAGS "-std=c++")
|
||||
endif()
|
||||
else(CMAKE_BUILD_TYPE)
|
||||
set(COMPILATION_FLAGS "-std=c++")
|
||||
endif(CMAKE_BUILD_TYPE)
|
||||
endif (CMAKE_BUILD_TYPE)
|
||||
|
||||
list(APPEND INCLUDE_DIRS_FOR_DYNAMIC_VIEW
|
||||
"${CMAKE_INSTALL_PREFIX}/${INSTALL_INCLUDE_DIR}")
|
||||
"${CMAKE_INSTALL_PREFIX}/${INSTALL_INCLUDE_DIR}")
|
||||
list(REMOVE_DUPLICATES INCLUDE_DIRS_FOR_DYNAMIC_VIEW)
|
||||
set(INS_STRING "")
|
||||
foreach(loop_var ${INCLUDE_DIRS_FOR_DYNAMIC_VIEW})
|
||||
set(INS_STRING "${INS_STRING} -I${loop_var}")
|
||||
endforeach(loop_var)
|
||||
foreach (loop_var ${INCLUDE_DIRS_FOR_DYNAMIC_VIEW})
|
||||
set(INS_STRING "${INS_STRING} -I${loop_var}")
|
||||
endforeach (loop_var)
|
||||
|
||||
set(INCLUDING_DIRS ${INS_STRING})
|
||||
|
||||
configure_file(${PROJECT_SOURCE_DIR}/cmake/templates/config.h.in
|
||||
${PROJECT_BINARY_DIR}/drogon/config.h @ONLY)
|
||||
${PROJECT_BINARY_DIR}/drogon/config.h @ONLY)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_subdirectory(lib/tests)
|
||||
if(pg_FOUND)
|
||||
add_subdirectory(${PROJECT_SOURCE_DIR}/orm_lib/src/postgresql_impl/test)
|
||||
endif(pg_FOUND)
|
||||
if(MySQL_FOUND)
|
||||
add_subdirectory(${PROJECT_SOURCE_DIR}/orm_lib/src/mysql_impl/test)
|
||||
endif(MySQL_FOUND)
|
||||
if(SQLite3_FOUND)
|
||||
add_subdirectory(${PROJECT_SOURCE_DIR}/orm_lib/src/sqlite3_impl/test)
|
||||
endif(SQLite3_FOUND)
|
||||
if(pg_FOUND OR MySQL_FOUND OR SQLite3_FOUND)
|
||||
add_subdirectory(${PROJECT_SOURCE_DIR}/orm_lib/tests)
|
||||
endif(pg_FOUND OR MySQL_FOUND OR SQLite3_FOUND)
|
||||
find_package(GTest)
|
||||
if(GTest_FOUND)
|
||||
message(STATUS "gtest found")
|
||||
enable_testing()
|
||||
add_subdirectory(unittest)
|
||||
endif(GTest_FOUND)
|
||||
endif(BUILD_TESTING)
|
||||
if (BUILD_TESTING)
|
||||
add_subdirectory(lib/tests)
|
||||
if (pg_FOUND)
|
||||
add_subdirectory(${PROJECT_SOURCE_DIR}/orm_lib/src/postgresql_impl/test)
|
||||
endif (pg_FOUND)
|
||||
if (MySQL_FOUND)
|
||||
add_subdirectory(${PROJECT_SOURCE_DIR}/orm_lib/src/mysql_impl/test)
|
||||
endif (MySQL_FOUND)
|
||||
if (SQLite3_FOUND)
|
||||
add_subdirectory(${PROJECT_SOURCE_DIR}/orm_lib/src/sqlite3_impl/test)
|
||||
endif (SQLite3_FOUND)
|
||||
if (pg_FOUND OR MySQL_FOUND OR SQLite3_FOUND)
|
||||
add_subdirectory(${PROJECT_SOURCE_DIR}/orm_lib/tests)
|
||||
endif (pg_FOUND OR MySQL_FOUND OR SQLite3_FOUND)
|
||||
find_package(GTest)
|
||||
if (GTest_FOUND)
|
||||
message(STATUS "gtest found")
|
||||
enable_testing()
|
||||
add_subdirectory(unittest)
|
||||
endif (GTest_FOUND)
|
||||
endif (BUILD_TESTING)
|
||||
|
||||
# Installation
|
||||
|
||||
@ -383,79 +415,84 @@ install(TARGETS ${PROJECT_NAME}
|
||||
LIBRARY DESTINATION "${INSTALL_LIB_DIR}" COMPONENT lib)
|
||||
|
||||
set(DROGON_HEADERS
|
||||
lib/inc/drogon/Attribute.h
|
||||
lib/inc/drogon/CacheMap.h
|
||||
lib/inc/drogon/Cookie.h
|
||||
lib/inc/drogon/DrClassMap.h
|
||||
lib/inc/drogon/DrObject.h
|
||||
lib/inc/drogon/DrTemplate.h
|
||||
lib/inc/drogon/DrTemplateBase.h
|
||||
lib/inc/drogon/HttpAppFramework.h
|
||||
lib/inc/drogon/HttpBinder.h
|
||||
lib/inc/drogon/HttpClient.h
|
||||
lib/inc/drogon/HttpController.h
|
||||
lib/inc/drogon/HttpFilter.h
|
||||
lib/inc/drogon/HttpRequest.h
|
||||
lib/inc/drogon/HttpResponse.h
|
||||
lib/inc/drogon/HttpSimpleController.h
|
||||
lib/inc/drogon/HttpTypes.h
|
||||
lib/inc/drogon/HttpViewData.h
|
||||
lib/inc/drogon/IntranetIpFilter.h
|
||||
lib/inc/drogon/IOThreadStorage.h
|
||||
lib/inc/drogon/LocalHostFilter.h
|
||||
lib/inc/drogon/MultiPart.h
|
||||
lib/inc/drogon/NotFound.h
|
||||
lib/inc/drogon/Session.h
|
||||
lib/inc/drogon/UploadFile.h
|
||||
lib/inc/drogon/WebSocketClient.h
|
||||
lib/inc/drogon/WebSocketConnection.h
|
||||
lib/inc/drogon/WebSocketController.h
|
||||
lib/inc/drogon/drogon.h
|
||||
lib/inc/drogon/version.h
|
||||
lib/inc/drogon/drogon_callbacks.h
|
||||
lib/inc/drogon/PubSubService.h)
|
||||
lib/inc/drogon/Attribute.h
|
||||
lib/inc/drogon/CacheMap.h
|
||||
lib/inc/drogon/Cookie.h
|
||||
lib/inc/drogon/DrClassMap.h
|
||||
lib/inc/drogon/DrObject.h
|
||||
lib/inc/drogon/DrTemplate.h
|
||||
lib/inc/drogon/DrTemplateBase.h
|
||||
lib/inc/drogon/HttpAppFramework.h
|
||||
lib/inc/drogon/HttpBinder.h
|
||||
lib/inc/drogon/HttpClient.h
|
||||
lib/inc/drogon/HttpController.h
|
||||
lib/inc/drogon/HttpFilter.h
|
||||
lib/inc/drogon/HttpRequest.h
|
||||
lib/inc/drogon/HttpResponse.h
|
||||
lib/inc/drogon/HttpSimpleController.h
|
||||
lib/inc/drogon/HttpTypes.h
|
||||
lib/inc/drogon/HttpViewData.h
|
||||
lib/inc/drogon/IntranetIpFilter.h
|
||||
lib/inc/drogon/IOThreadStorage.h
|
||||
lib/inc/drogon/LocalHostFilter.h
|
||||
lib/inc/drogon/MultiPart.h
|
||||
lib/inc/drogon/NotFound.h
|
||||
lib/inc/drogon/Session.h
|
||||
lib/inc/drogon/UploadFile.h
|
||||
lib/inc/drogon/WebSocketClient.h
|
||||
lib/inc/drogon/WebSocketConnection.h
|
||||
lib/inc/drogon/WebSocketController.h
|
||||
lib/inc/drogon/drogon.h
|
||||
lib/inc/drogon/version.h
|
||||
lib/inc/drogon/drogon_callbacks.h
|
||||
lib/inc/drogon/PubSubService.h)
|
||||
install(FILES ${DROGON_HEADERS} DESTINATION ${INSTALL_INCLUDE_DIR}/drogon)
|
||||
|
||||
set(ORM_HEADERS
|
||||
orm_lib/inc/drogon/orm/ArrayParser.h
|
||||
orm_lib/inc/drogon/orm/Criteria.h
|
||||
orm_lib/inc/drogon/orm/DbClient.h
|
||||
orm_lib/inc/drogon/orm/DbTypes.h
|
||||
orm_lib/inc/drogon/orm/Exception.h
|
||||
orm_lib/inc/drogon/orm/Field.h
|
||||
orm_lib/inc/drogon/orm/FunctionTraits.h
|
||||
orm_lib/inc/drogon/orm/Mapper.h
|
||||
orm_lib/inc/drogon/orm/Result.h
|
||||
orm_lib/inc/drogon/orm/ResultIterator.h
|
||||
orm_lib/inc/drogon/orm/Row.h
|
||||
orm_lib/inc/drogon/orm/RowIterator.h
|
||||
orm_lib/inc/drogon/orm/SqlBinder.h
|
||||
orm_lib/inc/drogon/orm/RestfulController.h)
|
||||
orm_lib/inc/drogon/orm/ArrayParser.h
|
||||
orm_lib/inc/drogon/orm/Criteria.h
|
||||
orm_lib/inc/drogon/orm/DbClient.h
|
||||
orm_lib/inc/drogon/orm/DbTypes.h
|
||||
orm_lib/inc/drogon/orm/Exception.h
|
||||
orm_lib/inc/drogon/orm/Field.h
|
||||
orm_lib/inc/drogon/orm/FunctionTraits.h
|
||||
orm_lib/inc/drogon/orm/Mapper.h
|
||||
orm_lib/inc/drogon/orm/Result.h
|
||||
orm_lib/inc/drogon/orm/ResultIterator.h
|
||||
orm_lib/inc/drogon/orm/Row.h
|
||||
orm_lib/inc/drogon/orm/RowIterator.h
|
||||
orm_lib/inc/drogon/orm/SqlBinder.h
|
||||
orm_lib/inc/drogon/orm/RestfulController.h)
|
||||
install(FILES ${ORM_HEADERS} DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/orm)
|
||||
|
||||
set(NOSQL_HEADERS nosql_lib/redis/inc/drogon/nosql/RedisClient.h
|
||||
nosql_lib/redis/inc/drogon/nosql/RedisResult.h
|
||||
nosql_lib/redis/inc/drogon/nosql/RedisException.h)
|
||||
install(FILES ${NOSQL_HEADERS} DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/nosql)
|
||||
|
||||
set(DROGON_UTIL_HEADERS
|
||||
lib/inc/drogon/utils/FunctionTraits.h
|
||||
lib/inc/drogon/utils/Utilities.h
|
||||
lib/inc/drogon/utils/any.h
|
||||
lib/inc/drogon/utils/string_view.h
|
||||
lib/inc/drogon/utils/optional.h
|
||||
lib/inc/drogon/utils/coroutine.h
|
||||
lib/inc/drogon/utils/HttpConstraint.h
|
||||
lib/inc/drogon/utils/OStringStream.h)
|
||||
lib/inc/drogon/utils/FunctionTraits.h
|
||||
lib/inc/drogon/utils/Utilities.h
|
||||
lib/inc/drogon/utils/any.h
|
||||
lib/inc/drogon/utils/string_view.h
|
||||
lib/inc/drogon/utils/optional.h
|
||||
lib/inc/drogon/utils/coroutine.h
|
||||
lib/inc/drogon/utils/HttpConstraint.h
|
||||
lib/inc/drogon/utils/OStringStream.h)
|
||||
install(FILES ${DROGON_UTIL_HEADERS}
|
||||
DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/utils)
|
||||
|
||||
set(DROGON_PLUGIN_HEADERS lib/inc/drogon/plugins/Plugin.h
|
||||
lib/inc/drogon/plugins/SecureSSLRedirector.h)
|
||||
lib/inc/drogon/plugins/SecureSSLRedirector.h)
|
||||
install(FILES ${DROGON_PLUGIN_HEADERS}
|
||||
DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/plugins)
|
||||
|
||||
source_group("Public API"
|
||||
FILES
|
||||
${DROGON_HEADERS}
|
||||
${ORM_HEADERS}
|
||||
${DROGON_UTIL_HEADERS}
|
||||
${DROGON_PLUGIN_HEADERS})
|
||||
FILES
|
||||
${DROGON_HEADERS}
|
||||
${ORM_HEADERS}
|
||||
${DROGON_UTIL_HEADERS}
|
||||
${DROGON_PLUGIN_HEADERS})
|
||||
|
||||
# Export the package for use from the build-tree (this registers the build-tree
|
||||
# with a global cmake-registry) export(PACKAGE Drogon)
|
||||
@ -463,20 +500,20 @@ source_group("Public API"
|
||||
include(CMakePackageConfigHelpers)
|
||||
# ... for the install tree
|
||||
configure_package_config_file(
|
||||
cmake/templates/DrogonConfig.cmake.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/DrogonConfig.cmake
|
||||
INSTALL_DESTINATION
|
||||
${INSTALL_DROGON_CMAKE_DIR})
|
||||
cmake/templates/DrogonConfig.cmake.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/DrogonConfig.cmake
|
||||
INSTALL_DESTINATION
|
||||
${INSTALL_DROGON_CMAKE_DIR})
|
||||
|
||||
# version
|
||||
write_basic_package_version_file(
|
||||
${CMAKE_CURRENT_BINARY_DIR}/DrogonConfigVersion.cmake
|
||||
VERSION ${DROGON_VERSION}
|
||||
COMPATIBILITY SameMajorVersion)
|
||||
${CMAKE_CURRENT_BINARY_DIR}/DrogonConfigVersion.cmake
|
||||
VERSION ${DROGON_VERSION}
|
||||
COMPATIBILITY SameMajorVersion)
|
||||
|
||||
# Install the DrogonConfig.cmake and DrogonConfigVersion.cmake
|
||||
install(
|
||||
FILES "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/DrogonConfig.cmake"
|
||||
FILES "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/DrogonConfig.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/DrogonConfigVersion.cmake"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/FindUUID.cmake"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/FindJsoncpp.cmake"
|
||||
@ -485,9 +522,10 @@ install(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/Findpg.cmake"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/FindBrotli.cmake"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/Findcoz-profiler.cmake"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/FindHiredis.cmake"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/DrogonUtilities.cmake"
|
||||
DESTINATION "${INSTALL_DROGON_CMAKE_DIR}"
|
||||
COMPONENT dev)
|
||||
DESTINATION "${INSTALL_DROGON_CMAKE_DIR}"
|
||||
COMPONENT dev)
|
||||
|
||||
# Install the export set for use with the install-tree
|
||||
install(EXPORT DrogonTargets
|
||||
|
@ -36,6 +36,9 @@ endif()
|
||||
if(@COZ-PROFILER_FOUND@)
|
||||
find_dependency(coz-profiler)
|
||||
endif()
|
||||
if(@Hiredis_FOUND@)
|
||||
find_dependency(Hiredis)
|
||||
endif()
|
||||
|
||||
# Our library dependencies (contains definitions for IMPORTED targets)
|
||||
|
||||
|
40
cmake_modules/FindHiredis.cmake
Normal file
40
cmake_modules/FindHiredis.cmake
Normal file
@ -0,0 +1,40 @@
|
||||
# Try to find hiredis
|
||||
# Once done, this will define
|
||||
#
|
||||
# HIREDIS_FOUND - system has hiredis
|
||||
# HIREDIS_INCLUDE_DIRS - hiredis include directories
|
||||
# HIREDIS_LIBRARIES - libraries need to use hiredis
|
||||
|
||||
if (HIREDIS_INCLUDE_DIRS AND HIREDIS_LIBRARIES)
|
||||
set(HIREDIS_FIND_QUIETLY TRUE)
|
||||
else ()
|
||||
find_path(
|
||||
HIREDIS_INCLUDE_DIR
|
||||
NAMES hiredis/hiredis.h
|
||||
HINTS ${HIREDIS_ROOT_DIR}
|
||||
PATH_SUFFIXES include)
|
||||
|
||||
find_library(
|
||||
HIREDIS_LIBRARY
|
||||
NAMES hiredis
|
||||
HINTS ${HIREDIS_ROOT_DIR}
|
||||
PATH_SUFFIXES ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
set(HIREDIS_INCLUDE_DIRS ${HIREDIS_INCLUDE_DIR})
|
||||
set(HIREDIS_LIBRARIES ${HIREDIS_LIBRARY})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(
|
||||
Hiredis DEFAULT_MSG HIREDIS_LIBRARY HIREDIS_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(HIREDIS_LIBRARY HIREDIS_INCLUDE_DIR)
|
||||
endif ()
|
||||
|
||||
if(Hiredis_FOUND)
|
||||
add_library(Hiredis_lib INTERFACE IMPORTED)
|
||||
set_target_properties(Hiredis_lib
|
||||
PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
|
||||
"${HIREDIS_INCLUDE_DIRS}"
|
||||
INTERFACE_LINK_LIBRARIES
|
||||
"${HIREDIS_LIBRARIES}")
|
||||
endif(Hiredis_FOUND)
|
@ -52,15 +52,33 @@
|
||||
//client_encoding: The character set used by the client. it is empty string by default which
|
||||
//means use the default character set.
|
||||
//"client_encoding": "",
|
||||
//connection_number: 1 by default, if the 'is_fast' is true, the number is the number of
|
||||
//number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
|
||||
//connections per IO thread, otherwise it is the total number of all connections.
|
||||
"connection_number": 1
|
||||
"number_of_connections": 1
|
||||
}
|
||||
],
|
||||
"redis_clients": [
|
||||
{
|
||||
//name: Name of the client,'default' by default
|
||||
//"name":"",
|
||||
//host: Server IP, 127.0.0.1 by default
|
||||
"host": "127.0.0.1",
|
||||
//port: Server port, 6379 by default
|
||||
"port": 6379,
|
||||
//passwd: '' by default
|
||||
"passwd": "",
|
||||
//is_fast: false by default, if it is true, the client is faster but user can't call
|
||||
//any synchronous interface of it.
|
||||
"is_fast": false,
|
||||
//number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
|
||||
//connections per IO thread, otherwise it is the total number of all connections.
|
||||
"number_of_connections": 1
|
||||
}
|
||||
],*/
|
||||
"app": {
|
||||
//threads_num: The number of IO threads, 1 by default, if the value is set to 0, the number of threads
|
||||
//number_of_threads: The number of IO threads, 1 by default, if the value is set to 0, the number of threads
|
||||
//is the number of CPU cores
|
||||
"threads_num": 1,
|
||||
"number_of_threads": 1,
|
||||
//enable_session: False by default
|
||||
"enable_session": true,
|
||||
"session_timeout": 0,
|
||||
@ -128,9 +146,9 @@
|
||||
"filters": []
|
||||
}
|
||||
],
|
||||
//max_connections: maximum connections number,100000 by default
|
||||
//max_connections: maximum number of connections, 100000 by default
|
||||
"max_connections": 100000,
|
||||
//max_connections_per_ip: maximum connections number per clinet,0 by default which means no limit
|
||||
//max_connections_per_ip: maximum number of connections per clinet, 0 by default which means no limit
|
||||
"max_connections_per_ip": 0,
|
||||
//Load_dynamic_views: False by default, when set to true, drogon
|
||||
//compiles and loads dynamically "CSP View Files" in directories defined
|
||||
|
@ -52,15 +52,33 @@
|
||||
//client_encoding: The character set used by the client. it is empty string by default which
|
||||
//means use the default character set.
|
||||
//"client_encoding": "",
|
||||
//connection_number: 1 by default, if the 'is_fast' is true, the number is the number of
|
||||
//number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
|
||||
//connections per IO thread, otherwise it is the total number of all connections.
|
||||
"connection_number": 1
|
||||
"number_of_connections": 1
|
||||
}
|
||||
],
|
||||
"redis_clients": [
|
||||
{
|
||||
//name: Name of the client,'default' by default
|
||||
//"name":"",
|
||||
//host: Server IP, 127.0.0.1 by default
|
||||
"host": "127.0.0.1",
|
||||
//port: Server port, 6379 by default
|
||||
"port": 6379,
|
||||
//passwd: '' by default
|
||||
"passwd": "",
|
||||
//is_fast: false by default, if it is true, the client is faster but user can't call
|
||||
//any synchronous interface of it.
|
||||
"is_fast": false,
|
||||
//number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
|
||||
//connections per IO thread, otherwise it is the total number of all connections.
|
||||
"number_of_connections": 1
|
||||
}
|
||||
],*/
|
||||
"app": {
|
||||
//threads_num: The number of IO threads, 1 by default, if the value is set to 0, the number of threads
|
||||
//number_of_threads: The number of IO threads, 1 by default, if the value is set to 0, the number of threads
|
||||
//is the number of CPU cores
|
||||
"threads_num": 1,
|
||||
"number_of_threads": 1,
|
||||
//enable_session: False by default
|
||||
"enable_session": false,
|
||||
"session_timeout": 0,
|
||||
@ -128,9 +146,9 @@
|
||||
"filters": []
|
||||
}
|
||||
],
|
||||
//max_connections: maximum connections number,100000 by default
|
||||
//max_connections: maximum number of connections, 100000 by default
|
||||
"max_connections": 100000,
|
||||
//max_connections_per_ip: maximum connections number per clinet,0 by default which means no limit
|
||||
//max_connections_per_ip: maximum number of connections per clinet, 0 by default which means no limit
|
||||
"max_connections_per_ip": 0,
|
||||
//Load_dynamic_views: False by default, when set to true, drogon
|
||||
//compiles and loads dynamically "CSP View Files" in directories defined
|
||||
|
@ -56,6 +56,11 @@ void version::handleCommand(std::vector<std::string> ¶meters)
|
||||
std::cout << " boost: yes\n";
|
||||
#else
|
||||
std::cout << " boost: no\n";
|
||||
#endif
|
||||
#ifdef USE_REDIS
|
||||
std::cout << " hiredis: yes\n";
|
||||
#else
|
||||
std::cout << " hiredis: no\n";
|
||||
#endif
|
||||
std::cout << " c-ares: "
|
||||
<< (trantor::Resolver::isCAresUsed() ? "yes\n" : "no\n");
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#find lib orm_lib examples drogon_ctl -name *.h -o -name *.cc -exec dos2unix {} \;
|
||||
find lib orm_lib examples drogon_ctl -name *.h -o -name *.cc|xargs clang-format -i -style=file
|
||||
find lib orm_lib nosql_lib examples drogon_ctl -name *.h -o -name *.cc|xargs clang-format -i -style=file
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <drogon/HttpRequest.h>
|
||||
#include <drogon/HttpResponse.h>
|
||||
#include <drogon/orm/DbClient.h>
|
||||
#include <drogon/nosql/RedisClient.h>
|
||||
#include <trantor/net/Resolver.h>
|
||||
#include <trantor/net/EventLoop.h>
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
@ -59,7 +60,7 @@ class WebSocketControllerBase;
|
||||
class HttpAppFramework : public trantor::NonCopyable
|
||||
{
|
||||
public:
|
||||
virtual ~HttpAppFramework();
|
||||
virtual ~HttpAppFramework() = default;
|
||||
/// Get the instance of HttpAppFramework
|
||||
/**
|
||||
* HttpAppFramework works at singleton mode, so any calling of this
|
||||
@ -109,7 +110,7 @@ class HttpAppFramework : public trantor::NonCopyable
|
||||
* @note
|
||||
* The event loop is one of the network IO loops. Use the loop
|
||||
* for events/actions rather then the main thread.
|
||||
* REMAKRS : Function assumed the number of threads will not exceed 2^32.
|
||||
* REMARKS : Function assumed the number of threads will not exceed 2^32.
|
||||
* Change to long long for alien computers.
|
||||
*/
|
||||
virtual trantor::EventLoop *getIOLoop(size_t id) const = 0;
|
||||
@ -200,7 +201,7 @@ class HttpAppFramework : public trantor::NonCopyable
|
||||
* Users can use the callback to modify the response if they want.
|
||||
* @note This advice is called before any subsequent operation on the
|
||||
* response is performed by drogon or applications, so some modification
|
||||
* (e.g. modification on the status code) in this callback may be overrided
|
||||
* (e.g. modification on the status code) in this callback may be override
|
||||
* by subsequent operations.
|
||||
* @return HttpAppFramework&
|
||||
*/
|
||||
@ -282,7 +283,7 @@ class HttpAppFramework : public trantor::NonCopyable
|
||||
|
||||
/// Register an advice called after routing
|
||||
/**
|
||||
* @param advice is called immediately after the request matchs a handler
|
||||
* @param advice is called immediately after the request matches a handler
|
||||
* path and before any 'doFilter' method of filters applies. The parameters
|
||||
* of the advice are same as those of the doFilter method of the Filter
|
||||
* class.
|
||||
@ -507,7 +508,7 @@ class HttpAppFramework : public trantor::NonCopyable
|
||||
*/
|
||||
virtual HttpAppFramework ®isterWebSocketController(
|
||||
const std::string &pathName,
|
||||
const std::string &crtlName,
|
||||
const std::string &ctrlName,
|
||||
const std::vector<internal::HttpConstraint> &filtersAndMethods =
|
||||
std::vector<internal::HttpConstraint>{}) = 0;
|
||||
|
||||
@ -563,7 +564,7 @@ class HttpAppFramework : public trantor::NonCopyable
|
||||
HttpAppFramework ®isterFilter(const std::shared_ptr<T> &filterPtr)
|
||||
{
|
||||
static_assert(std::is_base_of<HttpFilterBase, T>::value,
|
||||
"Error! Only fitler objects can be registered here");
|
||||
"Error! Only filter objects can be registered here");
|
||||
static_assert(!T::isAutoCreation,
|
||||
"Filters created and initialized "
|
||||
"automatically by drogon cannot be "
|
||||
@ -696,7 +697,7 @@ class HttpAppFramework : public trantor::NonCopyable
|
||||
*/
|
||||
virtual HttpAppFramework &disableSession() = 0;
|
||||
|
||||
/// Set the root path of HTTP document, defaut path is ./
|
||||
/// Set the root path of HTTP document, default path is ./
|
||||
/**
|
||||
* @note
|
||||
* This operation can be performed by an option in the configuration file.
|
||||
@ -771,7 +772,7 @@ class HttpAppFramework : public trantor::NonCopyable
|
||||
/// Enable supporting for dynamic views loading.
|
||||
/**
|
||||
*
|
||||
* @param libPaths is a vactor that contains paths to view files.
|
||||
* @param libPaths is a vector that contains paths to view files.
|
||||
*
|
||||
* @param outputPath is the directory where the output source files locate. if
|
||||
* it is set to an empty string, drogon use libPaths as output paths. If the
|
||||
@ -1138,6 +1139,22 @@ class HttpAppFramework : public trantor::NonCopyable
|
||||
*/
|
||||
virtual bool areAllDbClientsAvailable() const noexcept = 0;
|
||||
|
||||
/// Get a redis client by name
|
||||
/**
|
||||
* @note
|
||||
* This method must be called after the framework has been run.
|
||||
*/
|
||||
virtual nosql::RedisClientPtr getRedisClient(
|
||||
const std::string &name = "default") = 0;
|
||||
|
||||
/// Get a 'fast' redis client by name
|
||||
/**
|
||||
* @note
|
||||
* This method must be called after the framework has been run.
|
||||
*/
|
||||
virtual nosql::RedisClientPtr getFastRedisClient(
|
||||
const std::string &name = "default") = 0;
|
||||
|
||||
/**
|
||||
* @brief This method is to enable or disable the unicode escaping (\u) in
|
||||
* the json string of HTTP responses or requests. it works (disable
|
||||
@ -1204,6 +1221,26 @@ class HttpAppFramework : public trantor::NonCopyable
|
||||
const bool isFast = false,
|
||||
const std::string &characterSet = "") = 0;
|
||||
|
||||
/// Create a redis client
|
||||
/**
|
||||
* @param ip IP of redis server.
|
||||
* @param port The port on which the redis server is listening.
|
||||
* @param name The client name.
|
||||
* @param password Password for the redis server
|
||||
* @param connectionNum The number of connections to the redis server.
|
||||
* @param isFast Indicates if the client is a fast database client.
|
||||
*
|
||||
* @note
|
||||
* This operation can be performed by an option in the configuration file.
|
||||
*/
|
||||
virtual HttpAppFramework &createRedisClient(
|
||||
const std::string &ip,
|
||||
unsigned short port,
|
||||
const std::string &name = "default",
|
||||
const std::string &password = "",
|
||||
size_t connectionNum = 1,
|
||||
bool isFast = false) = 0;
|
||||
|
||||
/// Get the DNS resolver
|
||||
/**
|
||||
* @note
|
||||
|
@ -413,9 +413,9 @@ struct AsyncTask final
|
||||
}
|
||||
};
|
||||
|
||||
/// Helper class that provices the infrastructure for turning callback into
|
||||
/// corourines
|
||||
// The user is responsible to fill in `await_suspend()` and construtors.
|
||||
/// Helper class that provides the infrastructure for turning callback into
|
||||
/// coroutines
|
||||
// The user is responsible to fill in `await_suspend()` and constructors.
|
||||
template <typename T = void>
|
||||
struct CallbackAwaiter
|
||||
{
|
||||
@ -427,7 +427,7 @@ struct CallbackAwaiter
|
||||
const T &await_resume() const noexcept(false)
|
||||
{
|
||||
// await_resume() should always be called after co_await
|
||||
// (await_suspend()) is called. Therefor the value should always be set
|
||||
// (await_suspend()) is called. Therefore the value should always be set
|
||||
// (or there's an exception)
|
||||
assert(result_.has_value() == true || exception_ != nullptr);
|
||||
|
||||
@ -437,7 +437,7 @@ struct CallbackAwaiter
|
||||
}
|
||||
|
||||
private:
|
||||
// HACK: Not all desired types are default contructable. But we need the
|
||||
// HACK: Not all desired types are default constructable. But we need the
|
||||
// entire struct to be constructed for awaiting. std::optional takes care of
|
||||
// that.
|
||||
optional<T> result_;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
*
|
||||
* string_view.h
|
||||
* An Tao
|
||||
* @file string_view.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
|
@ -231,6 +231,10 @@ static void loadApp(const Json::Value &app)
|
||||
return;
|
||||
// threads number
|
||||
auto threadsNum = app.get("threads_num", 1).asUInt64();
|
||||
if (threadsNum == 1)
|
||||
{
|
||||
threadsNum = app.get("number_of_threads", 1).asUInt64();
|
||||
}
|
||||
if (threadsNum == 0)
|
||||
{
|
||||
// set the number to the number of processors.
|
||||
@ -497,6 +501,10 @@ static void loadDbClients(const Json::Value &dbClients)
|
||||
password = client.get("password", "").asString();
|
||||
}
|
||||
auto connNum = client.get("connection_number", 1).asUInt();
|
||||
if (connNum == 1)
|
||||
{
|
||||
connNum = client.get("number_of_connections", 1).asUInt();
|
||||
}
|
||||
auto name = client.get("name", "default").asString();
|
||||
auto filename = client.get("filename", "").asString();
|
||||
auto isFast = client.get("is_fast", false).asBool();
|
||||
@ -518,6 +526,32 @@ static void loadDbClients(const Json::Value &dbClients)
|
||||
characterSet);
|
||||
}
|
||||
}
|
||||
|
||||
static void loadRedisClients(const Json::Value &redisClients)
|
||||
{
|
||||
if (!redisClients)
|
||||
return;
|
||||
for (auto const &client : redisClients)
|
||||
{
|
||||
auto host = client.get("host", "127.0.0.1").asString();
|
||||
auto port = client.get("port", 6379).asUInt();
|
||||
auto password = client.get("passwd", "").asString();
|
||||
if (password.empty())
|
||||
{
|
||||
password = client.get("password", "").asString();
|
||||
}
|
||||
auto connNum = client.get("connection_number", 1).asUInt();
|
||||
if (connNum == 1)
|
||||
{
|
||||
connNum = client.get("number_of_connections", 1).asUInt();
|
||||
}
|
||||
auto name = client.get("name", "default").asString();
|
||||
auto isFast = client.get("is_fast", false).asBool();
|
||||
drogon::app().createRedisClient(
|
||||
host, port, name, password, connNum, isFast);
|
||||
}
|
||||
}
|
||||
|
||||
static void loadListeners(const Json::Value &listeners)
|
||||
{
|
||||
if (!listeners)
|
||||
@ -550,4 +584,5 @@ void ConfigLoader::load()
|
||||
loadSSL(configJsonRoot_["ssl"]);
|
||||
loadListeners(configJsonRoot_["listeners"]);
|
||||
loadDbClients(configJsonRoot_["db_clients"]);
|
||||
loadRedisClients(configJsonRoot_["redis_clients"]);
|
||||
}
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "SharedLibManager.h"
|
||||
#include "SessionManager.h"
|
||||
#include "DbClientManager.h"
|
||||
#include "RedisClientManager.h"
|
||||
#include <drogon/config.h>
|
||||
#include <algorithm>
|
||||
#include <drogon/version.h>
|
||||
@ -86,6 +87,7 @@ HttpAppFrameworkImpl::HttpAppFrameworkImpl()
|
||||
listenerManagerPtr_(new ListenerManager),
|
||||
pluginsManagerPtr_(new PluginsManager),
|
||||
dbClientManagerPtr_(new orm::DbClientManager),
|
||||
redisClientManagerPtr_(new nosql::RedisClientManager),
|
||||
uploadPath_(rootPath_ + "uploads")
|
||||
{
|
||||
}
|
||||
@ -116,7 +118,7 @@ HttpResponsePtr defaultErrorHandler(HttpStatusCode code)
|
||||
return std::make_shared<HttpResponseImpl>(code, CT_TEXT_HTML);
|
||||
}
|
||||
|
||||
static void godaemon(void)
|
||||
static void godaemon()
|
||||
{
|
||||
printf("Initializing daemon mode\n");
|
||||
#ifndef _WIN32
|
||||
@ -374,7 +376,7 @@ HttpAppFramework &HttpAppFrameworkImpl::setLogPath(
|
||||
const std::string &logfileBaseName,
|
||||
size_t logfileSize)
|
||||
{
|
||||
if (logPath == "")
|
||||
if (logPath.empty())
|
||||
return *this;
|
||||
#ifdef _WIN32
|
||||
if (_access(logPath.c_str(), 0) != 0)
|
||||
@ -482,7 +484,7 @@ void HttpAppFrameworkImpl::run()
|
||||
else
|
||||
{
|
||||
std::string baseName = logfileBaseName_;
|
||||
if (baseName == "")
|
||||
if (baseName.empty())
|
||||
{
|
||||
baseName = "drogon";
|
||||
}
|
||||
@ -504,8 +506,9 @@ void HttpAppFrameworkImpl::run()
|
||||
#ifndef _WIN32
|
||||
if (!libFilePaths_.empty())
|
||||
{
|
||||
sharedLibManagerPtr_ = std::unique_ptr<SharedLibManager>(
|
||||
new SharedLibManager(libFilePaths_, libFileOutputPath_));
|
||||
sharedLibManagerPtr_ =
|
||||
std::make_unique<SharedLibManager>(libFilePaths_,
|
||||
libFileOutputPath_);
|
||||
}
|
||||
#endif
|
||||
// Create all listeners.
|
||||
@ -528,11 +531,11 @@ void HttpAppFrameworkImpl::run()
|
||||
// loop, so put the main loop into ioLoops.
|
||||
ioLoops.push_back(getLoop());
|
||||
dbClientManagerPtr_->createDbClients(ioLoops);
|
||||
|
||||
redisClientManagerPtr_->createRedisClients(ioLoops);
|
||||
if (useSession_)
|
||||
{
|
||||
sessionManagerPtr_ = std::unique_ptr<SessionManager>(
|
||||
new SessionManager(getLoop(), sessionTimeout_));
|
||||
sessionManagerPtr_ =
|
||||
std::make_unique<SessionManager>(getLoop(), sessionTimeout_);
|
||||
}
|
||||
|
||||
// Initialize plugins
|
||||
@ -852,9 +855,6 @@ HttpAppFramework &HttpAppFramework::instance()
|
||||
return HttpAppFrameworkImpl::instance();
|
||||
}
|
||||
|
||||
HttpAppFramework::~HttpAppFramework()
|
||||
{
|
||||
}
|
||||
void HttpAppFrameworkImpl::forward(
|
||||
const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
@ -926,6 +926,16 @@ orm::DbClientPtr HttpAppFrameworkImpl::getFastDbClient(const std::string &name)
|
||||
{
|
||||
return dbClientManagerPtr_->getFastDbClient(name);
|
||||
}
|
||||
nosql::RedisClientPtr HttpAppFrameworkImpl::getRedisClient(
|
||||
const std::string &name)
|
||||
{
|
||||
return redisClientManagerPtr_->getRedisClient(name);
|
||||
}
|
||||
nosql::RedisClientPtr HttpAppFrameworkImpl::getFastRedisClient(
|
||||
const std::string &name)
|
||||
{
|
||||
return redisClientManagerPtr_->getFastRedisClient(name);
|
||||
}
|
||||
HttpAppFramework &HttpAppFrameworkImpl::createDbClient(
|
||||
const std::string &dbType,
|
||||
const std::string &host,
|
||||
@ -954,6 +964,19 @@ HttpAppFramework &HttpAppFrameworkImpl::createDbClient(
|
||||
return *this;
|
||||
}
|
||||
|
||||
HttpAppFramework &HttpAppFrameworkImpl::createRedisClient(
|
||||
const std::string &ip,
|
||||
unsigned short port,
|
||||
const std::string &name,
|
||||
const std::string &password,
|
||||
size_t connectionNum,
|
||||
bool isFast)
|
||||
{
|
||||
assert(!running_);
|
||||
redisClientManagerPtr_->createRedisClient(
|
||||
name, ip, port, password, connectionNum, isFast);
|
||||
return *this;
|
||||
}
|
||||
void HttpAppFrameworkImpl::quit()
|
||||
{
|
||||
if (getLoop()->isRunning())
|
||||
|
@ -38,44 +38,44 @@ struct InitBeforeMainFunction
|
||||
}
|
||||
};
|
||||
|
||||
class HttpAppFrameworkImpl : public HttpAppFramework
|
||||
class HttpAppFrameworkImpl final : public HttpAppFramework
|
||||
{
|
||||
public:
|
||||
HttpAppFrameworkImpl();
|
||||
|
||||
virtual const Json::Value &getCustomConfig() const override
|
||||
const Json::Value &getCustomConfig() const override
|
||||
{
|
||||
return jsonConfig_["custom_config"];
|
||||
}
|
||||
|
||||
virtual PluginBase *getPlugin(const std::string &name) override;
|
||||
virtual HttpAppFramework &addListener(const std::string &ip,
|
||||
uint16_t port,
|
||||
bool useSSL = false,
|
||||
const std::string &certFile = "",
|
||||
const std::string &keyFile = "",
|
||||
bool useOldTLS = false) override;
|
||||
virtual HttpAppFramework &setThreadNum(size_t threadNum) override;
|
||||
virtual size_t getThreadNum() const override
|
||||
PluginBase *getPlugin(const std::string &name) override;
|
||||
HttpAppFramework &addListener(const std::string &ip,
|
||||
uint16_t port,
|
||||
bool useSSL,
|
||||
const std::string &certFile,
|
||||
const std::string &keyFile,
|
||||
bool useOldTLS) override;
|
||||
HttpAppFramework &setThreadNum(size_t threadNum) override;
|
||||
size_t getThreadNum() const override
|
||||
{
|
||||
return threadNum_;
|
||||
}
|
||||
virtual HttpAppFramework &setSSLFiles(const std::string &certPath,
|
||||
const std::string &keyPath) override;
|
||||
virtual void run() override;
|
||||
virtual HttpAppFramework ®isterWebSocketController(
|
||||
HttpAppFramework &setSSLFiles(const std::string &certPath,
|
||||
const std::string &keyPath) override;
|
||||
void run() override;
|
||||
HttpAppFramework ®isterWebSocketController(
|
||||
const std::string &pathName,
|
||||
const std::string &crtlName,
|
||||
const std::vector<internal::HttpConstraint> &filtersAndMethods =
|
||||
std::vector<internal::HttpConstraint>{}) override;
|
||||
virtual HttpAppFramework ®isterHttpSimpleController(
|
||||
const std::string &ctrlName,
|
||||
const std::vector<internal::HttpConstraint> &filtersAndMethods)
|
||||
override;
|
||||
HttpAppFramework ®isterHttpSimpleController(
|
||||
const std::string &pathName,
|
||||
const std::string &crtlName,
|
||||
const std::vector<internal::HttpConstraint> &filtersAndMethods =
|
||||
std::vector<internal::HttpConstraint>{}) override;
|
||||
const std::string &ctrlName,
|
||||
const std::vector<internal::HttpConstraint> &filtersAndMethods)
|
||||
override;
|
||||
|
||||
virtual HttpAppFramework &setCustom404Page(const HttpResponsePtr &resp,
|
||||
bool set404) override
|
||||
HttpAppFramework &setCustom404Page(const HttpResponsePtr &resp,
|
||||
bool set404) override
|
||||
{
|
||||
if (set404)
|
||||
{
|
||||
@ -91,25 +91,24 @@ class HttpAppFrameworkImpl : public HttpAppFramework
|
||||
|
||||
const HttpResponsePtr &getCustom404Page();
|
||||
|
||||
virtual void forward(
|
||||
const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
const std::string &hostString = "",
|
||||
double timeout = 0) override;
|
||||
void forward(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
const std::string &hostString,
|
||||
double timeout) override;
|
||||
|
||||
void forward(const HttpRequestImplPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
const std::string &hostString,
|
||||
double timeout = 0);
|
||||
|
||||
virtual HttpAppFramework ®isterBeginningAdvice(
|
||||
HttpAppFramework ®isterBeginningAdvice(
|
||||
const std::function<void()> &advice) override
|
||||
{
|
||||
beginningAdvices_.emplace_back(advice);
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual HttpAppFramework ®isterNewConnectionAdvice(
|
||||
HttpAppFramework ®isterNewConnectionAdvice(
|
||||
const std::function<bool(const trantor::InetAddress &,
|
||||
const trantor::InetAddress &)> &advice)
|
||||
override
|
||||
@ -118,7 +117,7 @@ class HttpAppFrameworkImpl : public HttpAppFramework
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual HttpAppFramework ®isterHttpResponseCreationAdvice(
|
||||
HttpAppFramework ®isterHttpResponseCreationAdvice(
|
||||
const std::function<void(const HttpResponsePtr &)> &advice) override
|
||||
{
|
||||
responseCreationAdvices_.emplace_back(advice);
|
||||
@ -131,14 +130,14 @@ class HttpAppFrameworkImpl : public HttpAppFramework
|
||||
return responseCreationAdvices_;
|
||||
}
|
||||
|
||||
virtual HttpAppFramework ®isterSyncAdvice(
|
||||
HttpAppFramework ®isterSyncAdvice(
|
||||
const std::function<HttpResponsePtr(const HttpRequestPtr &)> &advice)
|
||||
override
|
||||
{
|
||||
syncAdvices_.emplace_back(advice);
|
||||
return *this;
|
||||
}
|
||||
virtual HttpAppFramework ®isterPreRoutingAdvice(
|
||||
HttpAppFramework ®isterPreRoutingAdvice(
|
||||
const std::function<void(const HttpRequestPtr &,
|
||||
AdviceCallback &&,
|
||||
AdviceChainCallback &&)> &advice) override
|
||||
@ -146,7 +145,7 @@ class HttpAppFrameworkImpl : public HttpAppFramework
|
||||
preRoutingAdvices_.emplace_back(advice);
|
||||
return *this;
|
||||
}
|
||||
virtual HttpAppFramework ®isterPostRoutingAdvice(
|
||||
HttpAppFramework ®isterPostRoutingAdvice(
|
||||
const std::function<void(const HttpRequestPtr &,
|
||||
AdviceCallback &&,
|
||||
AdviceChainCallback &&)> &advice) override
|
||||
@ -154,7 +153,7 @@ class HttpAppFrameworkImpl : public HttpAppFramework
|
||||
postRoutingAdvices_.emplace_back(advice);
|
||||
return *this;
|
||||
}
|
||||
virtual HttpAppFramework ®isterPreHandlingAdvice(
|
||||
HttpAppFramework ®isterPreHandlingAdvice(
|
||||
const std::function<void(const HttpRequestPtr &,
|
||||
AdviceCallback &&,
|
||||
AdviceChainCallback &&)> &advice) override
|
||||
@ -163,25 +162,25 @@ class HttpAppFrameworkImpl : public HttpAppFramework
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual HttpAppFramework ®isterPreRoutingAdvice(
|
||||
HttpAppFramework ®isterPreRoutingAdvice(
|
||||
const std::function<void(const HttpRequestPtr &)> &advice) override
|
||||
{
|
||||
preRoutingObservers_.emplace_back(advice);
|
||||
return *this;
|
||||
}
|
||||
virtual HttpAppFramework ®isterPostRoutingAdvice(
|
||||
HttpAppFramework ®isterPostRoutingAdvice(
|
||||
const std::function<void(const HttpRequestPtr &)> &advice) override
|
||||
{
|
||||
postRoutingObservers_.emplace_back(advice);
|
||||
return *this;
|
||||
}
|
||||
virtual HttpAppFramework ®isterPreHandlingAdvice(
|
||||
HttpAppFramework ®isterPreHandlingAdvice(
|
||||
const std::function<void(const HttpRequestPtr &)> &advice) override
|
||||
{
|
||||
preHandlingObservers_.emplace_back(advice);
|
||||
return *this;
|
||||
}
|
||||
virtual HttpAppFramework ®isterPostHandlingAdvice(
|
||||
HttpAppFramework ®isterPostHandlingAdvice(
|
||||
const std::function<void(const HttpRequestPtr &,
|
||||
const HttpResponsePtr &)> &advice) override
|
||||
{
|
||||
@ -189,152 +188,140 @@ class HttpAppFrameworkImpl : public HttpAppFramework
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual HttpAppFramework &enableSession(const size_t timeout = 0) override
|
||||
HttpAppFramework &enableSession(const size_t timeout) override
|
||||
{
|
||||
useSession_ = true;
|
||||
sessionTimeout_ = timeout;
|
||||
return *this;
|
||||
}
|
||||
virtual HttpAppFramework &disableSession() override
|
||||
HttpAppFramework &disableSession() override
|
||||
{
|
||||
useSession_ = false;
|
||||
return *this;
|
||||
}
|
||||
virtual const std::string &getDocumentRoot() const override
|
||||
const std::string &getDocumentRoot() const override
|
||||
{
|
||||
return rootPath_;
|
||||
}
|
||||
virtual HttpAppFramework &setDocumentRoot(
|
||||
const std::string &rootPath) override
|
||||
HttpAppFramework &setDocumentRoot(const std::string &rootPath) override
|
||||
{
|
||||
rootPath_ = rootPath;
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual HttpAppFramework &setStaticFileHeaders(
|
||||
HttpAppFramework &setStaticFileHeaders(
|
||||
const std::vector<std::pair<std::string, std::string>> &headers)
|
||||
override;
|
||||
|
||||
virtual HttpAppFramework &addALocation(
|
||||
HttpAppFramework &addALocation(
|
||||
const std::string &uriPrefix,
|
||||
const std::string &defaultContentType = "",
|
||||
const std::string &alias = "",
|
||||
bool isCaseSensitive = false,
|
||||
bool allowAll = true,
|
||||
bool isRecursive = true,
|
||||
const std::vector<std::string> &filters = {}) override;
|
||||
const std::string &defaultContentType,
|
||||
const std::string &alias,
|
||||
bool isCaseSensitive,
|
||||
bool allowAll,
|
||||
bool isRecursive,
|
||||
const std::vector<std::string> &filters) override;
|
||||
|
||||
virtual const std::string &getUploadPath() const override
|
||||
const std::string &getUploadPath() const override
|
||||
{
|
||||
return uploadPath_;
|
||||
}
|
||||
virtual const std::shared_ptr<trantor::Resolver> &getResolver()
|
||||
const override
|
||||
const std::shared_ptr<trantor::Resolver> &getResolver() const override
|
||||
{
|
||||
static auto resolver = trantor::Resolver::newResolver(getLoop());
|
||||
return resolver;
|
||||
}
|
||||
virtual HttpAppFramework &setUploadPath(
|
||||
const std::string &uploadPath) override;
|
||||
virtual HttpAppFramework &setFileTypes(
|
||||
HttpAppFramework &setUploadPath(const std::string &uploadPath) override;
|
||||
HttpAppFramework &setFileTypes(
|
||||
const std::vector<std::string> &types) override;
|
||||
#ifndef _WIN32
|
||||
virtual HttpAppFramework &enableDynamicViewsLoading(
|
||||
HttpAppFramework &enableDynamicViewsLoading(
|
||||
const std::vector<std::string> &libPaths,
|
||||
const std::string &outputPath) override;
|
||||
#endif
|
||||
virtual HttpAppFramework &setMaxConnectionNum(
|
||||
size_t maxConnections) override;
|
||||
virtual HttpAppFramework &setMaxConnectionNumPerIP(
|
||||
HttpAppFramework &setMaxConnectionNum(size_t maxConnections) override;
|
||||
HttpAppFramework &setMaxConnectionNumPerIP(
|
||||
size_t maxConnectionsPerIP) override;
|
||||
virtual HttpAppFramework &loadConfigFile(
|
||||
const std::string &fileName) override;
|
||||
virtual HttpAppFramework &loadConfigJson(const Json::Value &data) override;
|
||||
virtual HttpAppFramework &loadConfigJson(Json::Value &&data) override;
|
||||
virtual HttpAppFramework &enableRunAsDaemon() override
|
||||
HttpAppFramework &loadConfigFile(const std::string &fileName) override;
|
||||
HttpAppFramework &loadConfigJson(const Json::Value &data) override;
|
||||
HttpAppFramework &loadConfigJson(Json::Value &&data) override;
|
||||
HttpAppFramework &enableRunAsDaemon() override
|
||||
{
|
||||
runAsDaemon_ = true;
|
||||
return *this;
|
||||
}
|
||||
virtual HttpAppFramework &disableSigtermHandling() override
|
||||
HttpAppFramework &disableSigtermHandling() override
|
||||
{
|
||||
handleSigterm_ = false;
|
||||
return *this;
|
||||
}
|
||||
virtual HttpAppFramework &enableRelaunchOnError() override
|
||||
HttpAppFramework &enableRelaunchOnError() override
|
||||
{
|
||||
relaunchOnError_ = true;
|
||||
return *this;
|
||||
}
|
||||
virtual HttpAppFramework &setLogPath(
|
||||
const std::string &logPath,
|
||||
const std::string &logfileBaseName = "",
|
||||
size_t logfileSize = 100000000) override;
|
||||
virtual HttpAppFramework &setLogLevel(
|
||||
trantor::Logger::LogLevel level) override;
|
||||
virtual HttpAppFramework &enableSendfile(bool sendFile) override
|
||||
HttpAppFramework &setLogPath(const std::string &logPath,
|
||||
const std::string &logfileBaseName,
|
||||
size_t logfileSize) override;
|
||||
HttpAppFramework &setLogLevel(trantor::Logger::LogLevel level) override;
|
||||
HttpAppFramework &enableSendfile(bool sendFile) override
|
||||
{
|
||||
useSendfile_ = sendFile;
|
||||
return *this;
|
||||
}
|
||||
virtual HttpAppFramework &enableGzip(bool useGzip) override
|
||||
HttpAppFramework &enableGzip(bool useGzip) override
|
||||
{
|
||||
useGzip_ = useGzip;
|
||||
return *this;
|
||||
}
|
||||
virtual bool isGzipEnabled() const override
|
||||
bool isGzipEnabled() const override
|
||||
{
|
||||
return useGzip_;
|
||||
}
|
||||
virtual HttpAppFramework &enableBrotli(bool useBrotli) override
|
||||
HttpAppFramework &enableBrotli(bool useBrotli) override
|
||||
{
|
||||
useBrotli_ = useBrotli;
|
||||
return *this;
|
||||
}
|
||||
virtual bool isBrotliEnabled() const override
|
||||
bool isBrotliEnabled() const override
|
||||
{
|
||||
return useBrotli_;
|
||||
}
|
||||
virtual HttpAppFramework &setStaticFilesCacheTime(int cacheTime) override;
|
||||
virtual int staticFilesCacheTime() const override;
|
||||
virtual HttpAppFramework &setIdleConnectionTimeout(size_t timeout) override
|
||||
HttpAppFramework &setStaticFilesCacheTime(int cacheTime) override;
|
||||
int staticFilesCacheTime() const override;
|
||||
HttpAppFramework &setIdleConnectionTimeout(size_t timeout) override
|
||||
{
|
||||
idleConnectionTimeout_ = timeout;
|
||||
return *this;
|
||||
}
|
||||
virtual HttpAppFramework &setKeepaliveRequestsNumber(
|
||||
const size_t number) override
|
||||
HttpAppFramework &setKeepaliveRequestsNumber(const size_t number) override
|
||||
{
|
||||
keepaliveRequestsNumber_ = number;
|
||||
return *this;
|
||||
}
|
||||
virtual HttpAppFramework &setPipeliningRequestsNumber(
|
||||
const size_t number) override
|
||||
HttpAppFramework &setPipeliningRequestsNumber(const size_t number) override
|
||||
{
|
||||
pipeliningRequestsNumber_ = number;
|
||||
return *this;
|
||||
}
|
||||
virtual HttpAppFramework &setGzipStatic(bool useGzipStatic) override;
|
||||
virtual HttpAppFramework &setBrStatic(bool useGzipStatic) override;
|
||||
virtual HttpAppFramework &setClientMaxBodySize(size_t maxSize) override
|
||||
HttpAppFramework &setGzipStatic(bool useGzipStatic) override;
|
||||
HttpAppFramework &setBrStatic(bool useGzipStatic) override;
|
||||
HttpAppFramework &setClientMaxBodySize(size_t maxSize) override
|
||||
{
|
||||
clientMaxBodySize_ = maxSize;
|
||||
return *this;
|
||||
}
|
||||
virtual HttpAppFramework &setClientMaxMemoryBodySize(
|
||||
size_t maxSize) override
|
||||
HttpAppFramework &setClientMaxMemoryBodySize(size_t maxSize) override
|
||||
{
|
||||
clientMaxMemoryBodySize_ = maxSize;
|
||||
return *this;
|
||||
}
|
||||
virtual HttpAppFramework &setClientMaxWebSocketMessageSize(
|
||||
size_t maxSize) override
|
||||
HttpAppFramework &setClientMaxWebSocketMessageSize(size_t maxSize) override
|
||||
{
|
||||
clientMaxWebSocketMessageSize_ = maxSize;
|
||||
return *this;
|
||||
}
|
||||
virtual HttpAppFramework &setHomePage(
|
||||
const std::string &homePageFile) override
|
||||
HttpAppFramework &setHomePage(const std::string &homePageFile) override
|
||||
{
|
||||
homePageFile_ = homePageFile;
|
||||
return *this;
|
||||
@ -343,7 +330,7 @@ class HttpAppFrameworkImpl : public HttpAppFramework
|
||||
{
|
||||
return homePageFile_;
|
||||
}
|
||||
virtual HttpAppFramework &setTermSignalHandler(
|
||||
HttpAppFramework &setTermSignalHandler(
|
||||
const std::function<void()> &handler) override
|
||||
{
|
||||
termSignalHandler_ = handler;
|
||||
@ -353,10 +340,9 @@ class HttpAppFrameworkImpl : public HttpAppFramework
|
||||
{
|
||||
return termSignalHandler_;
|
||||
}
|
||||
virtual HttpAppFramework &setImplicitPageEnable(
|
||||
bool useImplicitPage) override;
|
||||
HttpAppFramework &setImplicitPageEnable(bool useImplicitPage) override;
|
||||
bool isImplicitPageEnabled() const override;
|
||||
virtual HttpAppFramework &setImplicitPage(
|
||||
HttpAppFramework &setImplicitPage(
|
||||
const std::string &implicitPageFile) override;
|
||||
const std::string &getImplicitPage() const override;
|
||||
size_t getClientMaxBodySize() const
|
||||
@ -371,7 +357,7 @@ class HttpAppFrameworkImpl : public HttpAppFramework
|
||||
{
|
||||
return clientMaxWebSocketMessageSize_;
|
||||
}
|
||||
virtual std::vector<std::tuple<std::string, HttpMethod, std::string>>
|
||||
std::vector<std::tuple<std::string, HttpMethod, std::string>>
|
||||
getHandlersInfo() const override;
|
||||
|
||||
size_t keepaliveRequestsNumber() const
|
||||
@ -383,44 +369,42 @@ class HttpAppFrameworkImpl : public HttpAppFramework
|
||||
return pipeliningRequestsNumber_;
|
||||
}
|
||||
|
||||
virtual ~HttpAppFrameworkImpl() noexcept;
|
||||
virtual bool isRunning() override
|
||||
~HttpAppFrameworkImpl() noexcept override;
|
||||
bool isRunning() override
|
||||
{
|
||||
return running_;
|
||||
}
|
||||
|
||||
virtual HttpAppFramework &setUnicodeEscapingInJson(
|
||||
bool enable) noexcept override
|
||||
HttpAppFramework &setUnicodeEscapingInJson(bool enable) noexcept override
|
||||
{
|
||||
usingUnicodeEscaping_ = enable;
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual bool isUnicodeEscapingUsedInJson() const noexcept override
|
||||
bool isUnicodeEscapingUsedInJson() const noexcept override
|
||||
{
|
||||
return usingUnicodeEscaping_;
|
||||
}
|
||||
virtual HttpAppFramework &setFloatPrecisionInJson(
|
||||
HttpAppFramework &setFloatPrecisionInJson(
|
||||
unsigned int precision,
|
||||
const std::string &precisionType = "significant") noexcept override
|
||||
const std::string &precisionType) noexcept override
|
||||
{
|
||||
floatPrecisionInJson_ = std::make_pair(precision, precisionType);
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual const std::pair<unsigned int, std::string>
|
||||
&getFloatPrecisionInJson() const noexcept override
|
||||
const std::pair<unsigned int, std::string> &getFloatPrecisionInJson()
|
||||
const noexcept override
|
||||
{
|
||||
return floatPrecisionInJson_;
|
||||
}
|
||||
virtual trantor::EventLoop *getLoop() const override;
|
||||
trantor::EventLoop *getLoop() const override;
|
||||
|
||||
virtual trantor::EventLoop *getIOLoop(size_t id) const override;
|
||||
trantor::EventLoop *getIOLoop(size_t id) const override;
|
||||
|
||||
virtual void quit() override;
|
||||
void quit() override;
|
||||
|
||||
virtual HttpAppFramework &setServerHeaderField(
|
||||
const std::string &server) override
|
||||
HttpAppFramework &setServerHeaderField(const std::string &server) override
|
||||
{
|
||||
assert(!running_);
|
||||
assert(server.find("\r\n") == std::string::npos);
|
||||
@ -428,12 +412,12 @@ class HttpAppFrameworkImpl : public HttpAppFramework
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual HttpAppFramework &enableServerHeader(bool flag) override
|
||||
HttpAppFramework &enableServerHeader(bool flag) override
|
||||
{
|
||||
enableServerHeader_ = flag;
|
||||
return *this;
|
||||
}
|
||||
virtual HttpAppFramework &enableDateHeader(bool flag) override
|
||||
HttpAppFramework &enableDateHeader(bool flag) override
|
||||
{
|
||||
enableDateHeader_ = flag;
|
||||
return *this;
|
||||
@ -451,29 +435,34 @@ class HttpAppFrameworkImpl : public HttpAppFramework
|
||||
return serverHeader_;
|
||||
}
|
||||
|
||||
virtual orm::DbClientPtr getDbClient(
|
||||
const std::string &name = "default") override;
|
||||
virtual orm::DbClientPtr getFastDbClient(
|
||||
const std::string &name = "default") override;
|
||||
virtual HttpAppFramework &createDbClient(
|
||||
const std::string &dbType,
|
||||
const std::string &host,
|
||||
const unsigned short port,
|
||||
const std::string &databaseName,
|
||||
const std::string &userName,
|
||||
const std::string &password,
|
||||
const size_t connectionNum = 1,
|
||||
const std::string &filename = "",
|
||||
const std::string &name = "default",
|
||||
const bool isFast = false,
|
||||
const std::string &characterSet = "") override;
|
||||
virtual std::vector<trantor::InetAddress> getListeners() const override;
|
||||
orm::DbClientPtr getDbClient(const std::string &name) override;
|
||||
orm::DbClientPtr getFastDbClient(const std::string &name) override;
|
||||
HttpAppFramework &createDbClient(const std::string &dbType,
|
||||
const std::string &host,
|
||||
unsigned short port,
|
||||
const std::string &databaseName,
|
||||
const std::string &userName,
|
||||
const std::string &password,
|
||||
size_t connectionNum,
|
||||
const std::string &filename,
|
||||
const std::string &name,
|
||||
bool isFast,
|
||||
const std::string &characterSet) override;
|
||||
HttpAppFramework &createRedisClient(const std::string &ip,
|
||||
unsigned short port,
|
||||
const std::string &name,
|
||||
const std::string &password,
|
||||
size_t connectionNum,
|
||||
bool isFast) override;
|
||||
nosql::RedisClientPtr getRedisClient(const std::string &name) override;
|
||||
nosql::RedisClientPtr getFastRedisClient(const std::string &name) override;
|
||||
std::vector<trantor::InetAddress> getListeners() const override;
|
||||
inline static HttpAppFrameworkImpl &instance()
|
||||
{
|
||||
static HttpAppFrameworkImpl instance;
|
||||
return instance;
|
||||
}
|
||||
bool useSendfile()
|
||||
bool useSendfile() const
|
||||
{
|
||||
return useSendfile_;
|
||||
}
|
||||
@ -482,7 +471,7 @@ class HttpAppFrameworkImpl : public HttpAppFramework
|
||||
const HttpResponsePtr &resp,
|
||||
const std::function<void(const HttpResponsePtr &)> &callback);
|
||||
|
||||
virtual bool supportSSL() const override
|
||||
bool supportSSL() const override
|
||||
{
|
||||
#ifdef OpenSSL_FOUND
|
||||
return true;
|
||||
@ -490,7 +479,7 @@ class HttpAppFrameworkImpl : public HttpAppFramework
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual size_t getCurrentThreadIndex() const override
|
||||
size_t getCurrentThreadIndex() const override
|
||||
{
|
||||
auto *loop = trantor::EventLoop::getEventLoopOfCurrentThread();
|
||||
if (loop)
|
||||
@ -504,30 +493,29 @@ class HttpAppFrameworkImpl : public HttpAppFramework
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual bool areAllDbClientsAvailable() const noexcept override;
|
||||
bool areAllDbClientsAvailable() const noexcept override;
|
||||
const std::function<HttpResponsePtr(HttpStatusCode)>
|
||||
&getCustomErrorHandler() const override;
|
||||
bool isUsingCustomErrorHandler() const
|
||||
{
|
||||
return usingCustomErrorHandler_;
|
||||
}
|
||||
virtual void enableReusePort(bool enable = true) override
|
||||
void enableReusePort(bool enable) override
|
||||
{
|
||||
reusePort_ = enable;
|
||||
}
|
||||
virtual bool reusePort() const override
|
||||
bool reusePort() const override
|
||||
{
|
||||
return reusePort_;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void registerHttpController(
|
||||
const std::string &pathPattern,
|
||||
const internal::HttpBinderBasePtr &binder,
|
||||
const std::vector<HttpMethod> &validMethods = std::vector<HttpMethod>(),
|
||||
const std::vector<std::string> &filters = std::vector<std::string>(),
|
||||
const std::string &handlerName = "") override;
|
||||
virtual void registerHttpControllerViaRegex(
|
||||
void registerHttpController(const std::string &pathPattern,
|
||||
const internal::HttpBinderBasePtr &binder,
|
||||
const std::vector<HttpMethod> &validMethods,
|
||||
const std::vector<std::string> &filters,
|
||||
const std::string &handlerName) override;
|
||||
void registerHttpControllerViaRegex(
|
||||
const std::string ®Exp,
|
||||
const internal::HttpBinderBasePtr &binder,
|
||||
const std::vector<HttpMethod> &validMethods,
|
||||
@ -541,10 +529,6 @@ class HttpAppFrameworkImpl : public HttpAppFramework
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
const WebSocketConnectionImplPtr &wsConnPtr);
|
||||
void onConnection(const trantor::TcpConnectionPtr &conn);
|
||||
void addHttpPath(const std::string &path,
|
||||
const internal::HttpBinderBasePtr &binder,
|
||||
const std::vector<HttpMethod> &validMethods,
|
||||
const std::vector<std::string> &filters);
|
||||
|
||||
void findSessionForRequest(const HttpRequestImplPtr &req);
|
||||
|
||||
@ -566,7 +550,7 @@ class HttpAppFrameworkImpl : public HttpAppFramework
|
||||
const std::unique_ptr<ListenerManager> listenerManagerPtr_;
|
||||
const std::unique_ptr<PluginsManager> pluginsManagerPtr_;
|
||||
const std::unique_ptr<orm::DbClientManager> dbClientManagerPtr_;
|
||||
|
||||
const std::unique_ptr<nosql::RedisClientManager> redisClientManagerPtr_;
|
||||
std::string rootPath_{"./"};
|
||||
std::string uploadPath_;
|
||||
std::atomic_bool running_{false};
|
||||
@ -590,8 +574,8 @@ class HttpAppFrameworkImpl : public HttpAppFramework
|
||||
bool runAsDaemon_{false};
|
||||
bool handleSigterm_{true};
|
||||
bool relaunchOnError_{false};
|
||||
std::string logPath_{""};
|
||||
std::string logfileBaseName_{""};
|
||||
std::string logPath_;
|
||||
std::string logfileBaseName_;
|
||||
size_t logfileSize_{100000000};
|
||||
size_t keepaliveRequestsNumber_{0};
|
||||
size_t pipeliningRequestsNumber_{0};
|
||||
|
68
lib/src/RedisClientManager.h
Normal file
68
lib/src/RedisClientManager.h
Normal file
@ -0,0 +1,68 @@
|
||||
/**
|
||||
*
|
||||
* RedisClientManager.h
|
||||
* An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <drogon/nosql/RedisClient.h>
|
||||
#include <drogon/HttpAppFramework.h>
|
||||
#include <drogon/IOThreadStorage.h>
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
#include <trantor/net/EventLoop.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace drogon
|
||||
{
|
||||
namespace nosql
|
||||
{
|
||||
class RedisClientManager : public trantor::NonCopyable
|
||||
{
|
||||
public:
|
||||
void createRedisClients(const std::vector<trantor::EventLoop *> &ioLoops);
|
||||
RedisClientPtr getRedisClient(const std::string &name)
|
||||
{
|
||||
assert(redisClientsMap_.find(name) != redisClientsMap_.end());
|
||||
return redisClientsMap_[name];
|
||||
}
|
||||
|
||||
RedisClientPtr getFastRedisClient(const std::string &name)
|
||||
{
|
||||
auto iter = redisFastClientsMap_.find(name);
|
||||
assert(iter != redisFastClientsMap_.end());
|
||||
return iter->second.getThreadData();
|
||||
}
|
||||
void createRedisClient(const std::string &name,
|
||||
const std::string &host,
|
||||
unsigned short port,
|
||||
const std::string &password,
|
||||
size_t connectionNum,
|
||||
bool isFast);
|
||||
// bool areAllRedisClientsAvailable() const noexcept;
|
||||
|
||||
private:
|
||||
std::map<std::string, RedisClientPtr> redisClientsMap_;
|
||||
std::map<std::string, IOThreadStorage<RedisClientPtr>> redisFastClientsMap_;
|
||||
struct RedisInfo
|
||||
{
|
||||
std::string name_;
|
||||
std::string addr_;
|
||||
std::string password_;
|
||||
unsigned short port_;
|
||||
bool isFast_;
|
||||
size_t connectionNumber_;
|
||||
};
|
||||
std::vector<RedisInfo> redisInfos_;
|
||||
};
|
||||
} // namespace nosql
|
||||
} // namespace drogon
|
47
lib/src/RedisClientManagerSkipped.cc
Normal file
47
lib/src/RedisClientManagerSkipped.cc
Normal file
@ -0,0 +1,47 @@
|
||||
/**
|
||||
*
|
||||
* RedisClientManagerSkipped.cc
|
||||
* An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#include "RedisClientManager.h"
|
||||
#include <drogon/config.h>
|
||||
#include <drogon/utils/Utilities.h>
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace drogon::nosql;
|
||||
using namespace drogon;
|
||||
|
||||
void RedisClientManager::createRedisClients(
|
||||
const std::vector<trantor::EventLoop *> &ioloops)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void RedisClientManager::createRedisClient(const std::string &name,
|
||||
const std::string &host,
|
||||
unsigned short port,
|
||||
const std::string &password,
|
||||
size_t connectionNum,
|
||||
bool isFast)
|
||||
{
|
||||
LOG_FATAL << "Redis is supported by drogon, please install the "
|
||||
"hiredis library first.";
|
||||
abort();
|
||||
}
|
||||
|
||||
// bool RedisClientManager::areAllRedisClientsAvailable() const noexcept
|
||||
// {
|
||||
// LOG_FATAL << "Redis is supported by drogon, please install the "
|
||||
// "hiredis library first.";
|
||||
// abort();
|
||||
// }
|
@ -42,6 +42,12 @@ class DbClient;
|
||||
using DbClientPtr = std::shared_ptr<DbClient>;
|
||||
class DbClientManager;
|
||||
} // namespace orm
|
||||
namespace nosql
|
||||
{
|
||||
class RedisClient;
|
||||
using RedisClientPtr = std::shared_ptr<RedisClient>;
|
||||
class RedisClientManager;
|
||||
} // namespace nosql
|
||||
} // namespace drogon
|
||||
|
||||
namespace trantor
|
||||
|
259
nosql_lib/redis/inc/drogon/nosql/RedisClient.h
Normal file
259
nosql_lib/redis/inc/drogon/nosql/RedisClient.h
Normal file
@ -0,0 +1,259 @@
|
||||
/**
|
||||
*
|
||||
* @file RedisClient.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <drogon/nosql/RedisResult.h>
|
||||
#include <drogon/nosql/RedisException.h>
|
||||
#include <drogon/utils/string_view.h>
|
||||
#include <trantor/net/InetAddress.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#ifdef __cpp_impl_coroutine
|
||||
#include <drogon/utils/coroutine.h>
|
||||
#endif
|
||||
|
||||
namespace drogon
|
||||
{
|
||||
namespace nosql
|
||||
{
|
||||
#ifdef __cpp_impl_coroutine
|
||||
class RedisClient;
|
||||
class RedisTransaction;
|
||||
namespace internal
|
||||
{
|
||||
struct RedisAwaiter : public CallbackAwaiter<RedisResult>
|
||||
{
|
||||
using RedisFunction =
|
||||
std::function<void(RedisResultCallback &&, RedisExceptionCallback &&)>;
|
||||
explicit RedisAwaiter(RedisFunction &&function)
|
||||
: function_(std::move(function))
|
||||
{
|
||||
}
|
||||
void await_suspend(std::coroutine_handle<> handle)
|
||||
{
|
||||
function_(
|
||||
[handle, this](const RedisResult &result) {
|
||||
this->setValue(result);
|
||||
handle.resume();
|
||||
},
|
||||
[handle, this](const RedisException &e) {
|
||||
LOG_ERROR << e.what();
|
||||
this->setException(std::make_exception_ptr(e));
|
||||
handle.resume();
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
RedisFunction function_;
|
||||
};
|
||||
|
||||
struct RedisTransactionAwaiter
|
||||
: public CallbackAwaiter<std::shared_ptr<RedisTransaction>>
|
||||
{
|
||||
RedisTransactionAwaiter(RedisClient *client) : client_(client)
|
||||
{
|
||||
}
|
||||
|
||||
void await_suspend(std::coroutine_handle<> handle);
|
||||
|
||||
private:
|
||||
RedisClient *client_;
|
||||
};
|
||||
} // namespace internal
|
||||
#endif
|
||||
|
||||
class RedisTransaction;
|
||||
/**
|
||||
* @brief This class represents a redis client that contains several connections
|
||||
* to a redis server.
|
||||
*
|
||||
*/
|
||||
class RedisClient
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Create a new redis client with multiple connections;
|
||||
*
|
||||
* @param serverAddress The server address.
|
||||
* @param numberOfConnections The number of connections. 1 by default.
|
||||
* @param password The password to authenticate if necessary.
|
||||
* @return std::shared_ptr<RedisClient>
|
||||
*/
|
||||
static std::shared_ptr<RedisClient> newRedisClient(
|
||||
const trantor::InetAddress &serverAddress,
|
||||
size_t numberOfConnections = 1,
|
||||
const std::string &password = "");
|
||||
/**
|
||||
* @brief Execute a redis command
|
||||
*
|
||||
* @param resultCallback The callback is called when a redis reply is
|
||||
* received successfully.
|
||||
* @param exceptionCallback The callback is called when an error occurs.
|
||||
* @note When a redis reply with REDIS_REPLY_ERROR code is received, this
|
||||
* callback is called.
|
||||
* @param command The command to be executed. the command string can contain
|
||||
* some placeholders for parameters, such as '%s', '%d', etc.
|
||||
* @param ... The command parameters.
|
||||
* For example:
|
||||
* @code
|
||||
redisClientPtr->execCommandAsync([](const RedisResult &r){
|
||||
std::cout << r.getStringForDisplaying() << std::endl;
|
||||
},[](const std::exception &err){
|
||||
std::cerr << err.what() << std::endl;
|
||||
}, "get %s", key.data());
|
||||
@endcode
|
||||
*/
|
||||
virtual void execCommandAsync(RedisResultCallback &&resultCallback,
|
||||
RedisExceptionCallback &&exceptionCallback,
|
||||
string_view command,
|
||||
...) noexcept = 0;
|
||||
|
||||
/**
|
||||
* @brief Create a redis transaction object.
|
||||
*
|
||||
* @return std::shared_ptr<RedisTransaction>
|
||||
*/
|
||||
virtual std::shared_ptr<RedisTransaction> newTransaction() = 0;
|
||||
|
||||
/**
|
||||
* @brief Create a transaction object in asynchronous mode.
|
||||
*
|
||||
* @return std::shared_ptr<RedisTransaction>
|
||||
*/
|
||||
virtual void newTransactionAsync(
|
||||
const std::function<void(const std::shared_ptr<RedisTransaction> &)>
|
||||
&callback) = 0;
|
||||
virtual ~RedisClient() = default;
|
||||
#ifdef __cpp_impl_coroutine
|
||||
/**
|
||||
* @brief Send a Redis command and await the RedisResult in a coroutine.
|
||||
*
|
||||
* @tparam Arguments
|
||||
* @param command
|
||||
* @param args
|
||||
* @return internal::RedisAwaiter that can be awaited in a coroutine.
|
||||
* For example:
|
||||
* @code
|
||||
try
|
||||
{
|
||||
auto result = co_await redisClient->execCommandCoro("get %s",
|
||||
"keyname");
|
||||
std::cout << result.getStringForDisplaying() << "\n";
|
||||
}
|
||||
catch(const RedisException &err)
|
||||
{
|
||||
std::cout << err.what() << "\n";
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
template <typename... Arguments>
|
||||
internal::RedisAwaiter execCommandCoro(string_view command,
|
||||
Arguments... args)
|
||||
{
|
||||
return internal::RedisAwaiter(
|
||||
[command,
|
||||
this,
|
||||
args...](RedisResultCallback &&commandCallback,
|
||||
RedisExceptionCallback &&exceptionCallback) {
|
||||
execCommandAsync(std::move(commandCallback),
|
||||
std::move(exceptionCallback),
|
||||
command,
|
||||
args...);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @brief await a RedisTransactionPtr in a coroutine.
|
||||
*
|
||||
* @return internal::RedisTransactionAwaiter that can be awaited in a
|
||||
* coroutine.
|
||||
* For example:
|
||||
* @code
|
||||
try
|
||||
{
|
||||
auto transPtr = co_await redisClient->newTransactionCoro();
|
||||
...
|
||||
}
|
||||
catch(const RedisException &err)
|
||||
{
|
||||
std::cout << err.what() << "\n";
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
internal::RedisTransactionAwaiter newTransactionCoro()
|
||||
{
|
||||
return internal::RedisTransactionAwaiter(this);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
class RedisTransaction : public RedisClient
|
||||
{
|
||||
public:
|
||||
// virtual void cancel() = 0;
|
||||
virtual void execute(RedisResultCallback &&resultCallback,
|
||||
RedisExceptionCallback &&exceptionCallback) = 0;
|
||||
#ifdef __cpp_impl_coroutine
|
||||
/**
|
||||
* @brief Send a "exec" command to execute the transaction and await a
|
||||
* RedisResult in a coroutine.
|
||||
*
|
||||
* @return internal::RedisAwaiter that can be awaited in a coroutine.
|
||||
* For example:
|
||||
* @code
|
||||
try
|
||||
{
|
||||
auto transPtr = co_await redisClient->newTransactionCoro();
|
||||
...
|
||||
auto result = co_await transPtr->executeCoro();
|
||||
std::cout << result.getStringForDisplaying() << "\n";
|
||||
}
|
||||
catch(const RedisException &err)
|
||||
{
|
||||
std::cout << err.what() << "\n";
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
internal::RedisAwaiter executeCoro()
|
||||
{
|
||||
return internal::RedisAwaiter(
|
||||
[this](RedisResultCallback &&resultCallback,
|
||||
RedisExceptionCallback &&exceptionCallback) {
|
||||
execute(std::move(resultCallback),
|
||||
std::move(exceptionCallback));
|
||||
});
|
||||
}
|
||||
#endif
|
||||
};
|
||||
using RedisClientPtr = std::shared_ptr<RedisClient>;
|
||||
using RedisTransactionPtr = std::shared_ptr<RedisTransaction>;
|
||||
|
||||
#ifdef __cpp_impl_coroutine
|
||||
inline void internal::RedisTransactionAwaiter::await_suspend(
|
||||
std::coroutine_handle<> handle)
|
||||
{
|
||||
assert(client_ != nullptr);
|
||||
client_->newTransactionAsync(
|
||||
[this, &handle](const std::shared_ptr<RedisTransaction> &transaction) {
|
||||
if (transaction == nullptr)
|
||||
setException(std::make_exception_ptr(
|
||||
RedisException(RedisErrorCode::kInternalError,
|
||||
"Failed to create transaction")));
|
||||
else
|
||||
setValue(transaction);
|
||||
handle.resume();
|
||||
});
|
||||
}
|
||||
#endif
|
||||
} // namespace nosql
|
||||
} // namespace drogon
|
60
nosql_lib/redis/inc/drogon/nosql/RedisException.h
Normal file
60
nosql_lib/redis/inc/drogon/nosql/RedisException.h
Normal file
@ -0,0 +1,60 @@
|
||||
/**
|
||||
*
|
||||
* @file RedisException.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
|
||||
namespace drogon
|
||||
{
|
||||
namespace nosql
|
||||
{
|
||||
enum class RedisErrorCode
|
||||
{
|
||||
kNone = 0,
|
||||
kUnknown,
|
||||
kConnectionBroken,
|
||||
kNoConnectionAvailable,
|
||||
kRedisError,
|
||||
kInternalError,
|
||||
kTransactionCancelled,
|
||||
kBadType
|
||||
};
|
||||
class RedisException final : public std::exception
|
||||
{
|
||||
public:
|
||||
const char *what() const noexcept override
|
||||
{
|
||||
return message_.data();
|
||||
}
|
||||
RedisErrorCode code() const
|
||||
{
|
||||
return code_;
|
||||
}
|
||||
RedisException(RedisErrorCode code, const std::string &message)
|
||||
: code_(code), message_(message)
|
||||
{
|
||||
}
|
||||
RedisException(RedisErrorCode code, std::string &&message)
|
||||
: code_(code), message_(std::move(message))
|
||||
{
|
||||
}
|
||||
RedisException() = delete;
|
||||
|
||||
private:
|
||||
std::string message_;
|
||||
RedisErrorCode code_{RedisErrorCode::kNone};
|
||||
};
|
||||
using RedisExceptionCallback = std::function<void(const RedisException &)>;
|
||||
} // namespace nosql
|
||||
} // namespace drogon
|
124
nosql_lib/redis/inc/drogon/nosql/RedisResult.h
Normal file
124
nosql_lib/redis/inc/drogon/nosql/RedisResult.h
Normal file
@ -0,0 +1,124 @@
|
||||
/**
|
||||
*
|
||||
* @file RedisResult.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
struct redisReply;
|
||||
namespace drogon
|
||||
{
|
||||
namespace nosql
|
||||
{
|
||||
enum class RedisResultType
|
||||
{
|
||||
kInteger = 0,
|
||||
kString,
|
||||
kArray,
|
||||
kStatus,
|
||||
kNil,
|
||||
kError
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This class represents a redis reply with no error.
|
||||
* @note Limited by the hiredis library, the RedisResult object is only
|
||||
* available in the context of the result callback, one can't hold or copy or
|
||||
* move a RedisResult object for later use after the callback is returned.
|
||||
*/
|
||||
class RedisResult
|
||||
{
|
||||
public:
|
||||
explicit RedisResult(redisReply *result) : result_(result)
|
||||
{
|
||||
}
|
||||
~RedisResult() = default;
|
||||
|
||||
/**
|
||||
* @brief Return the type of the result_
|
||||
* @return RedisResultType
|
||||
* @note The kError type is never obtained here.
|
||||
*/
|
||||
RedisResultType type() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Get the string value of the result.
|
||||
*
|
||||
* @return std::string
|
||||
* @note Calling the method of a result object which is the kArray type
|
||||
* throws a runtime exception.
|
||||
*/
|
||||
std::string asString() const noexcept(false);
|
||||
|
||||
/**
|
||||
* @brief Get the array value of the result.
|
||||
*
|
||||
* @return std::vector<RedisResult>
|
||||
* @note Calling the method of a result object whose type is not kArray type
|
||||
* throws a runtime exception.
|
||||
*/
|
||||
std::vector<RedisResult> asArray() const noexcept(false);
|
||||
|
||||
/**
|
||||
* @brief Get the integer value of the result.
|
||||
*
|
||||
* @return long long
|
||||
* @note Calling the method of a result object whose type is not kInteger
|
||||
* type throws a runtime exception.
|
||||
*/
|
||||
long long asInteger() const noexcept(false);
|
||||
|
||||
/**
|
||||
* @brief Get the string for displaying the result.
|
||||
*
|
||||
* @return std::string
|
||||
*/
|
||||
std::string getStringForDisplaying() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Get the string for displaying with indent.
|
||||
*
|
||||
* @param indent The indent value.
|
||||
* @return std::string
|
||||
*/
|
||||
std::string getStringForDisplayingWithIndent(
|
||||
size_t indent = 0) const noexcept;
|
||||
|
||||
/**
|
||||
* @brief return true if the result object is nil.
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool isNil() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Check if the result object is not nil.
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
explicit operator bool() const
|
||||
{
|
||||
return !isNil();
|
||||
}
|
||||
|
||||
private:
|
||||
redisReply *result_;
|
||||
};
|
||||
using RedisResultCallback = std::function<void(const RedisResult &)>;
|
||||
} // namespace nosql
|
||||
} // namespace drogon
|
233
nosql_lib/redis/src/RedisClientImpl.cc
Normal file
233
nosql_lib/redis/src/RedisClientImpl.cc
Normal file
@ -0,0 +1,233 @@
|
||||
/**
|
||||
*
|
||||
* @file RedisClientImpl.cc
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#include "RedisClientImpl.h"
|
||||
#include "RedisTransactionImpl.h"
|
||||
using namespace drogon::nosql;
|
||||
std::shared_ptr<RedisClient> RedisClient::newRedisClient(
|
||||
const trantor::InetAddress &serverAddress,
|
||||
size_t connectionNumber,
|
||||
const std::string &password)
|
||||
{
|
||||
return std::make_shared<RedisClientImpl>(serverAddress,
|
||||
connectionNumber,
|
||||
password);
|
||||
}
|
||||
|
||||
RedisClientImpl::RedisClientImpl(const trantor::InetAddress &serverAddress,
|
||||
size_t numberOfConnections,
|
||||
std::string password)
|
||||
: loops_(numberOfConnections < std::thread::hardware_concurrency()
|
||||
? numberOfConnections
|
||||
: std::thread::hardware_concurrency(),
|
||||
"RedisLoop"),
|
||||
serverAddr_(serverAddress),
|
||||
password_(std::move(password)),
|
||||
numberOfConnections_(numberOfConnections)
|
||||
{
|
||||
loops_.start();
|
||||
for (size_t i = 0; i < numberOfConnections_; ++i)
|
||||
{
|
||||
auto loop = loops_.getNextLoop();
|
||||
loop->queueInLoop([this, loop]() {
|
||||
std::lock_guard<std::mutex> lock(connectionsMutex_);
|
||||
connections_.insert(newConnection(loop));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
RedisConnectionPtr RedisClientImpl::newConnection(trantor::EventLoop *loop)
|
||||
{
|
||||
auto conn = std::make_shared<RedisConnection>(serverAddr_, password_, loop);
|
||||
std::weak_ptr<RedisClientImpl> thisWeakPtr = shared_from_this();
|
||||
conn->setConnectCallback([thisWeakPtr](RedisConnectionPtr &&conn) {
|
||||
auto thisPtr = thisWeakPtr.lock();
|
||||
if (thisPtr)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(thisPtr->connectionsMutex_);
|
||||
thisPtr->readyConnections_.push_back(conn);
|
||||
}
|
||||
thisPtr->handleNextTask(conn);
|
||||
}
|
||||
});
|
||||
conn->setDisconnectCallback([thisWeakPtr](RedisConnectionPtr &&conn) {
|
||||
// assert(status == REDIS_CONNECTED);
|
||||
auto thisPtr = thisWeakPtr.lock();
|
||||
if (thisPtr)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(thisPtr->connectionsMutex_);
|
||||
thisPtr->connections_.erase(conn);
|
||||
for (auto iter = thisPtr->readyConnections_.begin();
|
||||
iter != thisPtr->readyConnections_.end();
|
||||
++iter)
|
||||
{
|
||||
if (*iter == conn)
|
||||
{
|
||||
thisPtr->readyConnections_.erase(iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto loop = trantor::EventLoop::getEventLoopOfCurrentThread();
|
||||
assert(loop);
|
||||
loop->runAfter(2.0, [thisPtr, loop, conn]() {
|
||||
thisPtr->connections_.insert(thisPtr->newConnection(loop));
|
||||
});
|
||||
}
|
||||
});
|
||||
conn->setIdleCallback([thisWeakPtr](const RedisConnectionPtr &connPtr) {
|
||||
auto thisPtr = thisWeakPtr.lock();
|
||||
if (thisPtr)
|
||||
{
|
||||
thisPtr->handleNextTask(connPtr);
|
||||
}
|
||||
});
|
||||
return conn;
|
||||
}
|
||||
|
||||
void RedisClientImpl::execCommandAsync(
|
||||
RedisResultCallback &&resultCallback,
|
||||
RedisExceptionCallback &&exceptionCallback,
|
||||
string_view command,
|
||||
...) noexcept
|
||||
{
|
||||
RedisConnectionPtr connPtr;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(connectionsMutex_);
|
||||
if (!readyConnections_.empty())
|
||||
{
|
||||
if (connectionPos_ >= readyConnections_.size())
|
||||
{
|
||||
connPtr = readyConnections_[0];
|
||||
connectionPos_ = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
connPtr = readyConnections_[connectionPos_++];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (connPtr)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, command);
|
||||
connPtr->sendvCommand(command,
|
||||
std::move(resultCallback),
|
||||
std::move(exceptionCallback),
|
||||
args);
|
||||
va_end(args);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_TRACE << "no connection available, push command to buffer";
|
||||
std::weak_ptr<RedisClientImpl> thisWeakPtr = shared_from_this();
|
||||
va_list args;
|
||||
va_start(args, command);
|
||||
auto formattedCmd = RedisConnection::getFormattedCommand(command, args);
|
||||
va_end(args);
|
||||
std::lock_guard<std::mutex> lock(connectionsMutex_);
|
||||
tasks_.emplace([thisWeakPtr,
|
||||
resultCallback = std::move(resultCallback),
|
||||
exceptionCallback = std::move(exceptionCallback),
|
||||
formattedCmd = std::move(formattedCmd)](
|
||||
const RedisConnectionPtr &connPtr) mutable {
|
||||
connPtr->sendFormattedCommand(std::move(formattedCmd),
|
||||
std::move(resultCallback),
|
||||
std::move(exceptionCallback));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
RedisClientImpl::~RedisClientImpl()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(connectionsMutex_);
|
||||
for (auto &conn : connections_)
|
||||
{
|
||||
conn->disconnect();
|
||||
}
|
||||
readyConnections_.clear();
|
||||
connections_.clear();
|
||||
}
|
||||
|
||||
void RedisClientImpl::newTransactionAsync(
|
||||
const std::function<void(const std::shared_ptr<RedisTransaction> &)>
|
||||
&callback)
|
||||
{
|
||||
RedisConnectionPtr connPtr;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(connectionsMutex_);
|
||||
if (!readyConnections_.empty())
|
||||
{
|
||||
connPtr = readyConnections_[readyConnections_.size() - 1];
|
||||
readyConnections_.resize(readyConnections_.size() - 1);
|
||||
}
|
||||
}
|
||||
if (connPtr)
|
||||
{
|
||||
callback(makeTransaction(connPtr));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::weak_ptr<RedisClientImpl> thisWeakPtr = shared_from_this();
|
||||
std::lock_guard<std::mutex> lock(connectionsMutex_);
|
||||
tasks_.emplace(
|
||||
[callback, thisWeakPtr](const RedisConnectionPtr &connPtr) {
|
||||
auto thisPtr = thisWeakPtr.lock();
|
||||
if (thisPtr)
|
||||
{
|
||||
thisPtr->newTransactionAsync(callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<RedisTransaction> RedisClientImpl::makeTransaction(
|
||||
const RedisConnectionPtr &connPtr)
|
||||
{
|
||||
std::weak_ptr<RedisClientImpl> thisWeakPtr = shared_from_this();
|
||||
auto trans = std::shared_ptr<RedisTransactionImpl>(
|
||||
new RedisTransactionImpl(connPtr),
|
||||
[thisWeakPtr, connPtr](RedisTransactionImpl *p) {
|
||||
delete p;
|
||||
auto thisPtr = thisWeakPtr.lock();
|
||||
if (thisPtr)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(
|
||||
thisPtr->connectionsMutex_);
|
||||
thisPtr->readyConnections_.push_back(connPtr);
|
||||
}
|
||||
thisPtr->handleNextTask(connPtr);
|
||||
}
|
||||
});
|
||||
trans->doBegin();
|
||||
return trans;
|
||||
}
|
||||
|
||||
void RedisClientImpl::handleNextTask(const RedisConnectionPtr &connPtr)
|
||||
{
|
||||
std::function<void(const RedisConnectionPtr &)> task;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(connectionsMutex_);
|
||||
if (!tasks_.empty())
|
||||
{
|
||||
task = std::move(tasks_.front());
|
||||
tasks_.pop();
|
||||
}
|
||||
}
|
||||
if (task)
|
||||
{
|
||||
task(connPtr);
|
||||
}
|
||||
}
|
72
nosql_lib/redis/src/RedisClientImpl.h
Normal file
72
nosql_lib/redis/src/RedisClientImpl.h
Normal file
@ -0,0 +1,72 @@
|
||||
/**
|
||||
*
|
||||
* @file RedisClientImpl.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "RedisConnection.h"
|
||||
#include <drogon/nosql/RedisClient.h>
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
#include <trantor/net/EventLoopThreadPool.h>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
#include <queue>
|
||||
#include <future>
|
||||
|
||||
namespace drogon
|
||||
{
|
||||
namespace nosql
|
||||
{
|
||||
class RedisClientImpl final
|
||||
: public RedisClient,
|
||||
public trantor::NonCopyable,
|
||||
public std::enable_shared_from_this<RedisClientImpl>
|
||||
{
|
||||
public:
|
||||
RedisClientImpl(const trantor::InetAddress &serverAddress,
|
||||
size_t numberOfConnections,
|
||||
std::string password = "");
|
||||
void execCommandAsync(RedisResultCallback &&resultCallback,
|
||||
RedisExceptionCallback &&exceptionCallback,
|
||||
string_view command,
|
||||
...) noexcept override;
|
||||
~RedisClientImpl() override;
|
||||
RedisTransactionPtr newTransaction() override
|
||||
{
|
||||
std::promise<RedisTransactionPtr> prom;
|
||||
auto f = prom.get_future();
|
||||
newTransactionAsync([&prom](const RedisTransactionPtr &transPtr) {
|
||||
prom.set_value(transPtr);
|
||||
});
|
||||
return f.get();
|
||||
}
|
||||
void newTransactionAsync(
|
||||
const std::function<void(const RedisTransactionPtr &)> &callback)
|
||||
override;
|
||||
|
||||
private:
|
||||
trantor::EventLoopThreadPool loops_;
|
||||
std::mutex connectionsMutex_;
|
||||
std::unordered_set<RedisConnectionPtr> connections_;
|
||||
std::vector<RedisConnectionPtr> readyConnections_;
|
||||
size_t connectionPos_{0};
|
||||
RedisConnectionPtr newConnection(trantor::EventLoop *loop);
|
||||
const trantor::InetAddress serverAddr_;
|
||||
const std::string password_;
|
||||
const size_t numberOfConnections_;
|
||||
std::queue<std::function<void(const RedisConnectionPtr &)>> tasks_;
|
||||
std::shared_ptr<RedisTransaction> makeTransaction(
|
||||
const RedisConnectionPtr &connPtr);
|
||||
void handleNextTask(const RedisConnectionPtr &connPtr);
|
||||
};
|
||||
} // namespace nosql
|
||||
} // namespace drogon
|
208
nosql_lib/redis/src/RedisClientLockFree.cc
Normal file
208
nosql_lib/redis/src/RedisClientLockFree.cc
Normal file
@ -0,0 +1,208 @@
|
||||
/**
|
||||
*
|
||||
* @file RedisClientLockFree.cc
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#include "RedisClientLockFree.h"
|
||||
#include "RedisTransactionImpl.h"
|
||||
using namespace drogon::nosql;
|
||||
|
||||
RedisClientLockFree::RedisClientLockFree(
|
||||
const trantor::InetAddress &serverAddress,
|
||||
size_t numberOfConnections,
|
||||
trantor::EventLoop *loop,
|
||||
std::string password)
|
||||
: loop_(loop),
|
||||
serverAddr_(serverAddress),
|
||||
password_(std::move(password)),
|
||||
numberOfConnections_(numberOfConnections)
|
||||
{
|
||||
assert(loop_);
|
||||
for (size_t i = 0; i < numberOfConnections_; ++i)
|
||||
{
|
||||
loop_->queueInLoop([this]() { connections_.insert(newConnection()); });
|
||||
}
|
||||
}
|
||||
|
||||
RedisConnectionPtr RedisClientLockFree::newConnection()
|
||||
{
|
||||
loop_->assertInLoopThread();
|
||||
auto conn =
|
||||
std::make_shared<RedisConnection>(serverAddr_, password_, loop_);
|
||||
std::weak_ptr<RedisClientLockFree> thisWeakPtr = shared_from_this();
|
||||
conn->setConnectCallback([thisWeakPtr](RedisConnectionPtr &&conn) {
|
||||
auto thisPtr = thisWeakPtr.lock();
|
||||
if (thisPtr)
|
||||
{
|
||||
thisPtr->readyConnections_.push_back(conn);
|
||||
thisPtr->handleNextTask(conn);
|
||||
}
|
||||
});
|
||||
conn->setDisconnectCallback([thisWeakPtr](RedisConnectionPtr &&conn) {
|
||||
// assert(status == REDIS_CONNECTED);
|
||||
auto thisPtr = thisWeakPtr.lock();
|
||||
if (thisPtr)
|
||||
{
|
||||
thisPtr->connections_.erase(conn);
|
||||
for (auto iter = thisPtr->readyConnections_.begin();
|
||||
iter != thisPtr->readyConnections_.end();
|
||||
++iter)
|
||||
{
|
||||
if (*iter == conn)
|
||||
{
|
||||
thisPtr->readyConnections_.erase(iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
thisPtr->loop_->runAfter(2.0, [thisPtr, conn]() {
|
||||
thisPtr->connections_.insert(thisPtr->newConnection());
|
||||
});
|
||||
}
|
||||
});
|
||||
conn->setIdleCallback([thisWeakPtr](const RedisConnectionPtr &connPtr) {
|
||||
auto thisPtr = thisWeakPtr.lock();
|
||||
if (thisPtr)
|
||||
{
|
||||
thisPtr->handleNextTask(connPtr);
|
||||
}
|
||||
});
|
||||
return conn;
|
||||
}
|
||||
|
||||
void RedisClientLockFree::execCommandAsync(
|
||||
RedisResultCallback &&resultCallback,
|
||||
RedisExceptionCallback &&exceptionCallback,
|
||||
string_view command,
|
||||
...) noexcept
|
||||
{
|
||||
loop_->assertInLoopThread();
|
||||
RedisConnectionPtr connPtr;
|
||||
{
|
||||
if (!readyConnections_.empty())
|
||||
{
|
||||
if (connectionPos_ >= readyConnections_.size())
|
||||
{
|
||||
connPtr = readyConnections_[0];
|
||||
connectionPos_ = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
connPtr = readyConnections_[connectionPos_++];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (connPtr)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, command);
|
||||
connPtr->sendvCommand(command,
|
||||
std::move(resultCallback),
|
||||
std::move(exceptionCallback),
|
||||
args);
|
||||
va_end(args);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_TRACE << "no connection available, push command to buffer";
|
||||
std::weak_ptr<RedisClientLockFree> thisWeakPtr = shared_from_this();
|
||||
va_list args;
|
||||
va_start(args, command);
|
||||
auto formattedCmd = RedisConnection::getFormattedCommand(command, args);
|
||||
va_end(args);
|
||||
tasks_.emplace([thisWeakPtr,
|
||||
resultCallback = std::move(resultCallback),
|
||||
exceptionCallback = std::move(exceptionCallback),
|
||||
formattedCmd = std::move(formattedCmd)](
|
||||
const RedisConnectionPtr &connPtr) mutable {
|
||||
connPtr->sendFormattedCommand(std::move(formattedCmd),
|
||||
std::move(resultCallback),
|
||||
std::move(exceptionCallback));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
RedisClientLockFree::~RedisClientLockFree()
|
||||
{
|
||||
for (auto &conn : connections_)
|
||||
{
|
||||
conn->disconnect();
|
||||
}
|
||||
readyConnections_.clear();
|
||||
connections_.clear();
|
||||
}
|
||||
|
||||
void RedisClientLockFree::newTransactionAsync(
|
||||
const std::function<void(const std::shared_ptr<RedisTransaction> &)>
|
||||
&callback)
|
||||
{
|
||||
loop_->assertInLoopThread();
|
||||
RedisConnectionPtr connPtr;
|
||||
|
||||
if (!readyConnections_.empty())
|
||||
{
|
||||
connPtr = readyConnections_[readyConnections_.size() - 1];
|
||||
readyConnections_.resize(readyConnections_.size() - 1);
|
||||
}
|
||||
|
||||
if (connPtr)
|
||||
{
|
||||
callback(makeTransaction(connPtr));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::weak_ptr<RedisClientLockFree> thisWeakPtr = shared_from_this();
|
||||
tasks_.emplace(
|
||||
[callback, thisWeakPtr](const RedisConnectionPtr &connPtr) {
|
||||
auto thisPtr = thisWeakPtr.lock();
|
||||
if (thisPtr)
|
||||
{
|
||||
thisPtr->newTransactionAsync(callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<RedisTransaction> RedisClientLockFree::makeTransaction(
|
||||
const RedisConnectionPtr &connPtr)
|
||||
{
|
||||
std::weak_ptr<RedisClientLockFree> thisWeakPtr = shared_from_this();
|
||||
auto trans = std::shared_ptr<RedisTransactionImpl>(
|
||||
new RedisTransactionImpl(connPtr),
|
||||
[thisWeakPtr, connPtr](RedisTransactionImpl *p) {
|
||||
delete p;
|
||||
auto thisPtr = thisWeakPtr.lock();
|
||||
if (thisPtr)
|
||||
{
|
||||
thisPtr->readyConnections_.push_back(connPtr);
|
||||
thisPtr->handleNextTask(connPtr);
|
||||
}
|
||||
});
|
||||
trans->doBegin();
|
||||
return trans;
|
||||
}
|
||||
|
||||
void RedisClientLockFree::handleNextTask(const RedisConnectionPtr &connPtr)
|
||||
{
|
||||
loop_->assertInLoopThread();
|
||||
std::function<void(const RedisConnectionPtr &)> task;
|
||||
|
||||
if (!tasks_.empty())
|
||||
{
|
||||
task = std::move(tasks_.front());
|
||||
tasks_.pop();
|
||||
}
|
||||
|
||||
if (task)
|
||||
{
|
||||
task(connPtr);
|
||||
}
|
||||
}
|
72
nosql_lib/redis/src/RedisClientLockFree.h
Normal file
72
nosql_lib/redis/src/RedisClientLockFree.h
Normal file
@ -0,0 +1,72 @@
|
||||
/**
|
||||
*
|
||||
* @file RedisClientLockFree.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "RedisConnection.h"
|
||||
#include <drogon/nosql/RedisClient.h>
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
#include <trantor/net/EventLoopThreadPool.h>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
#include <queue>
|
||||
#include <future>
|
||||
|
||||
namespace drogon
|
||||
{
|
||||
namespace nosql
|
||||
{
|
||||
class RedisClientLockFree final
|
||||
: public RedisClient,
|
||||
public trantor::NonCopyable,
|
||||
public std::enable_shared_from_this<RedisClientLockFree>
|
||||
{
|
||||
public:
|
||||
RedisClientLockFree(const trantor::InetAddress &serverAddress,
|
||||
size_t numberOfConnections,
|
||||
trantor::EventLoop *loop,
|
||||
std::string password = "");
|
||||
void execCommandAsync(RedisResultCallback &&resultCallback,
|
||||
RedisExceptionCallback &&exceptionCallback,
|
||||
string_view command,
|
||||
...) noexcept override;
|
||||
~RedisClientLockFree() override;
|
||||
RedisTransactionPtr newTransaction() override
|
||||
{
|
||||
LOG_ERROR
|
||||
<< "You can't use the synchronous interface in the fast redis "
|
||||
"client, please use the asynchronous version "
|
||||
"(newTransactionAsync)";
|
||||
assert(0);
|
||||
return nullptr;
|
||||
}
|
||||
void newTransactionAsync(
|
||||
const std::function<void(const RedisTransactionPtr &)> &callback)
|
||||
override;
|
||||
|
||||
private:
|
||||
trantor::EventLoop *loop_;
|
||||
std::unordered_set<RedisConnectionPtr> connections_;
|
||||
std::vector<RedisConnectionPtr> readyConnections_;
|
||||
size_t connectionPos_{0};
|
||||
RedisConnectionPtr newConnection();
|
||||
const trantor::InetAddress serverAddr_;
|
||||
const std::string password_;
|
||||
const size_t numberOfConnections_;
|
||||
std::queue<std::function<void(const RedisConnectionPtr &)>> tasks_;
|
||||
std::shared_ptr<RedisTransaction> makeTransaction(
|
||||
const RedisConnectionPtr &connPtr);
|
||||
void handleNextTask(const RedisConnectionPtr &connPtr);
|
||||
};
|
||||
} // namespace nosql
|
||||
} // namespace drogon
|
92
nosql_lib/redis/src/RedisClientManager.cc
Normal file
92
nosql_lib/redis/src/RedisClientManager.cc
Normal file
@ -0,0 +1,92 @@
|
||||
/**
|
||||
*
|
||||
* @file RedisClientManager.cc
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../lib/src/RedisClientManager.h"
|
||||
#include "RedisClientLockFree.h"
|
||||
#include "RedisClientImpl.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace drogon::nosql;
|
||||
using namespace drogon;
|
||||
|
||||
void RedisClientManager::createRedisClients(
|
||||
const std::vector<trantor::EventLoop *> &ioLoops)
|
||||
{
|
||||
assert(redisClientsMap_.empty());
|
||||
assert(redisFastClientsMap_.empty());
|
||||
for (auto &redisInfo : redisInfos_)
|
||||
{
|
||||
if (redisInfo.isFast_)
|
||||
{
|
||||
redisFastClientsMap_[redisInfo.name_] =
|
||||
IOThreadStorage<RedisClientPtr>();
|
||||
redisFastClientsMap_[redisInfo.name_].init([&](RedisClientPtr &c,
|
||||
size_t idx) {
|
||||
assert(idx == ioLoops[idx]->index());
|
||||
LOG_TRACE << "create fast redis client for the thread " << idx;
|
||||
c = std::make_shared<RedisClientLockFree>(
|
||||
trantor::InetAddress(redisInfo.addr_, redisInfo.port_),
|
||||
redisInfo.connectionNumber_,
|
||||
ioLoops[idx],
|
||||
redisInfo.password_);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
redisClientsMap_[redisInfo.name_] =
|
||||
std::make_shared<RedisClientImpl>(
|
||||
trantor::InetAddress(redisInfo.addr_, redisInfo.port_),
|
||||
redisInfo.connectionNumber_,
|
||||
redisInfo.password_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RedisClientManager::createRedisClient(const std::string &name,
|
||||
const std::string &addr,
|
||||
unsigned short port,
|
||||
const std::string &password,
|
||||
const size_t connectionNum,
|
||||
const bool isFast)
|
||||
{
|
||||
RedisInfo info;
|
||||
info.name_ = name;
|
||||
info.addr_ = addr;
|
||||
info.port_ = port;
|
||||
info.password_ = password;
|
||||
info.connectionNumber_ = connectionNum;
|
||||
info.isFast_ = isFast;
|
||||
|
||||
redisInfos_.emplace_back(std::move(info));
|
||||
}
|
||||
|
||||
// bool RedisClientManager::areAllRedisClientsAvailable() const noexcept
|
||||
//{
|
||||
// for (auto const &pair : redisClientsMap_)
|
||||
// {
|
||||
// if (!(pair.second)->hasAvailableConnections())
|
||||
// return false;
|
||||
// }
|
||||
// auto loop = trantor::EventLoop::getEventLoopOfCurrentThread();
|
||||
// if (loop && loop->index() < app().getThreadNum())
|
||||
// {
|
||||
// for (auto const &pair : redisFastClientsMap_)
|
||||
// {
|
||||
// if (!(*(pair.second))->hasAvailableConnections())
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// return true;
|
||||
//}
|
254
nosql_lib/redis/src/RedisConnection.cc
Normal file
254
nosql_lib/redis/src/RedisConnection.cc
Normal file
@ -0,0 +1,254 @@
|
||||
/**
|
||||
*
|
||||
* @file RedisConnection.cc
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#include "RedisConnection.h"
|
||||
#include <drogon/nosql/RedisResult.h>
|
||||
#include <future>
|
||||
|
||||
using namespace drogon::nosql;
|
||||
RedisConnection::RedisConnection(const trantor::InetAddress &serverAddress,
|
||||
const std::string &password,
|
||||
trantor::EventLoop *loop)
|
||||
: serverAddr_(serverAddress), password_(password), loop_(loop)
|
||||
{
|
||||
assert(loop_);
|
||||
loop_->queueInLoop([this]() { startConnectionInLoop(); });
|
||||
}
|
||||
|
||||
void RedisConnection::startConnectionInLoop()
|
||||
{
|
||||
loop_->assertInLoopThread();
|
||||
assert(!redisContext_);
|
||||
|
||||
redisContext_ =
|
||||
::redisAsyncConnect(serverAddr_.toIp().c_str(), serverAddr_.toPort());
|
||||
status_ = ConnectStatus::kConnecting;
|
||||
if (redisContext_->err)
|
||||
{
|
||||
LOG_ERROR << "Error: " << redisContext_->errstr;
|
||||
if (disconnectCallback_)
|
||||
{
|
||||
disconnectCallback_(shared_from_this());
|
||||
}
|
||||
}
|
||||
redisContext_->ev.addWrite = addWrite;
|
||||
redisContext_->ev.delWrite = delWrite;
|
||||
redisContext_->ev.addRead = addRead;
|
||||
redisContext_->ev.delRead = delRead;
|
||||
redisContext_->ev.cleanup = cleanup;
|
||||
redisContext_->ev.data = this;
|
||||
|
||||
channel_ = std::make_unique<trantor::Channel>(loop_, redisContext_->c.fd);
|
||||
channel_->setReadCallback([this]() { handleRedisRead(); });
|
||||
channel_->setWriteCallback([this]() { handleRedisWrite(); });
|
||||
redisAsyncSetConnectCallback(
|
||||
redisContext_, [](const redisAsyncContext *context, int status) {
|
||||
auto thisPtr = static_cast<RedisConnection *>(context->ev.data);
|
||||
if (status != REDIS_OK)
|
||||
{
|
||||
LOG_ERROR << "Failed to connect to "
|
||||
<< thisPtr->serverAddr_.toIpPort() << "! "
|
||||
<< context->errstr;
|
||||
thisPtr->handleDisconnect();
|
||||
if (thisPtr->disconnectCallback_)
|
||||
{
|
||||
thisPtr->disconnectCallback_(thisPtr->shared_from_this());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_TRACE << "Connected successfully to "
|
||||
<< thisPtr->serverAddr_.toIpPort();
|
||||
if (thisPtr->password_.empty())
|
||||
{
|
||||
thisPtr->status_ = ConnectStatus::kConnected;
|
||||
if (thisPtr->connectCallback_)
|
||||
{
|
||||
thisPtr->connectCallback_(thisPtr->shared_from_this());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::weak_ptr<RedisConnection> weakThisPtr =
|
||||
thisPtr->shared_from_this();
|
||||
thisPtr->sendCommand(
|
||||
|
||||
[weakThisPtr](const RedisResult &r) {
|
||||
auto thisPtr = weakThisPtr.lock();
|
||||
if (!thisPtr)
|
||||
return;
|
||||
if (r.asString() == "OK")
|
||||
{
|
||||
if (thisPtr->connectCallback_)
|
||||
thisPtr->connectCallback_(
|
||||
thisPtr->shared_from_this());
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR << r.asString();
|
||||
thisPtr->disconnect();
|
||||
}
|
||||
},
|
||||
[weakThisPtr](const std::exception &err) {
|
||||
LOG_ERROR << err.what();
|
||||
auto thisPtr = weakThisPtr.lock();
|
||||
if (!thisPtr)
|
||||
return;
|
||||
thisPtr->disconnect();
|
||||
},
|
||||
"auth %s",
|
||||
thisPtr->password_.data());
|
||||
}
|
||||
}
|
||||
});
|
||||
redisAsyncSetDisconnectCallback(
|
||||
redisContext_, [](const redisAsyncContext *context, int status) {
|
||||
auto thisPtr = static_cast<RedisConnection *>(context->ev.data);
|
||||
|
||||
thisPtr->handleDisconnect();
|
||||
if (thisPtr->disconnectCallback_)
|
||||
{
|
||||
thisPtr->disconnectCallback_(thisPtr->shared_from_this());
|
||||
}
|
||||
|
||||
LOG_TRACE << "Disconnected from "
|
||||
<< thisPtr->serverAddr_.toIpPort();
|
||||
});
|
||||
}
|
||||
|
||||
void RedisConnection::handleDisconnect()
|
||||
{
|
||||
LOG_TRACE << "handleDisconnect";
|
||||
loop_->assertInLoopThread();
|
||||
while ((!resultCallbacks_.empty()) && (!exceptionCallbacks_.empty()))
|
||||
{
|
||||
if (exceptionCallbacks_.front())
|
||||
{
|
||||
exceptionCallbacks_.front()(
|
||||
RedisException(RedisErrorCode::kConnectionBroken,
|
||||
"Connection is broken"));
|
||||
}
|
||||
resultCallbacks_.pop();
|
||||
exceptionCallbacks_.pop();
|
||||
}
|
||||
status_ = ConnectStatus::kEnd;
|
||||
channel_->disableAll();
|
||||
channel_->remove();
|
||||
redisContext_->ev.addWrite = nullptr;
|
||||
redisContext_->ev.delWrite = nullptr;
|
||||
redisContext_->ev.addRead = nullptr;
|
||||
redisContext_->ev.delRead = nullptr;
|
||||
redisContext_->ev.cleanup = nullptr;
|
||||
redisContext_->ev.data = nullptr;
|
||||
}
|
||||
void RedisConnection::addWrite(void *userData)
|
||||
{
|
||||
auto thisPtr = static_cast<RedisConnection *>(userData);
|
||||
assert(thisPtr->channel_);
|
||||
thisPtr->channel_->enableWriting();
|
||||
}
|
||||
void RedisConnection::delWrite(void *userData)
|
||||
{
|
||||
auto thisPtr = static_cast<RedisConnection *>(userData);
|
||||
assert(thisPtr->channel_);
|
||||
thisPtr->channel_->disableWriting();
|
||||
}
|
||||
void RedisConnection::addRead(void *userData)
|
||||
{
|
||||
auto thisPtr = static_cast<RedisConnection *>(userData);
|
||||
assert(thisPtr->channel_);
|
||||
thisPtr->channel_->enableReading();
|
||||
}
|
||||
void RedisConnection::delRead(void *userData)
|
||||
{
|
||||
auto thisPtr = static_cast<RedisConnection *>(userData);
|
||||
assert(thisPtr->channel_);
|
||||
thisPtr->channel_->disableReading();
|
||||
}
|
||||
void RedisConnection::cleanup(void *userData)
|
||||
{
|
||||
LOG_TRACE << "cleanup";
|
||||
}
|
||||
|
||||
void RedisConnection::handleRedisRead()
|
||||
{
|
||||
redisAsyncHandleRead(redisContext_);
|
||||
}
|
||||
void RedisConnection::handleRedisWrite()
|
||||
{
|
||||
if (redisContext_->c.flags == REDIS_DISCONNECTING)
|
||||
{
|
||||
channel_->disableAll();
|
||||
channel_->remove();
|
||||
}
|
||||
redisAsyncHandleWrite(redisContext_);
|
||||
}
|
||||
|
||||
void RedisConnection::sendCommandInLoop(
|
||||
const std::string &command,
|
||||
RedisResultCallback &&resultCallback,
|
||||
RedisExceptionCallback &&exceptionCallback)
|
||||
{
|
||||
resultCallbacks_.emplace(std::move(resultCallback));
|
||||
exceptionCallbacks_.emplace(std::move(exceptionCallback));
|
||||
|
||||
redisAsyncFormattedCommand(
|
||||
redisContext_,
|
||||
[](redisAsyncContext *context, void *r, void *userData) {
|
||||
auto thisPtr = static_cast<RedisConnection *>(context->ev.data);
|
||||
thisPtr->handleResult(static_cast<redisReply *>(r));
|
||||
},
|
||||
nullptr,
|
||||
command.c_str(),
|
||||
command.length());
|
||||
}
|
||||
|
||||
void RedisConnection::handleResult(redisReply *result)
|
||||
{
|
||||
LOG_TRACE << "redis reply: " << result->type;
|
||||
auto commandCallback = std::move(resultCallbacks_.front());
|
||||
resultCallbacks_.pop();
|
||||
auto exceptionCallback = std::move(exceptionCallbacks_.front());
|
||||
exceptionCallbacks_.pop();
|
||||
if (result->type != REDIS_REPLY_ERROR)
|
||||
{
|
||||
commandCallback(RedisResult(result));
|
||||
}
|
||||
else
|
||||
{
|
||||
exceptionCallback(
|
||||
RedisException(RedisErrorCode::kRedisError,
|
||||
std::string{result->str, result->len}));
|
||||
}
|
||||
if (resultCallbacks_.empty())
|
||||
{
|
||||
assert(exceptionCallbacks_.empty());
|
||||
if (idleCallback_)
|
||||
{
|
||||
idleCallback_(shared_from_this());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RedisConnection::disconnect()
|
||||
{
|
||||
std::promise<int> pro;
|
||||
auto f = pro.get_future();
|
||||
auto thisPtr = shared_from_this();
|
||||
loop_->runInLoop([thisPtr, &pro]() {
|
||||
redisAsyncDisconnect(thisPtr->redisContext_);
|
||||
pro.set_value(1);
|
||||
});
|
||||
f.get();
|
||||
}
|
195
nosql_lib/redis/src/RedisConnection.h
Normal file
195
nosql_lib/redis/src/RedisConnection.h
Normal file
@ -0,0 +1,195 @@
|
||||
/**
|
||||
*
|
||||
* @file RedisConnection.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <drogon/utils/string_view.h>
|
||||
#include <drogon/nosql/RedisException.h>
|
||||
#include <drogon/nosql/RedisResult.h>
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
#include <trantor/net/InetAddress.h>
|
||||
#include <trantor/net/EventLoop.h>
|
||||
#include <trantor/net/Channel.h>
|
||||
#include <hiredis/async.h>
|
||||
#include <hiredis/hiredis.h>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
|
||||
namespace drogon
|
||||
{
|
||||
namespace nosql
|
||||
{
|
||||
enum class ConnectStatus
|
||||
{
|
||||
kNone = 0,
|
||||
kConnecting,
|
||||
kConnected,
|
||||
kEnd
|
||||
};
|
||||
class RedisConnection : public trantor::NonCopyable,
|
||||
public std::enable_shared_from_this<RedisConnection>
|
||||
{
|
||||
public:
|
||||
RedisConnection(const trantor::InetAddress &serverAddress,
|
||||
const std::string &password,
|
||||
trantor::EventLoop *loop);
|
||||
void setConnectCallback(
|
||||
const std::function<void(std::shared_ptr<RedisConnection> &&)>
|
||||
&callback)
|
||||
{
|
||||
connectCallback_ = callback;
|
||||
}
|
||||
void setDisconnectCallback(
|
||||
const std::function<void(std::shared_ptr<RedisConnection> &&)>
|
||||
&callback)
|
||||
{
|
||||
disconnectCallback_ = callback;
|
||||
}
|
||||
void setIdleCallback(
|
||||
const std::function<void(const std::shared_ptr<RedisConnection> &)>
|
||||
&callback)
|
||||
{
|
||||
idleCallback_ = callback;
|
||||
}
|
||||
static std::string getFormattedCommand(const string_view &command,
|
||||
va_list ap) noexcept(false)
|
||||
{
|
||||
char *cmd;
|
||||
auto len = redisvFormatCommand(&cmd, command.data(), ap);
|
||||
if (len == -1)
|
||||
{
|
||||
throw RedisException(RedisErrorCode::kInternalError,
|
||||
"Out of memory");
|
||||
}
|
||||
else if (len == -2)
|
||||
{
|
||||
throw RedisException(RedisErrorCode::kInternalError,
|
||||
"Invalid format string");
|
||||
}
|
||||
else if (len <= 0)
|
||||
{
|
||||
throw RedisException(RedisErrorCode::kInternalError,
|
||||
"Unknown format error");
|
||||
}
|
||||
std::string fullCommand{cmd, static_cast<size_t>(len)};
|
||||
free(cmd);
|
||||
return fullCommand;
|
||||
}
|
||||
void sendFormattedCommand(std::string &&command,
|
||||
RedisResultCallback &&resultCallback,
|
||||
RedisExceptionCallback &&exceptionCallback)
|
||||
{
|
||||
if (loop_->isInLoopThread())
|
||||
{
|
||||
sendCommandInLoop(command,
|
||||
std::move(resultCallback),
|
||||
std::move(exceptionCallback));
|
||||
}
|
||||
else
|
||||
{
|
||||
loop_->queueInLoop(
|
||||
[this,
|
||||
callback = std::move(resultCallback),
|
||||
exceptionCallback = std::move(exceptionCallback),
|
||||
command = std::move(command)]() mutable {
|
||||
sendCommandInLoop(command,
|
||||
std::move(callback),
|
||||
std::move(exceptionCallback));
|
||||
});
|
||||
}
|
||||
}
|
||||
void sendvCommand(string_view command,
|
||||
RedisResultCallback &&resultCallback,
|
||||
RedisExceptionCallback &&exceptionCallback,
|
||||
va_list ap)
|
||||
{
|
||||
LOG_TRACE << "redis command: " << command;
|
||||
try
|
||||
{
|
||||
auto fullCommand = getFormattedCommand(command, ap);
|
||||
if (loop_->isInLoopThread())
|
||||
{
|
||||
sendCommandInLoop(fullCommand,
|
||||
std::move(resultCallback),
|
||||
std::move(exceptionCallback));
|
||||
}
|
||||
else
|
||||
{
|
||||
loop_->queueInLoop(
|
||||
[this,
|
||||
callback = std::move(resultCallback),
|
||||
exceptionCallback = std::move(exceptionCallback),
|
||||
fullCommand = std::move(fullCommand)]() mutable {
|
||||
sendCommandInLoop(fullCommand,
|
||||
std::move(callback),
|
||||
std::move(exceptionCallback));
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (const RedisException &err)
|
||||
{
|
||||
exceptionCallback(err);
|
||||
}
|
||||
}
|
||||
|
||||
~RedisConnection()
|
||||
{
|
||||
LOG_TRACE << (int)status_;
|
||||
if (redisContext_ && status_ != ConnectStatus::kEnd)
|
||||
redisAsyncDisconnect(redisContext_);
|
||||
}
|
||||
void disconnect();
|
||||
void sendCommand(RedisResultCallback &&resultCallback,
|
||||
RedisExceptionCallback &&exceptionCallback,
|
||||
string_view command,
|
||||
...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, command);
|
||||
sendvCommand(command,
|
||||
std::move(resultCallback),
|
||||
std::move(exceptionCallback),
|
||||
args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
private:
|
||||
redisAsyncContext *redisContext_{nullptr};
|
||||
const trantor::InetAddress serverAddr_;
|
||||
const std::string password_;
|
||||
trantor::EventLoop *loop_{nullptr};
|
||||
std::unique_ptr<trantor::Channel> channel_{nullptr};
|
||||
std::function<void(std::shared_ptr<RedisConnection> &&)> connectCallback_;
|
||||
std::function<void(std::shared_ptr<RedisConnection> &&)>
|
||||
disconnectCallback_;
|
||||
std::function<void(const std::shared_ptr<RedisConnection> &)> idleCallback_;
|
||||
std::queue<RedisResultCallback> resultCallbacks_;
|
||||
std::queue<RedisExceptionCallback> exceptionCallbacks_;
|
||||
ConnectStatus status_{ConnectStatus::kNone};
|
||||
void startConnectionInLoop();
|
||||
static void addWrite(void *userData);
|
||||
static void delWrite(void *userData);
|
||||
static void addRead(void *userData);
|
||||
static void delRead(void *userData);
|
||||
static void cleanup(void *userData);
|
||||
void handleRedisRead();
|
||||
void handleRedisWrite();
|
||||
void handleResult(redisReply *result);
|
||||
void sendCommandInLoop(const std::string &command,
|
||||
RedisResultCallback &&resultCallback,
|
||||
RedisExceptionCallback &&exceptionCallback);
|
||||
void handleDisconnect();
|
||||
};
|
||||
using RedisConnectionPtr = std::shared_ptr<RedisConnection>;
|
||||
} // namespace nosql
|
||||
} // namespace drogon
|
127
nosql_lib/redis/src/RedisResult.cc
Normal file
127
nosql_lib/redis/src/RedisResult.cc
Normal file
@ -0,0 +1,127 @@
|
||||
/**
|
||||
*
|
||||
* @file RedisResult.cc
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#include <drogon/nosql/RedisResult.h>
|
||||
#include <drogon/nosql/RedisClient.h>
|
||||
#include <hiredis/hiredis.h>
|
||||
|
||||
using namespace drogon::nosql;
|
||||
|
||||
std::string RedisResult::getStringForDisplaying() const noexcept
|
||||
{
|
||||
return getStringForDisplayingWithIndent(0);
|
||||
}
|
||||
|
||||
std::string RedisResult::getStringForDisplayingWithIndent(
|
||||
size_t indent) const noexcept
|
||||
{
|
||||
switch (result_->type)
|
||||
{
|
||||
case REDIS_REPLY_STRING:
|
||||
return "\"" + std::string{result_->str, result_->len} + "\"";
|
||||
case REDIS_REPLY_STATUS:
|
||||
return std::string{result_->str, result_->len};
|
||||
case REDIS_REPLY_ERROR:
|
||||
return "'ERROR:" + std::string{result_->str, result_->len} + "'";
|
||||
case REDIS_REPLY_NIL:
|
||||
return "(nil)";
|
||||
case REDIS_REPLY_INTEGER:
|
||||
return std::to_string(result_->integer);
|
||||
case REDIS_REPLY_ARRAY:
|
||||
{
|
||||
std::string ret;
|
||||
for (size_t i = 0; i < result_->elements; ++i)
|
||||
{
|
||||
std::string lineNum = std::to_string(i + 1) + ") ";
|
||||
if (i > 0)
|
||||
{
|
||||
ret += std::string(indent, ' ');
|
||||
}
|
||||
ret += lineNum;
|
||||
ret += RedisResult(result_->element[i])
|
||||
.getStringForDisplayingWithIndent(lineNum.length());
|
||||
if (i != result_->elements - 1)
|
||||
{
|
||||
ret += '\n';
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
default:
|
||||
return "*";
|
||||
}
|
||||
}
|
||||
|
||||
std::string RedisResult::asString() const noexcept(false)
|
||||
{
|
||||
auto rtype = type();
|
||||
if (rtype == RedisResultType::kString ||
|
||||
rtype == RedisResultType::kStatus || rtype == RedisResultType::kError)
|
||||
{
|
||||
return std::string(result_->str, result_->len);
|
||||
}
|
||||
else if (rtype == RedisResultType::kInteger)
|
||||
{
|
||||
return std::to_string(result_->integer);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw RedisException(RedisErrorCode::kBadType, "bad type");
|
||||
}
|
||||
}
|
||||
|
||||
RedisResultType RedisResult::type() const noexcept
|
||||
{
|
||||
switch (result_->type)
|
||||
{
|
||||
case REDIS_REPLY_STRING:
|
||||
return RedisResultType::kString;
|
||||
case REDIS_REPLY_ARRAY:
|
||||
return RedisResultType::kArray;
|
||||
case REDIS_REPLY_INTEGER:
|
||||
return RedisResultType::kInteger;
|
||||
case REDIS_REPLY_NIL:
|
||||
return RedisResultType::kNil;
|
||||
case REDIS_REPLY_STATUS:
|
||||
return RedisResultType::kStatus;
|
||||
case REDIS_REPLY_ERROR:
|
||||
default:
|
||||
return RedisResultType::kError;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<RedisResult> RedisResult::asArray() const noexcept(false)
|
||||
{
|
||||
auto rtype = type();
|
||||
if (rtype == RedisResultType::kArray)
|
||||
{
|
||||
std::vector<RedisResult> array;
|
||||
for (size_t i = 0; i < result_->elements; ++i)
|
||||
{
|
||||
array.emplace_back(result_->element[i]);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
throw RedisException(RedisErrorCode::kBadType, "bad type");
|
||||
}
|
||||
long long RedisResult::asInteger() const noexcept(false)
|
||||
{
|
||||
if (type() == RedisResultType::kInteger)
|
||||
return result_->integer;
|
||||
throw RedisException(RedisErrorCode::kBadType, "bad type");
|
||||
}
|
||||
bool RedisResult::isNil() const noexcept
|
||||
{
|
||||
return type() == RedisResultType::kNil;
|
||||
}
|
83
nosql_lib/redis/src/RedisTransactionImpl.cc
Normal file
83
nosql_lib/redis/src/RedisTransactionImpl.cc
Normal file
@ -0,0 +1,83 @@
|
||||
/**
|
||||
*
|
||||
* @file RedisConnection.cc
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#include "RedisTransactionImpl.h"
|
||||
|
||||
using namespace drogon::nosql;
|
||||
|
||||
RedisTransactionImpl::RedisTransactionImpl(RedisConnectionPtr connPtr) noexcept
|
||||
: connPtr_(std::move(connPtr))
|
||||
{
|
||||
}
|
||||
|
||||
void RedisTransactionImpl::execute(RedisResultCallback &&resultCallback,
|
||||
RedisExceptionCallback &&exceptionCallback)
|
||||
{
|
||||
execCommandAsync(
|
||||
[thisPtr = shared_from_this(),
|
||||
resultCallback =
|
||||
std::move(resultCallback)](const RedisResult &result) {
|
||||
thisPtr->isExecutedOrCancelled_ = true;
|
||||
resultCallback(result);
|
||||
},
|
||||
std::move(exceptionCallback),
|
||||
"EXEC");
|
||||
}
|
||||
void RedisTransactionImpl::execCommandAsync(
|
||||
RedisResultCallback &&resultCallback,
|
||||
RedisExceptionCallback &&exceptionCallback,
|
||||
string_view command,
|
||||
...) noexcept
|
||||
{
|
||||
if (isExecutedOrCancelled_)
|
||||
{
|
||||
exceptionCallback(RedisException(RedisErrorCode::kTransactionCancelled,
|
||||
"Transaction was cancelled"));
|
||||
return;
|
||||
}
|
||||
va_list args;
|
||||
va_start(args, command);
|
||||
connPtr_->sendvCommand(
|
||||
command,
|
||||
std::move(resultCallback),
|
||||
[thisPtr = shared_from_this(),
|
||||
exceptionCallback =
|
||||
std::move(exceptionCallback)](const RedisException &err) {
|
||||
LOG_ERROR << err.what();
|
||||
thisPtr->isExecutedOrCancelled_ = true;
|
||||
exceptionCallback(err);
|
||||
},
|
||||
args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void RedisTransactionImpl::doBegin()
|
||||
{
|
||||
assert(!isExecutedOrCancelled_);
|
||||
execCommandAsync([](const RedisResult &result) {},
|
||||
[](const RedisException &err) {},
|
||||
"MULTI");
|
||||
}
|
||||
|
||||
RedisTransactionImpl::~RedisTransactionImpl()
|
||||
{
|
||||
if (!isExecutedOrCancelled_)
|
||||
{
|
||||
LOG_WARN << "The transaction is not executed before being destroyed";
|
||||
connPtr_->sendCommand([](const RedisResult &result) {},
|
||||
[](const RedisException &err) {},
|
||||
"DISCARD");
|
||||
}
|
||||
LOG_TRACE << "transaction is destroyed";
|
||||
}
|
55
nosql_lib/redis/src/RedisTransactionImpl.h
Normal file
55
nosql_lib/redis/src/RedisTransactionImpl.h
Normal file
@ -0,0 +1,55 @@
|
||||
/**
|
||||
*
|
||||
* @file RedisConnection.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "RedisConnection.h"
|
||||
#include <drogon/nosql/RedisClient.h>
|
||||
#include <memory>
|
||||
|
||||
namespace drogon
|
||||
{
|
||||
namespace nosql
|
||||
{
|
||||
class RedisTransactionImpl final
|
||||
: public RedisTransaction,
|
||||
public std::enable_shared_from_this<RedisTransactionImpl>
|
||||
{
|
||||
public:
|
||||
explicit RedisTransactionImpl(RedisConnectionPtr connection) noexcept;
|
||||
// virtual void cancel() override;
|
||||
void execute(RedisResultCallback &&resultCallback,
|
||||
RedisExceptionCallback &&exceptionCallback) override;
|
||||
void execCommandAsync(RedisResultCallback &&resultCallback,
|
||||
RedisExceptionCallback &&exceptionCallback,
|
||||
string_view command,
|
||||
...) noexcept override;
|
||||
std::shared_ptr<RedisTransaction> newTransaction() override
|
||||
{
|
||||
return shared_from_this();
|
||||
}
|
||||
void newTransactionAsync(
|
||||
const std::function<void(const std::shared_ptr<RedisTransaction> &)>
|
||||
&callback) override
|
||||
{
|
||||
callback(shared_from_this());
|
||||
}
|
||||
void doBegin();
|
||||
~RedisTransactionImpl() override;
|
||||
|
||||
private:
|
||||
bool isExecutedOrCancelled_{false};
|
||||
RedisConnectionPtr connPtr_;
|
||||
};
|
||||
} // namespace nosql
|
||||
} // namespace drogon
|
9
nosql_lib/redis/tests/CMakeLists.txt
Normal file
9
nosql_lib/redis/tests/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
||||
link_libraries(${PROJECT_NAME})
|
||||
|
||||
add_executable(redis_test
|
||||
redis_test.cc
|
||||
)
|
||||
|
||||
set_property(TARGET redis_test PROPERTY CXX_STANDARD ${DROGON_CXX_STANDARD})
|
||||
set_property(TARGET redis_test PROPERTY CXX_STANDARD_REQUIRED ON)
|
||||
set_property(TARGET redis_test PROPERTY CXX_EXTENSIONS OFF)
|
133
nosql_lib/redis/tests/redis_test.cc
Normal file
133
nosql_lib/redis/tests/redis_test.cc
Normal file
@ -0,0 +1,133 @@
|
||||
#include <drogon/nosql/RedisClient.h>
|
||||
#include <drogon/drogon.h>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#define RESET "\033[0m"
|
||||
#define RED "\033[31m" /* Red */
|
||||
#define GREEN "\033[32m" /* Green */
|
||||
using namespace std::chrono_literals;
|
||||
using namespace drogon::nosql;
|
||||
#ifdef __cpp_impl_coroutine
|
||||
#define TEST_COUNT 8
|
||||
#else
|
||||
#define TEST_COUNT 7
|
||||
#endif
|
||||
std::promise<int> pro;
|
||||
auto globalf = pro.get_future();
|
||||
int counter = 0;
|
||||
void addCount(int &count, std::promise<int> &pro)
|
||||
{
|
||||
++count;
|
||||
// LOG_DEBUG << count;
|
||||
if (count == TEST_COUNT)
|
||||
{
|
||||
pro.set_value(1);
|
||||
}
|
||||
}
|
||||
void testoutput(bool isGood, const std::string &testMessage)
|
||||
{
|
||||
if (isGood)
|
||||
{
|
||||
std::cout << GREEN << counter + 1 << ".\t" << testMessage << "\t\tOK\n";
|
||||
std::cout << RESET;
|
||||
addCount(counter, pro);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << RED << testMessage << "\t\tBAD\n";
|
||||
std::cout << RESET;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void doTest(const RedisClientPtr &redisClient)
|
||||
{
|
||||
// std::this_thread::sleep_for(1s);
|
||||
redisClient->newTransactionAsync([](const RedisTransactionPtr &transPtr) {
|
||||
// 1
|
||||
transPtr->execCommandAsync(
|
||||
[](const drogon::nosql::RedisResult &r) {
|
||||
testoutput(true, r.getStringForDisplaying());
|
||||
},
|
||||
[](const std::exception &err) { testoutput(false, err.what()); },
|
||||
"ping");
|
||||
// 2
|
||||
transPtr->execute(
|
||||
[](const drogon::nosql::RedisResult &r) {
|
||||
testoutput(true, r.getStringForDisplaying());
|
||||
},
|
||||
[](const std::exception &err) { testoutput(false, err.what()); });
|
||||
});
|
||||
// 3
|
||||
redisClient->execCommandAsync(
|
||||
[](const drogon::nosql::RedisResult &r) {
|
||||
testoutput(true, r.getStringForDisplaying());
|
||||
},
|
||||
[](const std::exception &err) { testoutput(false, err.what()); },
|
||||
"set %s %s",
|
||||
"id_123",
|
||||
"drogon");
|
||||
// 4
|
||||
redisClient->execCommandAsync(
|
||||
[](const drogon::nosql::RedisResult &r) {
|
||||
testoutput(r.type() == RedisResultType::kArray &&
|
||||
r.asArray().size() == 1,
|
||||
r.getStringForDisplaying());
|
||||
},
|
||||
[](const std::exception &err) { testoutput(false, err.what()); },
|
||||
"keys *");
|
||||
// 5
|
||||
redisClient->execCommandAsync(
|
||||
[](const drogon::nosql::RedisResult &r) {
|
||||
testoutput(r.asString() == "hello", r.getStringForDisplaying());
|
||||
},
|
||||
[](const RedisException &err) { testoutput(false, err.what()); },
|
||||
"echo %s",
|
||||
"hello");
|
||||
// 6
|
||||
redisClient->execCommandAsync(
|
||||
[](const drogon::nosql::RedisResult &r) {
|
||||
testoutput(true, r.getStringForDisplaying());
|
||||
},
|
||||
[](const RedisException &err) { testoutput(false, err.what()); },
|
||||
"flushall");
|
||||
// 7
|
||||
redisClient->execCommandAsync(
|
||||
|
||||
[](const drogon::nosql::RedisResult &r) {
|
||||
testoutput(r.type() == RedisResultType::kNil,
|
||||
r.getStringForDisplaying());
|
||||
},
|
||||
[](const RedisException &err) { testoutput(false, err.what()); },
|
||||
"get %s",
|
||||
"xxxxx");
|
||||
|
||||
std::cout << "start\n";
|
||||
#ifdef __cpp_impl_coroutine
|
||||
auto coro_test = [redisClient]() -> drogon::Task<> {
|
||||
// 8
|
||||
try
|
||||
{
|
||||
auto r = co_await redisClient->execCommandCoro("get %s", "haha");
|
||||
testoutput(r.type() == RedisResultType::kNil,
|
||||
r.getStringForDisplaying());
|
||||
}
|
||||
catch (const RedisException &err)
|
||||
{
|
||||
testoutput(false, err.what());
|
||||
}
|
||||
};
|
||||
drogon::sync_wait(coro_test());
|
||||
#endif
|
||||
globalf.get();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
drogon::app().setLogLevel(trantor::Logger::kWarn);
|
||||
auto redisClient = drogon::nosql::RedisClient::newRedisClient(
|
||||
trantor::InetAddress("127.0.0.1", 6379), 1);
|
||||
doTest(redisClient);
|
||||
std::cout << "Test passed\n";
|
||||
return 0;
|
||||
}
|
@ -49,7 +49,7 @@ using namespace drogon::orm;
|
||||
DbClientImpl::DbClientImpl(const std::string &connInfo,
|
||||
const size_t connNum,
|
||||
ClientType type)
|
||||
: connectionsNumber_(connNum),
|
||||
: numberOfConnections_(connNum),
|
||||
loops_(type == ClientType::Sqlite3
|
||||
? 1
|
||||
: (connNum < std::thread::hardware_concurrency()
|
||||
@ -66,7 +66,7 @@ DbClientImpl::DbClientImpl(const std::string &connInfo,
|
||||
if (type == ClientType::PostgreSQL)
|
||||
{
|
||||
std::thread([this]() {
|
||||
for (size_t i = 0; i < connectionsNumber_; ++i)
|
||||
for (size_t i = 0; i < numberOfConnections_; ++i)
|
||||
{
|
||||
auto loop = loops_.getNextLoop();
|
||||
loop->runInLoop([this, loop]() {
|
||||
@ -79,7 +79,7 @@ DbClientImpl::DbClientImpl(const std::string &connInfo,
|
||||
else if (type == ClientType::Mysql)
|
||||
{
|
||||
std::thread([this]() {
|
||||
for (size_t i = 0; i < connectionsNumber_; ++i)
|
||||
for (size_t i = 0; i < numberOfConnections_; ++i)
|
||||
{
|
||||
auto loop = loops_.getNextLoop();
|
||||
loop->runAfter(0.1 * (i + 1), [this, loop]() {
|
||||
@ -96,7 +96,7 @@ DbClientImpl::DbClientImpl(const std::string &connInfo,
|
||||
auto loop = loops_.getNextLoop();
|
||||
loop->runInLoop([this]() {
|
||||
std::lock_guard<std::mutex> lock(connectionsMutex_);
|
||||
for (size_t i = 0; i < connectionsNumber_; ++i)
|
||||
for (size_t i = 0; i < numberOfConnections_; ++i)
|
||||
{
|
||||
connections_.insert(newConnection(nullptr));
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ class DbClientImpl : public DbClient,
|
||||
virtual bool hasAvailableConnections() const noexcept override;
|
||||
|
||||
private:
|
||||
size_t connectionsNumber_;
|
||||
size_t numberOfConnections_;
|
||||
trantor::EventLoopThreadPool loops_;
|
||||
std::shared_ptr<SharedMutex> sharedMutexPtr_;
|
||||
|
||||
|
@ -48,14 +48,14 @@ DbClientLockFree::DbClientLockFree(const std::string &connInfo,
|
||||
size_t connectionNumberPerLoop)
|
||||
: connectionInfo_(connInfo),
|
||||
loop_(loop),
|
||||
connectionsNumber_(connectionNumberPerLoop)
|
||||
numberOfConnections_(connectionNumberPerLoop)
|
||||
{
|
||||
type_ = type;
|
||||
LOG_TRACE << "type=" << (int)type;
|
||||
if (type == ClientType::PostgreSQL || type == ClientType::Mysql)
|
||||
{
|
||||
loop_->queueInLoop([this]() {
|
||||
for (size_t i = 0; i < connectionsNumber_; ++i)
|
||||
for (size_t i = 0; i < numberOfConnections_; ++i)
|
||||
connectionHolders_.push_back(newConnection());
|
||||
});
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ class DbClientLockFree : public DbClient,
|
||||
std::string connectionInfo_;
|
||||
trantor::EventLoop *loop_;
|
||||
DbConnectionPtr newConnection();
|
||||
const size_t connectionsNumber_;
|
||||
const size_t numberOfConnections_;
|
||||
std::vector<DbConnectionPtr> connections_;
|
||||
std::vector<DbConnectionPtr> connectionHolders_;
|
||||
std::unordered_set<DbConnectionPtr> transSet_;
|
||||
|
24
test.sh
24
test.sh
@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
drogon_ctl_exec=`pwd`/build/drogon_ctl/drogon_ctl
|
||||
drogon_ctl_exec=$(pwd)/build/drogon_ctl/drogon_ctl
|
||||
echo ${drogon_ctl_exec}
|
||||
cd build/examples/
|
||||
|
||||
@ -11,14 +11,14 @@ parallel=1
|
||||
|
||||
# simulate ninja's parallelism
|
||||
case $(nproc) in
|
||||
1)
|
||||
parallel=$(( $(nproc) + 1 ))
|
||||
1)
|
||||
parallel=$(($(nproc) + 1))
|
||||
;;
|
||||
2)
|
||||
parallel=$(( $(nproc) + 1 ))
|
||||
2)
|
||||
parallel=$(($(nproc) + 1))
|
||||
;;
|
||||
*)
|
||||
parallel=$(( $(nproc) + 2 ))
|
||||
*)
|
||||
parallel=$(($(nproc) + 2))
|
||||
;;
|
||||
esac
|
||||
|
||||
@ -130,7 +130,7 @@ fi
|
||||
|
||||
cd ../views
|
||||
|
||||
echo "Hello, world!" >> hello.csp
|
||||
echo "Hello, world!" >>hello.csp
|
||||
|
||||
cd ../build
|
||||
cmake .. $cmake_gen
|
||||
@ -170,6 +170,14 @@ if [ "$1" = "-t" ]; then
|
||||
echo "Error in testing"
|
||||
exit -1
|
||||
fi
|
||||
if [ -f "./nosql_lib/redis/tests/redis_test" ]; then
|
||||
echo "Test redis"
|
||||
./nosql_lib/redis/tests/redis_test
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error in testing"
|
||||
exit -1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Everything is ok!"
|
||||
|
Loading…
x
Reference in New Issue
Block a user