Compare commits

..

No commits in common. "master" and "v1.8.5" have entirely different histories.

444 changed files with 7886 additions and 31999 deletions

View File

@ -53,7 +53,7 @@ ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4 ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4 ContinuationIndentWidth: 4
Cpp11BracedListStyle: true Cpp11BracedListStyle: true
DerivePointerAlignment: false DerivePointerAlignment: true
DisableFormat: false DisableFormat: false
FixNamespaceComments: true FixNamespaceComments: true
ForEachMacros: ForEachMacros:
@ -75,7 +75,6 @@ IndentCaseLabels: true
IndentPPDirectives: None IndentPPDirectives: None
IndentWidth: 4 IndentWidth: 4
IndentWrappedFunctionNames: false IndentWrappedFunctionNames: false
InsertNewlineAtEOF: true
JavaScriptQuotes: Leave JavaScriptQuotes: Leave
JavaScriptWrapImports: true JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false KeepEmptyLinesAtTheStartOfBlocks: false
@ -95,8 +94,7 @@ PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10 PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000 PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 2000 PenaltyReturnTypeOnItsOwnLine: 2000
PointerAlignment: Right PointerAlignment: Left
ReferenceAlignment: Right
RawStringFormats: RawStringFormats:
- Language: Cpp - Language: Cpp
Delimiters: Delimiters:
@ -126,8 +124,7 @@ RawStringFormats:
CanonicalDelimiter: '' CanonicalDelimiter: ''
BasedOnStyle: google BasedOnStyle: google
ReflowComments: true ReflowComments: true
SeparateDefinitionBlocks: Always SortIncludes: false
SortIncludes: Never
SortUsingDeclarations: true SortUsingDeclarations: true
SpaceAfterCStyleCast: false SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true SpaceAfterTemplateKeyword: true

View File

@ -1,11 +0,0 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"

View File

@ -1,4 +1,4 @@
name: Build & Test name: Build Drogon
on: on:
push: push:
@ -6,10 +6,6 @@ on:
pull_request: pull_request:
workflow_dispatch: workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env: env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: Release BUILD_TYPE: Release
@ -17,14 +13,14 @@ env:
jobs: jobs:
windows: windows:
name: windows/msvc - ${{ matrix.link }} name: windows/msvc - ${{ matrix.link }}
runs-on: windows-2022 runs-on: windows-2019
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
link: ["STATIC", "SHARED"] link: [ 'STATIC', 'SHARED' ]
steps: steps:
- name: Checkout Drogon source code - name: Checkout Drogon source code
uses: actions/checkout@v4 uses: actions/checkout@v2
with: with:
submodules: true submodules: true
fetch-depth: 0 fetch-depth: 0
@ -53,7 +49,6 @@ jobs:
-DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" \ -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" \
-DBUILD_CTL=ON \ -DBUILD_CTL=ON \
-DBUILD_EXAMPLES=ON \ -DBUILD_EXAMPLES=ON \
-DUSE_SPDLOG=ON \
-DCMAKE_INSTALL_PREFIX=../install \ -DCMAKE_INSTALL_PREFIX=../install \
-DCMAKE_POLICY_DEFAULT_CMP0091=NEW \ -DCMAKE_POLICY_DEFAULT_CMP0091=NEW \
-DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_STANDARD=17
@ -65,40 +60,119 @@ jobs:
shell: bash shell: bash
run: ./test.sh -w run: ./test.sh -w
macos: unix:
runs-on: macos-${{ matrix.osver }} name: ${{ matrix.buildname }}
runs-on: ${{ matrix.os }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
osver: [13, 14, 15] include:
- os: ubuntu-20.04
buildname: 'ubuntu-20.04/gcc'
link: SHARED
triplet: x64-linux
compiler: gcc_64
- os: ubuntu-20.04
buildname: 'ubuntu-20.04/gcc'
link: STATIC
triplet: x64-linux
compiler: gcc_64
- os: ubuntu-20.04
buildname: 'ubuntu-20.04/gcc-10'
link: STATIC
triplet: x64-linux
- os: ubuntu-20.04
buildname: 'ubuntu-20.04/c++14'
link: STATIC
triplet: x64-linux
compiler: gcc_64
- os: macos-latest
buildname: 'macos/clang'
link: STATIC
triplet: x64-osx
compiler: clang_64
steps: steps:
- name: Checkout Drogon source code - name: Checkout Drogon source code
uses: actions/checkout@v4 uses: actions/checkout@v2
with: with:
submodules: true submodules: true
fetch-depth: 0 fetch-depth: 0
- name: Install dependencies - name: (macOS) Install dependencies
# Already installed: brotli, zlib, lz4, sqlite3 if: runner.os == 'macOS'
run: brew install ninja jsoncpp mariadb hiredis redis spdlog postgresql@14 # Already installed: brotli, zlib, postgresql@14, lz4, sqlite3
run: brew install jsoncpp mariadb hiredis redis
- name: (Linux) Install dependencies
if: runner.os == 'Linux'
run: |
# Installing packages might fail as the github image becomes outdated
sudo apt update
# These aren't available or don't work well in vcpkg
sudo apt-get install -y libjsoncpp-dev uuid-dev libssl-dev zlib1g-dev libsqlite3-dev
sudo apt-get install -y libbrotli-dev
- name: (Linux) Install gcc-10
if: matrix.buildname == 'ubuntu-20.04/gcc-10'
run: sudo apt-get install -y gcc-10 g++-10
- name: (Linux) Install boost
if: matrix.buildname == 'ubuntu-20.04/c++14'
run: |
sudo apt-get update
sudo apt-get -y install libboost-all-dev
- name: (Linux) Install postgresql
if: matrix.os == 'ubuntu-20.04'
run: sudo apt-get -y install postgresql-all
- name: Export `shared`
run: |
[[ ${{ matrix.link }} == "SHARED" ]] && shared="ON" || shared="OFF"
echo "shared=$shared" >> $GITHUB_ENV
- name: Create Build Environment & Configure Cmake - name: Create Build Environment & Configure Cmake
# Some projects don't allow in-source building, so create a separate build directory # Some projects don't allow in-source building, so create a separate build directory
# We'll use this as our working directory for all subsequent commands # We'll use this as our working directory for all subsequent commands
if: matrix.buildname != 'ubuntu-20.04/gcc-10' && matrix.buildname != 'ubuntu-20.04/c++14'
run: | run: |
cmake -B build -G Ninja \ cmake -B build \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \ -DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DBUILD_TESTING=on \ -DBUILD_TESTING=on \
-DUSE_SPDLOG=ON \ -DBUILD_SHARED_LIBS=$shared
-DBUILD_SHARED_LIBS=OFF
- name: Create Build Environment & Configure Cmake (gcc-10)
# Some projects don't allow in-source building, so create a separate build directory
# We'll use this as our working directory for all subsequent commands
if: matrix.buildname == 'ubuntu-20.04/gcc-10'
run: |
cmake -B build \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DBUILD_TESTING=on \
-DCMAKE_CXX_FLAGS="-fcoroutines" \
-DBUILD_SHARED_LIBS=$shared
env:
CC: gcc-10
CXX: g++-10
- name: Create Build Environment & Configure Cmake (C++14)
# Some projects don't allow in-source building, so create a separate build directory
# We'll use this as our working directory for all subsequent commands
if: matrix.buildname == 'ubuntu-20.04/C++14'
run: |
cmake -B build \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DBUILD_TESTING=on \
-DCMAKE_CXX_STANDARD=14 \
-DBUILD_SHARED_LIBS=$shared
- name: Build - name: Build
working-directory: ./build working-directory: ./build
# Execute the build. You can specify a specific target with "--target <NAME>" # Execute the build. You can specify a specific target with "--target <NAME>"
run: ninja && sudo ninja install run: make -j $(nproc) && sudo make install
- name: Prepare for testing - name: (macOS) Prepare for testing
if: runner.os == 'macOS'
run: | run: |
brew tap homebrew/services
brew services restart postgresql@14 brew services restart postgresql@14
brew services start mariadb brew services start mariadb
brew services start redis brew services start redis
@ -110,122 +184,8 @@ jobs:
sleep 4 sleep 4
psql -c 'create user postgres superuser;' postgres psql -c 'create user postgres superuser;' postgres
- name: Test - name: (Linux) Prepare for testing
# Execute tests defined by the CMake configuration. if: runner.os == 'Linux'
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
run: ./test.sh -t
ubuntu:
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
link: [SHARED, STATIC]
compiler:
- cxx: g++
ver: 9
- cxx: g++
ver: 10
- cxx: g++
ver: 11
- cxx: g++
ver: 12
- cxx: g++
ver: 13
- cxx: clang++
ver: 11
- cxx: clang++
ver: 12
- cxx: clang++
ver: 13
- cxx: clang++
ver: 14
- cxx: clang++
ver: 15
- cxx: clang++
ver: 16
- cxx: clang++
ver: 17
include:
- link: STATIC
compiler:
cxx: g++
ver: 13
feature: coroutines
env:
CXX: ${{ matrix.compiler.cxx }}-${{ matrix.compiler.ver }}
steps:
- name: Checkout Drogon source code
uses: actions/checkout@v4
with:
submodules: true
fetch-depth: 0
- name: Install dependencies
run: |
# Installing packages might fail as the github image becomes outdated
sudo apt update
# These aren't available or don't work well in vcpkg
sudo apt-get install -y libjsoncpp-dev uuid-dev libssl-dev zlib1g-dev libsqlite3-dev
sudo apt-get install -y ninja-build libbrotli-dev
sudo apt-get install -y libspdlog-dev
- name: Install postgresql
run: |
sudo apt-get --purge remove postgresql postgresql-doc postgresql-common postgresql-client-common
sudo apt-get -y install postgresql-all
- name: Install g++-13
if: startsWith(matrix.compiler.cxx, 'g++') && matrix.compiler.ver == 13
run: |
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt-get install g++-${{ matrix.compiler.ver }}
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 13
- name: Install Clang
if: startsWith(matrix.compiler.cxx, 'clang') && matrix.compiler.ver < 13
run: sudo apt-get install clang-${{ matrix.compiler.ver }}
- name: Install Clang
if: startsWith(matrix.compiler.cxx, 'clang') && matrix.compiler.ver >= 13
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x ./llvm.sh
sudo ./llvm.sh ${{ matrix.compiler.ver }}
- name: Export `shared`
run: |
[[ ${{ matrix.link }} == "SHARED" ]] && shared="ON" || shared="OFF"
echo "shared=$shared" >> $GITHUB_ENV
- name: Create Build Environment & Configure Cmake
# Some projects don't allow in-source building, so create a separate build directory
# We'll use this as our working directory for all subsequent commands
if: matrix.compiler.feature != 'coroutines'
run: |
cmake -B build -G Ninja \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DBUILD_TESTING=on \
-DUSE_SPDLOG=ON \
-DBUILD_SHARED_LIBS=$shared
- name: Create Build Environment & Configure Cmake (coroutines)
# Some projects don't allow in-source building, so create a separate build directory
# We'll use this as our working directory for all subsequent commands
if: matrix.compiler.feature == 'coroutines'
run: |
cmake -B build -G Ninja \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DBUILD_TESTING=on \
-DUSE_SPDLOG=ON \
-DCMAKE_CXX_FLAGS="-fcoroutines" \
-DBUILD_SHARED_LIBS=$shared \
- name: Build
working-directory: ./build
# Execute the build. You can specify a specific target with "--target <NAME>"
run: ninja && sudo ninja install
- name: Prepare for testing
run: | run: |
sudo systemctl start postgresql sudo systemctl start postgresql
sleep 1 sleep 1
@ -235,3 +195,9 @@ jobs:
# Execute tests defined by the CMake configuration. # Execute tests defined by the CMake configuration.
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
run: ./test.sh -t run: ./test.sh -t
- name: Lint
if: matrix.os == 'ubuntu-20.04'
run: |
sudo apt install -y dos2unix
./format.sh && git diff --exit-code

View File

@ -9,10 +9,6 @@ on:
schedule: schedule:
- cron: '46 7 * * 5' - cron: '46 7 * * 5'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env: env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: Release BUILD_TYPE: Release
@ -40,7 +36,7 @@ jobs:
steps: steps:
- name: Checkout Drogon source code - name: Checkout Drogon source code
uses: actions/checkout@v4 uses: actions/checkout@v2
with: with:
submodules: true submodules: true
fetch-depth: 0 fetch-depth: 0
@ -49,18 +45,18 @@ jobs:
run: | run: |
sudo apt update sudo apt update
sudo apt-get install -y libjsoncpp-dev uuid-dev libssl-dev zlib1g-dev libsqlite3-dev sudo apt-get install -y libjsoncpp-dev uuid-dev libssl-dev zlib1g-dev libsqlite3-dev
sudo apt-get install -y ninja-build libbrotli-dev sudo apt-get install -y libbrotli-dev
- name: Create Build Environment & Configure Cmake - name: Create Build Environment & Configure Cmake
run: | run: |
cmake -B build -G Ninja \ cmake -B build \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \ -DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DBUILD_TESTING=on \ -DBUILD_TESTING=on \
-DBUILD_SHARED_LIBS=$SHARED -DBUILD_SHARED_LIBS=$SHARED
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v3 uses: github/codeql-action/init@v2
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file. # If you wish to specify custom queries, you can do so here or in a config file.
@ -72,9 +68,9 @@ jobs:
- name: Build - name: Build
working-directory: ./build working-directory: ./build
run: ninja && sudo ninja install run: make -j $(nproc) && sudo make install
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3 uses: github/codeql-action/analyze@v2
with: with:
category: "/language:${{matrix.language}}" category: "/language:${{matrix.language}}"

View File

@ -1,15 +0,0 @@
# Look for typos in the codebase using codespell.
# https://github.com/codespell-project/codespell#readme
name: codespell
on:
push:
branches: [master]
pull_request:
branches: [master]
jobs:
codespell:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: sudo apt-get install -y codespell
- run: codespell --ignore-words-list="coo,folx,ot,statics,xwindows,NotIn,aNULL," --skip="*.csp"

View File

@ -1,41 +0,0 @@
name: C++
on:
push:
branches: [master]
pull_request:
permissions:
contents: read
jobs:
format:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install dos2unix
run: sudo apt-get install -y dos2unix
- name: Install clang-format-17
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x ./llvm.sh
sudo ./llvm.sh 17
sudo apt-get install -y clang-format-17
- name: Check formatting
run: ./format.sh && git diff --exit-code
env:
CLANG_FORMAT: clang-format-17
cpplint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install cpplint
run: pip install cpplint
- name: Run lint
run: cpplint --recursive .

View File

@ -1,28 +0,0 @@
name: Build and Push Docker Image
on:
release:
types: [created] # 当新版本被创建时触发
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build Docker image
run: |
cd docker/ubuntu
docker build -t drogonframework/drogon:latest .
- name: Push Docker image
run: |
docker push drogonframework/drogon:latest

1
.gitignore vendored
View File

@ -35,6 +35,7 @@ build/
cmake-build-debug/ cmake-build-debug/
cmake-build-debug-visual-studio/ cmake-build-debug-visual-studio/
.idea/ .idea/
lib/inc/drogon/version.h
html/ html/
latex/ latex/
.vscode .vscode

1
.gitmodules vendored
View File

@ -1,4 +1,3 @@
[submodule "trantor"] [submodule "trantor"]
path = trantor path = trantor
url = https://github.com/an-tao/trantor.git url = https://github.com/an-tao/trantor.git
branch = master

180
CMakeLists.txt Normal file → Executable file
View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.5...3.31) cmake_minimum_required(VERSION 3.5)
project(drogon) project(drogon)
@ -13,7 +13,6 @@ option(BUILD_DOC "Build Doxygen documentation" OFF)
option(BUILD_BROTLI "Build Brotli" ON) option(BUILD_BROTLI "Build Brotli" ON)
option(BUILD_YAML_CONFIG "Build yaml config" ON) option(BUILD_YAML_CONFIG "Build yaml config" ON)
option(USE_SUBMODULE "Use trantor as a submodule" ON) option(USE_SUBMODULE "Use trantor as a submodule" ON)
option(USE_STATIC_LIBS_ONLY "Use only static libraries as dependencies" OFF)
include(CMakeDependentOption) include(CMakeDependentOption)
CMAKE_DEPENDENT_OPTION(BUILD_POSTGRESQL "Build with postgresql support" ON "BUILD_ORM" OFF) CMAKE_DEPENDENT_OPTION(BUILD_POSTGRESQL "Build with postgresql support" ON "BUILD_ORM" OFF)
@ -21,11 +20,10 @@ CMAKE_DEPENDENT_OPTION(LIBPQ_BATCH_MODE "Use batch mode for libpq" ON "BUILD_POS
CMAKE_DEPENDENT_OPTION(BUILD_MYSQL "Build with mysql support" ON "BUILD_ORM" OFF) CMAKE_DEPENDENT_OPTION(BUILD_MYSQL "Build with mysql support" ON "BUILD_ORM" OFF)
CMAKE_DEPENDENT_OPTION(BUILD_SQLITE "Build with sqlite3 support" ON "BUILD_ORM" OFF) CMAKE_DEPENDENT_OPTION(BUILD_SQLITE "Build with sqlite3 support" ON "BUILD_ORM" OFF)
CMAKE_DEPENDENT_OPTION(BUILD_REDIS "Build with redis support" ON "BUILD_ORM" OFF) CMAKE_DEPENDENT_OPTION(BUILD_REDIS "Build with redis support" ON "BUILD_ORM" OFF)
CMAKE_DEPENDENT_OPTION(USE_SPDLOG "Allow using the spdlog logging library" OFF "USE_SUBMODULE" OFF)
set(DROGON_MAJOR_VERSION 1) set(DROGON_MAJOR_VERSION 1)
set(DROGON_MINOR_VERSION 9) set(DROGON_MINOR_VERSION 8)
set(DROGON_PATCH_VERSION 11) set(DROGON_PATCH_VERSION 5)
set(DROGON_VERSION 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}") set(DROGON_VERSION_STRING "${DROGON_VERSION}")
@ -42,15 +40,9 @@ set(INSTALL_DROGON_CMAKE_DIR ${DEF_INSTALL_DROGON_CMAKE_DIR}
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
# Force MSVC to use UTF-8 because that's what we use. Otherwise it uses # Force MSVC to use UTF-8 because that's what we use. Otherwise it uses
# the default of whatever Windows sets and causes encoding issues. # the default of whatever Windows sets and causes encoding issues.
message(STATUS "You are using MSVC. Forcing to use UTF-8") message(STATUS "You are using MSVC. Forceing to use UTF-8")
add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>") add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>") add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
if (MSVC_VERSION GREATER_EQUAL 1914)
# Tells Visual Studio 2017 (15.7+) and newer to correctly set the value of the standard __cplusplus macro,
# instead of leaving it to 199711L and settings the effective c++ version in _MSVC_LANG
# Dropping support for older versions of VS would allow to only rely on __cplusplus
add_compile_options("/Zc:__cplusplus")
endif()
endif () endif ()
add_library(${PROJECT_NAME}) add_library(${PROJECT_NAME})
@ -67,7 +59,7 @@ if (BUILD_SHARED_LIBS)
SOVERSION ${DROGON_MAJOR_VERSION}) SOVERSION ${DROGON_MAJOR_VERSION})
target_link_libraries(${PROJECT_NAME} PUBLIC Threads::Threads) target_link_libraries(${PROJECT_NAME} PUBLIC Threads::Threads)
if (WIN32) if (WIN32)
target_link_libraries(${PROJECT_NAME} PUBLIC rpcrt4 crypt32 advapi32 ws2_32) target_link_libraries(${PROJECT_NAME} PUBLIC Rpcrt4 ws2_32 crypt32 Advapi32)
if (CMAKE_CXX_COMPILER_ID MATCHES MSVC) if (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
# Ignore MSVC C4251 and C4275 warning of exporting std objects with no dll export # Ignore MSVC C4251 and C4275 warning of exporting std objects with no dll export
# We export class to facilitate maintenance, thus if you compile # We export class to facilitate maintenance, thus if you compile
@ -78,19 +70,6 @@ if (BUILD_SHARED_LIBS)
endif () endif ()
endif (BUILD_SHARED_LIBS) endif (BUILD_SHARED_LIBS)
if(USE_STATIC_LIBS_ONLY)
set(CMAKE_FIND_LIBRARY_SUFFIXES "${CMAKE_STATIC_LIBRARY_SUFFIX}")
endif(USE_STATIC_LIBS_ONLY)
if(USE_SPDLOG)
find_package(spdlog CONFIG)
if(spdlog_FOUND)
message(STATUS "spdlog found!")
target_link_libraries(${PROJECT_NAME} PUBLIC spdlog::spdlog_header_only)
target_compile_definitions(${PROJECT_NAME} PUBLIC DROGON_SPDLOG_SUPPORT SPDLOG_FMT_EXTERNAL FMT_HEADER_ONLY)
endif(spdlog_FOUND)
endif(USE_SPDLOG)
if (NOT ${CMAKE_PLATFORM_NAME} STREQUAL "Windows" AND CMAKE_CXX_COMPILER_ID MATCHES GNU) if (NOT ${CMAKE_PLATFORM_NAME} STREQUAL "Windows" AND CMAKE_CXX_COMPILER_ID MATCHES GNU)
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Werror) target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Werror)
endif () endif ()
@ -109,8 +88,10 @@ if (NOT "${CMAKE_CXX_STANDARD}" STREQUAL "")
set(DROGON_CXX_STANDARD ${CMAKE_CXX_STANDARD}) set(DROGON_CXX_STANDARD ${CMAKE_CXX_STANDARD})
elseif (HAS_ANY AND HAS_STRING_VIEW AND HAS_COROUTINE) elseif (HAS_ANY AND HAS_STRING_VIEW AND HAS_COROUTINE)
set(DROGON_CXX_STANDARD 20) set(DROGON_CXX_STANDARD 20)
else () elseif (HAS_ANY AND HAS_STRING_VIEW)
set(DROGON_CXX_STANDARD 17) set(DROGON_CXX_STANDARD 17)
else ()
set(DROGON_CXX_STANDARD 14)
endif () endif ()
if(USE_SUBMODULE) if(USE_SUBMODULE)
target_include_directories( target_include_directories(
@ -121,7 +102,6 @@ endif()
target_include_directories( target_include_directories(
${PROJECT_NAME} ${PROJECT_NAME}
PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/lib/inc> PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/lib/inc>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/lib/inc>
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/orm_lib/inc> $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/orm_lib/inc>
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/nosql_lib/redis/inc> $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/nosql_lib/redis/inc>
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}> $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>
@ -147,7 +127,7 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Haiku")
elseif (NOT WIN32 AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD") elseif (NOT WIN32 AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
target_link_libraries(${PROJECT_NAME} PRIVATE dl) target_link_libraries(${PROJECT_NAME} PRIVATE dl)
elseif (WIN32) elseif (WIN32)
target_link_libraries(${PROJECT_NAME} PRIVATE shlwapi ws2_32 iphlpapi) target_link_libraries(${PROJECT_NAME} PRIVATE shlwapi)
endif () endif ()
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules/) list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules/)
@ -155,27 +135,59 @@ list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules/)
find_package(Filesystem COMPONENTS Final) find_package(Filesystem COMPONENTS Final)
if(CXX_FILESYSTEM_HAVE_FS) if(CXX_FILESYSTEM_HAVE_FS)
message(STATUS "Found std::filesystem") message(STATUS "Found std::filesystem")
else ()
message(FATAL_ERROR "Not found std::filesystem, please use a newer compiler")
endif() endif()
if (DROGON_CXX_STANDARD EQUAL 17) # Check for C++ filesystem support
set(NEED_BOOST_FS 0)
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 "Using Boost filesystem, string_view and any")
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})
set(NEED_BOOST_FS 1)
elseif (DROGON_CXX_STANDARD EQUAL 17)
# With C++17, use Boost if std::filesystem::path is missing
message(STATUS "use c++17") message(STATUS "use c++17")
# Check for partial implementation of c++17 (Windows/OSX only?) # Check for partial implementation of c++17 (Windows/OSX only?)
if (CXX_FILESYSTEM_HAVE_FS)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
try_compile(check_filesystem_path ${CMAKE_BINARY_DIR}/cmaketest try_compile(check_filesystem_path ${CMAKE_BINARY_DIR}/cmaketest
${PROJECT_SOURCE_DIR}/cmake/tests/check_has_std_filesystem_path.cc ${PROJECT_SOURCE_DIR}/cmake/tests/check_has_std_filesystem_path.cc
CXX_STANDARD 17) CXX_STANDARD 17)
set(CMAKE_TRY_COMPILE_TARGET_TYPE) set(CMAKE_TRY_COMPILE_TARGET_TYPE)
if (NOT check_filesystem_path) if (NOT check_filesystem_path)
message(FATAL_ERROR "The std::filesystem seems to be a partial implementation," message(STATUS "The std::filesystem seems to be a partial implementation"
" please use a newer compiler") " Falling back to boost::filesystem")
set(NEED_BOOST_FS 1)
endif()
else()
set(NEED_BOOST_FS 1)
endif() endif()
else () else ()
message(STATUS "use c++20") message(STATUS "use c++20")
endif () endif ()
# Workaround: 2021-08-09 Android NDK does not provide proper std::filesystem
# support. Force boost::filesystem instead.
if (${CMAKE_SYSTEM_NAME} STREQUAL "Android")
message(STATUS "WORKAROUND: Forcing boost::filesystem on Android")
set(NEED_BOOST_FS 1)
endif ()
if(NEED_BOOST_FS)
find_package(Boost 1.49.0 COMPONENTS filesystem system REQUIRED)
message(STATUS "Using Boost filesytem::path")
message(STATUS "Boost include dir: " ${Boost_INCLUDE_DIR})
include_directories(${BOOST_INCLUDE_DIRS})
message(STATUS "Boost libraries: " ${Boost_LIBRARIES})
target_link_libraries(${PROJECT_NAME} PUBLIC Boost::filesystem Boost::system)
list(APPEND INCLUDE_DIRS_FOR_DYNAMIC_VIEW ${Boost_INCLUDE_DIR})
option(HAS_STD_FILESYSTEM_PATH "use boost::filesystem" OFF)
else()
option(HAS_STD_FILESYSTEM_PATH "use std::filesystem" ON) option(HAS_STD_FILESYSTEM_PATH "use std::filesystem" ON)
# HACK: Needed to be compiled on Yocto Linux # HACK: Needed to be compiled on Yocto Linux
if(TARGET std::filesystem) if(TARGET std::filesystem)
@ -184,7 +196,7 @@ if(TARGET std::filesystem)
target_link_libraries(${PROJECT_NAME} PUBLIC std::filesystem) target_link_libraries(${PROJECT_NAME} PUBLIC std::filesystem)
endif() endif()
endif() endif()
endif()
# jsoncpp # jsoncpp
find_package(Jsoncpp REQUIRED) find_package(Jsoncpp REQUIRED)
@ -224,18 +236,16 @@ if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD"
try_compile(normal_uuid ${CMAKE_BINARY_DIR}/cmaketest try_compile(normal_uuid ${CMAKE_BINARY_DIR}/cmaketest
${PROJECT_SOURCE_DIR}/cmake/tests/normal_uuid_lib_test.cc ${PROJECT_SOURCE_DIR}/cmake/tests/normal_uuid_lib_test.cc
LINK_LIBRARIES UUID_lib LINK_LIBRARIES UUID_lib)
OUTPUT_VARIABLE NORMAL_UUID_COMPILE_OUTPUT)
try_compile(ossp_uuid ${CMAKE_BINARY_DIR}/cmaketest try_compile(ossp_uuid ${CMAKE_BINARY_DIR}/cmaketest
${PROJECT_SOURCE_DIR}/cmake/tests/ossp_uuid_lib_test.cc ${PROJECT_SOURCE_DIR}/cmake/tests/ossp_uuid_lib_test.cc
LINK_LIBRARIES UUID_lib LINK_LIBRARIES UUID_lib)
OUTPUT_VARIABLE OSSP_UUID_COMPILE_OUTPUT)
if (normal_uuid) if (normal_uuid)
add_definitions(-DUSE_OSSP_UUID=0) add_definitions(-DUSE_OSSP_UUID=0)
elseif (ossp_uuid) elseif (ossp_uuid)
add_definitions(-DUSE_OSSP_UUID=1) add_definitions(-DUSE_OSSP_UUID=1)
else () else ()
message(FATAL_ERROR "uuid lib error:\n${NORMAL_UUID_COMPILE_OUTPUT}\n${OSSP_UUID_COMPILE_OUTPUT}") message(FATAL_ERROR "uuid lib error")
endif () endif ()
endif (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" endif (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD"
AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD" AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD"
@ -252,69 +262,62 @@ endif (BUILD_BROTLI)
set(DROGON_SOURCES set(DROGON_SOURCES
lib/src/AOPAdvice.cc lib/src/AOPAdvice.cc
lib/src/AccessLogger.cc
lib/src/CacheFile.cc lib/src/CacheFile.cc
lib/src/ConfigAdapterManager.cc
lib/src/ConfigLoader.cc lib/src/ConfigLoader.cc
lib/src/Cookie.cc lib/src/Cookie.cc
lib/src/DrClassMap.cc lib/src/DrClassMap.cc
lib/src/DrTemplateBase.cc lib/src/DrTemplateBase.cc
lib/src/MiddlewaresFunction.cc lib/src/FiltersFunction.cc
lib/src/FixedWindowRateLimiter.cc
lib/src/GlobalFilters.cc
lib/src/Histogram.cc
lib/src/Hodor.cc
lib/src/HttpAppFrameworkImpl.cc lib/src/HttpAppFrameworkImpl.cc
lib/src/HttpBinder.cc lib/src/HttpBinder.cc
lib/src/HttpClientImpl.cc lib/src/HttpClientImpl.cc
lib/src/HttpConnectionLimit.cc
lib/src/HttpControllerBinder.cc
lib/src/HttpControllersRouter.cc lib/src/HttpControllersRouter.cc
lib/src/HttpFileImpl.cc lib/src/HttpFileImpl.cc
lib/src/HttpFileUploadRequest.cc lib/src/HttpFileUploadRequest.cc
lib/src/HttpRequestImpl.cc lib/src/HttpRequestImpl.cc
lib/src/HttpRequestParser.cc lib/src/HttpRequestParser.cc
lib/src/RequestStream.cc
lib/src/HttpResponseImpl.cc lib/src/HttpResponseImpl.cc
lib/src/HttpResponseParser.cc lib/src/HttpResponseParser.cc
lib/src/HttpServer.cc lib/src/HttpServer.cc
lib/src/HttpSimpleControllersRouter.cc
lib/src/HttpUtils.cc lib/src/HttpUtils.cc
lib/src/HttpViewData.cc lib/src/HttpViewData.cc
lib/src/IntranetIpFilter.cc lib/src/IntranetIpFilter.cc
lib/src/JsonConfigAdapter.cc
lib/src/ListenerManager.cc lib/src/ListenerManager.cc
lib/src/LocalHostFilter.cc lib/src/LocalHostFilter.cc
lib/src/MultiPart.cc lib/src/MultiPart.cc
lib/src/MultipartStreamParser.cc
lib/src/NotFound.cc lib/src/NotFound.cc
lib/src/PluginsManager.cc lib/src/PluginsManager.cc
lib/src/PromExporter.cc
lib/src/RangeParser.cc lib/src/RangeParser.cc
lib/src/RateLimiter.cc
lib/src/RealIpResolver.cc
lib/src/SecureSSLRedirector.cc lib/src/SecureSSLRedirector.cc
lib/src/Redirector.cc lib/src/GlobalFilters.cc
lib/src/AccessLogger.cc
lib/src/RealIpResolver.cc
lib/src/SessionManager.cc lib/src/SessionManager.cc
lib/src/SlashRemover.cc
lib/src/SlidingWindowRateLimiter.cc
lib/src/StaticFileRouter.cc lib/src/StaticFileRouter.cc
lib/src/TaskTimeoutFlag.cc lib/src/TaskTimeoutFlag.cc
lib/src/TokenBucketRateLimiter.cc
lib/src/Utilities.cc lib/src/Utilities.cc
lib/src/WebSocketClientImpl.cc lib/src/WebSocketClientImpl.cc
lib/src/WebSocketConnectionImpl.cc lib/src/WebSocketConnectionImpl.cc
lib/src/YamlConfigAdapter.cc lib/src/WebsocketControllersRouter.cc
lib/src/drogon_test.cc) lib/src/RateLimiter.cc
lib/src/FixedWindowRateLimiter.cc
lib/src/SlidingWindowRateLimiter.cc
lib/src/TokenBucketRateLimiter.cc
lib/src/Hodor.cc
lib/src/SlashRemover.cc
lib/src/drogon_test.cc
lib/src/ConfigAdapterManager.cc
lib/src/JsonConfigAdapter.cc
lib/src/YamlConfigAdapter.cc)
set(private_headers set(private_headers
lib/src/AOPAdvice.h lib/src/AOPAdvice.h
lib/src/CacheFile.h lib/src/CacheFile.h
lib/src/ConfigLoader.h lib/src/ConfigLoader.h
lib/src/ControllerBinderBase.h lib/src/filesystem.h
lib/src/MiddlewaresFunction.h lib/src/FiltersFunction.h
lib/src/HttpAppFrameworkImpl.h lib/src/HttpAppFrameworkImpl.h
lib/src/HttpClientImpl.h lib/src/HttpClientImpl.h
lib/src/HttpConnectionLimit.h
lib/src/HttpControllerBinder.h
lib/src/HttpControllersRouter.h lib/src/HttpControllersRouter.h
lib/src/HttpFileImpl.h lib/src/HttpFileImpl.h
lib/src/HttpFileUploadRequest.h lib/src/HttpFileUploadRequest.h
@ -324,6 +327,7 @@ set(private_headers
lib/src/HttpResponseImpl.h lib/src/HttpResponseImpl.h
lib/src/HttpResponseParser.h lib/src/HttpResponseParser.h
lib/src/HttpServer.h lib/src/HttpServer.h
lib/src/HttpSimpleControllersRouter.h
lib/src/HttpUtils.h lib/src/HttpUtils.h
lib/src/impl_forwards.h lib/src/impl_forwards.h
lib/src/ListenerManager.h lib/src/ListenerManager.h
@ -334,30 +338,30 @@ set(private_headers
lib/src/TaskTimeoutFlag.h lib/src/TaskTimeoutFlag.h
lib/src/WebSocketClientImpl.h lib/src/WebSocketClientImpl.h
lib/src/WebSocketConnectionImpl.h lib/src/WebSocketConnectionImpl.h
lib/src/WebsocketControllersRouter.h
lib/src/FixedWindowRateLimiter.h lib/src/FixedWindowRateLimiter.h
lib/src/SlidingWindowRateLimiter.h lib/src/SlidingWindowRateLimiter.h
lib/src/TokenBucketRateLimiter.h lib/src/TokenBucketRateLimiter.h
lib/src/ConfigAdapterManager.h lib/src/ConfigAdapterManager.h
lib/src/JsonConfigAdapter.h lib/src/JsonConfigAdapter.h
lib/src/YamlConfigAdapter.h lib/src/YamlConfigAdapter.h
lib/src/ConfigAdapter.h lib/src/ConfigAdapter.h)
lib/src/MultipartStreamParser.h)
if (NOT WIN32 AND NOT CMAKE_SYSTEM_NAME STREQUAL "iOS") if (NOT WIN32)
set(DROGON_SOURCES set(DROGON_SOURCES
${DROGON_SOURCES} ${DROGON_SOURCES}
lib/src/SharedLibManager.cc) lib/src/SharedLibManager.cc)
set(private_headers set(private_headers
${private_headers} ${private_headers}
lib/src/SharedLibManager.h) lib/src/SharedLibManager.h)
elseif(WIN32) else (NOT WIN32)
set(DROGON_SOURCES set(DROGON_SOURCES
${DROGON_SOURCES} ${DROGON_SOURCES}
third_party/mman-win32/mman.c) third_party/mman-win32/mman.c)
set(private_headers set(private_headers
${private_headers} ${private_headers}
third_party/mman-win32/mman.h) third_party/mman-win32/mman.h)
endif() endif (NOT WIN32)
if (BUILD_POSTGRESQL) if (BUILD_POSTGRESQL)
# find postgres # find postgres
@ -511,7 +515,7 @@ execute_process(COMMAND "git" rev-parse HEAD
OUTPUT_VARIABLE GIT_SHA1 OUTPUT_VARIABLE GIT_SHA1
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
configure_file("${PROJECT_SOURCE_DIR}/cmake/templates/version.h.in" configure_file("${PROJECT_SOURCE_DIR}/cmake/templates/version.h.in"
"${CMAKE_CURRENT_BINARY_DIR}/lib/inc/drogon/version.h" @ONLY) "${PROJECT_SOURCE_DIR}/lib/inc/drogon/version.h" @ONLY)
if (DROGON_CXX_STANDARD EQUAL 20) if (DROGON_CXX_STANDARD EQUAL 20)
option(USE_COROUTINE "Enable C++20 coroutine support" ON) option(USE_COROUTINE "Enable C++20 coroutine support" ON)
@ -566,9 +570,7 @@ set(DROGON_HEADERS
lib/inc/drogon/HttpClient.h lib/inc/drogon/HttpClient.h
lib/inc/drogon/HttpController.h lib/inc/drogon/HttpController.h
lib/inc/drogon/HttpFilter.h lib/inc/drogon/HttpFilter.h
lib/inc/drogon/HttpMiddleware.h
lib/inc/drogon/HttpRequest.h lib/inc/drogon/HttpRequest.h
lib/inc/drogon/RequestStream.h
lib/inc/drogon/HttpResponse.h lib/inc/drogon/HttpResponse.h
lib/inc/drogon/HttpSimpleController.h lib/inc/drogon/HttpSimpleController.h
lib/inc/drogon/HttpTypes.h lib/inc/drogon/HttpTypes.h
@ -584,7 +586,7 @@ set(DROGON_HEADERS
lib/inc/drogon/WebSocketConnection.h lib/inc/drogon/WebSocketConnection.h
lib/inc/drogon/WebSocketController.h lib/inc/drogon/WebSocketController.h
lib/inc/drogon/drogon.h lib/inc/drogon/drogon.h
${CMAKE_CURRENT_BINARY_DIR}/lib/inc/drogon/version.h lib/inc/drogon/version.h
lib/inc/drogon/drogon_callbacks.h lib/inc/drogon/drogon_callbacks.h
lib/inc/drogon/PubSubService.h lib/inc/drogon/PubSubService.h
lib/inc/drogon/drogon_test.h lib/inc/drogon/drogon_test.h
@ -693,7 +695,6 @@ set(ORM_HEADERS
orm_lib/inc/drogon/orm/BaseBuilder.h orm_lib/inc/drogon/orm/BaseBuilder.h
orm_lib/inc/drogon/orm/Criteria.h orm_lib/inc/drogon/orm/Criteria.h
orm_lib/inc/drogon/orm/DbClient.h orm_lib/inc/drogon/orm/DbClient.h
orm_lib/inc/drogon/orm/DbConfig.h
orm_lib/inc/drogon/orm/DbListener.h orm_lib/inc/drogon/orm/DbListener.h
orm_lib/inc/drogon/orm/DbTypes.h orm_lib/inc/drogon/orm/DbTypes.h
orm_lib/inc/drogon/orm/Exception.h orm_lib/inc/drogon/orm/Exception.h
@ -717,37 +718,26 @@ set(NOSQL_HEADERS
install(FILES ${NOSQL_HEADERS} DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/nosql) install(FILES ${NOSQL_HEADERS} DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/nosql)
set(DROGON_UTIL_HEADERS set(DROGON_UTIL_HEADERS
lib/inc/drogon/utils/any.h
lib/inc/drogon/utils/apply.h
lib/inc/drogon/utils/coroutine.h lib/inc/drogon/utils/coroutine.h
lib/inc/drogon/utils/FunctionTraits.h lib/inc/drogon/utils/FunctionTraits.h
lib/inc/drogon/utils/HttpConstraint.h lib/inc/drogon/utils/HttpConstraint.h
lib/inc/drogon/utils/optional.h
lib/inc/drogon/utils/OStringStream.h lib/inc/drogon/utils/OStringStream.h
lib/inc/drogon/utils/Utilities.h lib/inc/drogon/utils/string_view.h
lib/inc/drogon/utils/monitoring.h) lib/inc/drogon/utils/Utilities.h)
install(FILES ${DROGON_UTIL_HEADERS} install(FILES ${DROGON_UTIL_HEADERS}
DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/utils) DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/utils)
set(DROGON_MONITORING_HEADERS
lib/inc/drogon/utils/monitoring/Counter.h
lib/inc/drogon/utils/monitoring/Metric.h
lib/inc/drogon/utils/monitoring/Registry.h
lib/inc/drogon/utils/monitoring/Collector.h
lib/inc/drogon/utils/monitoring/Sample.h
lib/inc/drogon/utils/monitoring/Gauge.h
lib/inc/drogon/utils/monitoring/Histogram.h)
install(FILES ${DROGON_MONITORING_HEADERS}
DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/utils/monitoring)
set(DROGON_PLUGIN_HEADERS set(DROGON_PLUGIN_HEADERS
lib/inc/drogon/plugins/Plugin.h lib/inc/drogon/plugins/Plugin.h
lib/inc/drogon/plugins/Redirector.h
lib/inc/drogon/plugins/SecureSSLRedirector.h lib/inc/drogon/plugins/SecureSSLRedirector.h
lib/inc/drogon/plugins/AccessLogger.h lib/inc/drogon/plugins/AccessLogger.h
lib/inc/drogon/plugins/RealIpResolver.h lib/inc/drogon/plugins/RealIpResolver.h
lib/inc/drogon/plugins/Hodor.h lib/inc/drogon/plugins/Hodor.h
lib/inc/drogon/plugins/SlashRemover.h lib/inc/drogon/plugins/SlashRemover.h
lib/inc/drogon/plugins/GlobalFilters.h lib/inc/drogon/plugins/GlobalFilters.h)
lib/inc/drogon/plugins/PromExporter.h)
install(FILES ${DROGON_PLUGIN_HEADERS} install(FILES ${DROGON_PLUGIN_HEADERS}
DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/plugins) DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/plugins)
@ -759,8 +749,7 @@ target_sources(${PROJECT_NAME} PRIVATE
${ORM_HEADERS} ${ORM_HEADERS}
${DROGON_UTIL_HEADERS} ${DROGON_UTIL_HEADERS}
${DROGON_PLUGIN_HEADERS} ${DROGON_PLUGIN_HEADERS}
${NOSQL_HEADERS} ${NOSQL_HEADERS})
${DROGON_MONITORING_HEADERS})
source_group("Public API" source_group("Public API"
FILES FILES
@ -768,8 +757,7 @@ source_group("Public API"
${ORM_HEADERS} ${ORM_HEADERS}
${DROGON_UTIL_HEADERS} ${DROGON_UTIL_HEADERS}
${DROGON_PLUGIN_HEADERS} ${DROGON_PLUGIN_HEADERS}
${NOSQL_HEADERS} ${NOSQL_HEADERS})
${DROGON_MONITORING_HEADERS})
source_group("Private Headers" source_group("Private Headers"
FILES FILES
${private_headers}) ${private_headers})

View File

@ -1,36 +0,0 @@
# Stop searching for additional config files.
set noparent
exclude_files=trantor
exclude_files=build
# Use non-const reference rather than a pointer.
filter=-runtime/references
# CHECK macros are from Drogon, not Google Test.
filter=-readability/check
# Don't warn about the use of C++11 or C++17 features.
filter=-build/c++11
filter=-build/c++17
filter=-build/include_subdir
# We prioritize clang-format for now.
filter=-whitespace
# We don't require a username in TODO comments.
filter=-readability/todo
# TODO: Fix these.
filter=-legal/copyright
filter=-build/namespaces
filter=-build/include
filter=-build/include_what_you_use
filter=-runtime/explicit
filter=-runtime/string
filter=-runtime/int
filter=-readability/casting
filter=-readability/braces
filter=-readability/fn_size
filter=-runtime/threadsafe_fn

View File

@ -4,442 +4,6 @@ All notable changes to this project will be documented in this file.
## [Unreleased] ## [Unreleased]
## [1.9.11] - 2025-06-20
### API changes list
- Add a new overload for execSqlCoro.
### Changed
- Do not write to source directory during build.
- Improve Postgres connection stability.
- Add handleFatalError in handleClosed.
- Add -o|--output option to drogon_ctl create models.
- Add qrcode for WeChat official account to the README file.
- Support for iOS compiling.
- Add cors example to demonstrate cross-origin support in drogon.
- Add support for continuation frame in WebSocketMessageParser.
- Add RawParameter API to pass raw SQL parameters.
- Upgrade Windows image and re-enable tests on Windows.
### Fixed
- Fix a bug in isAutoCreationClass<T>.
- Fix CI on MacOS.
- Fix issue with precision loss of double-type parameters in ORM inputs.
## [1.9.10] - 2025-02-20
### API changes list
- Add setConnectionCallback.
### Changed
- ORM:Avoid unnecessary copies when returning search results.
- Improve the zh-TW README translation.
- Make quit function thread safe.
- Added path_exempt in AccessLogger plugin config to exclude desired paths.
### Fixed
- Fix the issue in view generation by including the missing header file.
- Fix ci: codespell.
## [1.9.9] - 2025-01-01
### API changes list
- Added Partitioned flag for cookies.
### Changed
- Update FindFilesystem.cmake to check for GNU instead of GCC for CMAKE_CXX_COMPILER_ID.
- Update README.
- Chore(workflow/cmake.yml): upgrade macos runner.
- Add emptiness check to the LogStream &operator<< with std::string_view.
### Fixed
- Fix a bug in plugin Redirector.
- Fix CMAKE issues mentioned in #2144 and a linking problem which manifest with gcc12.3 when building with shared libs.
- Fix: Remove dependency on locales being installed on the system.
## [1.9.8] - 2024-10-27
### API changes list
- Add in-place base64 encode and decode.
- Add check the client connection status.
### Changed
- Add Hodor whitelists.
- Include exception header for std::exception_ptr.
- Add support for escaped identifiers in Postgresql.
- Remove content-length header from 101 Switching Protocols response.
- Remove websocketResponseTest from windows shared library env.
- Optimize query params and allow for empty values.
- Replace rejection sampling and remove use of rand().
- Add sending customized http requests to drogon_ctl.
### Fixed
- Fix coroutine continuation handle.
- Fix some bugs in plugin PromExporter.
- Fix a bug after removing content-length header in some responses.
## [1.9.7] - 2024-09-10
### API changes list
- Add coroutine mutex.
- Add requestsBufferSize function.
- Refine SQLite3 error types with new exception handling.
- Add a new method to reload SSL files on the fly.
### Changed
- Allow MultiPartParser to be movable.
- Add quotes to the table name in the ORM generator.
- Change stoi to stoul in the Field class.
- Modernize cookies.
- Change a log level.
### Fixed
- Use correct libraries when compiling statically.
## [1.9.6] - 2024-07-20
### API changes list
- Add setsockopt to HttpServer.
- Support request stream.
### Changed
- Allow MultiPartParser to parse PATCH requests.
- Add an example of prometheus.
- Delay parsing json for HttpClient.
- Update README.md.
### Fixed
- Fix some compilation warnings.
- Fix typo in yaml config.
## [1.9.5] - 2024-06-08
### API changes list
- Fix an error in the yaml format config file.
- Support postgresql connection options.
- Add regex support for websocket controller.
- Add the registerMiddleware method.
### Changed
- Add the conan badge to readme files.
- Install gcc in ci.
- Intention to present an alternative to improve the performance of a method in models.
### Fixed
- Fix an error in the yaml format config file.
- Fix CI on Windows.
- Fix some spelling errors.
## [1.9.4] - 2024-05-04
### API changes list
- Add client cert support for websocket.
- Add JSON send overloads for WebSocket connections.
### Changed
- Minor enhancement: move some smart pointers around instead of copying them.
- Remove the request shared_ptr from the multipart parser.
- Fix typo in HttpAppFrameworkImpl.cc.
- Avoid string copy and lowercasing on every request.
- Implemented database reconnection loop.
### Fixed
- Bypass clang thread_local error.
## [1.9.3] - 2024-02-09
### API changes list
- Added getParameter() and getOptionalParameter().
- Change drogon::MultiPartParser's parameters data type.
- Use std::string_view for WebSockets.
### Changed
- Add support for gentoo linux, dev-db/mariadb contains mysql.
- Introduce cpplint to the CI.
- Enable readability/alt_tokens for cpplint.
- Use clang-format-17.
- Add newline at EOF.
- Enable readability/inheritance for cpplint.
- Enable build/explicit_make_pair for cpplint.
- Enable build/include_order for cpplint.
- Enable build/header_guard for cpplint.
- Enable build/storage_class for cpplint.
- Enable readability/multiline_string for cpplint.
- Alias the safe hashmap template.
- Simplify traits in utils.
- Enhancement: extend drogon::ContentType for file handling.
### Fixed
- Fix a wrong place of return.
- Fix drogon::util::fromString().
## [1.9.2] - 2024-01-18
### API changes list
- Feature: Integrate spdlog as logging backend.
- Support asynchronous sending of chunked responses.
### Changed
- Modify the configuration file templates in drogon_ctl.
- Use execute_process instead of exec_program in FindJsoncpp.cmake.
- GitHub Action to find typos in the codebase using codespell.
- add discord link to readme.
- Add -k option to the drogon_ctl when running the press command.
- Refine request routing process.
- Add CI tests with more compilers.
- Avoid a race condition in database listener tests.
- Remove macos-11 CI; not supported by Homebrew.
- Bump github/codeql-action from 2 to 3.
- Move the RealIpResolver plugin to the PreRouting join point.
### Fixed
- Fix: typo on Mapper method.
- Fix a error of coroutines on Windows.
- Fix ORM: The original way did not handle exceptions correctly.
- Remove the default ctor of the Row class in ORM.
- Set the url of trantor to the official repository.
- Fix htonll/ntohll redefinition.
- Fix building with MSYS2.
- Fix name issue when cross-compiling.
## [1.9.1] - 2023-11-27
### API changes list
- Pass HttpRequestPtr to custom error handlers.
- Provide some functions for incrementing the value of given columns.
- Return HttpAppFramework by setExceptionHandler.
### Changed
- Custom sessions.
- Use the constexpr if instead of std::enable_if.
- Make id generator consistent.
- Update test_cmake.csp.
- Simplify drogon test with c++17.
- Remove unused and undefined overloads of isBase64.
### Fixed
- Fix build due to trantor commit out of date and address warnings.
- Fix a bug of the GlobalFilters plugin.
- Fix: uuid formatting.
## [1.9.0] - 2023-10-29
### API changes list
- Added isTopicEmpty function;
### Changed
- Update the ubuntu Dockerfile;
- Add optional Criteria && || operator support;
- Bump actions/checkout from 3 to 4;
- Make & and * directly adjacent to variable names;
- Use wss://echo.websocket.events/.ws in WebSocket client example;
- Change logs in the AccessLogger plugin to TRACE level;
### Fixed
- Fix an error in the secureRandomString function;
- FIX int mapping to int64_t instead of uint64_t;
## [1.9.0-rc.1] - 2023-09-23
### API changes list
- Drop cpp14 build support.
- Add isHead() method to HttpRequest, to preserve information about the original method for use in the controller.
- Allow omitting template parameter in execCommandSync.
- Add a method to HttpRequest to access the matched routing parameters.
### Changed
- Update readme files.
- Allow sync advice to be callable on websocket requests.
- Set concurrency to prevent blocking CI queue.
- Validate clang-format version & Customize clang-format path.
- Extract format action into distinct job.
- Split macOS and Ubuntu CIs for readability.
- Set concurrency for CodeQL.
- Add dependabot.yml for GH actions.
- Replace sprintf with snprintf.
- Use ninja to build faster.
- Avoid using well-known ports for demoMain.
- Simplify coroutine implementation.
- Add a plugin for prometheus.
- Optimize plugins with redirection functions.
- Optimize regex generator.
- Add override keyword to setSockOptCallback.
- SlashRemover optimization.
### Fixed
- Fix race condition when setting the secure flag during test.
## [1.8.6] - 2023-08-23
### Changed
- Show outputs of try_compile for UUID libs.
- Update Trantor to fix a serious bug when sending files.
## [1.8.5] - 2023-08-19 ## [1.8.5] - 2023-08-19
### API changes list ### API changes list
@ -520,7 +84,7 @@ All notable changes to this project will be documented in this file.
- Remove unused CI files and Jekyll config. - Remove unused CI files and Jekyll config.
- Ensure that all filters, AOP advice, and handlers are executed within the IO threads. - Ensure that all filters, AOP advices, and handlers are executed within the IO threads.
- Update test.sh and build.sh by appending prefix "X" to string variable comparisons. - Update test.sh and build.sh by appending prefix "X" to string variable comparisons.
@ -564,6 +128,7 @@ All notable changes to this project will be documented in this file.
- Fix broken link in CONTRIBUTING.md. - Fix broken link in CONTRIBUTING.md.
## [1.8.4] - 2023-03-19 ## [1.8.4] - 2023-03-19
### API Changes list ### API Changes list
@ -828,6 +393,7 @@ All notable changes to this project will be documented in this file.
- Add a pre-compilation macro in the pg pipeline test code. - Add a pre-compilation macro in the pg pipeline test code.
## [1.7.5] - 2022-02-19 ## [1.7.5] - 2022-02-19
### API changes list ### API changes list
@ -852,7 +418,7 @@ All notable changes to this project will be documented in this file.
- Check HTTP client is not sending requests in sync mode on the same event loop. - Check HTTP client is not sending requests in sync mode on the same event loop.
- Start listening after beginning advice. - Start listening after beginning advices.
- Allow using json_cpp in other sublibraries. - Allow using json_cpp in other sublibraries.
@ -954,7 +520,7 @@ All notable changes to this project will be documented in this file.
- Return on redis connection errors - Return on redis connection errors
- Fix(MultiPart): Does not respect quotes in Content-Disposition header - Fix(MutliPart): Does not respect quotes in Content-Disposition header
- Fix(cmake): error in FindFilesystem - Fix(cmake): error in FindFilesystem
@ -1112,7 +678,7 @@ All notable changes to this project will be documented in this file.
- Use two-phase construction for the DbClientImpl and the RedisClientImpl. - Use two-phase construction for the DbClientImpl and the RedisClientImpl.
- Add support 'select &lt;db&gt;' for redis. - Add support 'select <db>' for redis.
### Fixed ### Fixed
@ -1422,7 +988,7 @@ All notable changes to this project will be documented in this file.
- Destroy DNS resolver of HttpClient in the correct thread. - Destroy DNS resolver of HttpClient in the correct thread.
- Add the header &lt;cctype&gt; to resolve build errors in VS2017. - Add the header <cctype> to resolve build errors in VS2017.
## [1.0.0-beta18] - 2020-06-14 ## [1.0.0-beta18] - 2020-06-14
@ -1642,6 +1208,7 @@ All notable changes to this project will be documented in this file.
- Fix(compilation on alpine): Replace u_short alias. - Fix(compilation on alpine): Replace u_short alias.
## [1.0.0-beta9] - 2019-10-28 ## [1.0.0-beta9] - 2019-10-28
### API changes list ### API changes list
@ -1670,6 +1237,7 @@ All notable changes to this project will be documented in this file.
- Fix a busy loop bug when connections to MySQL server are timeout. - Fix a busy loop bug when connections to MySQL server are timeout.
## [1.0.0-beta8] - 2019-10-03 ## [1.0.0-beta8] - 2019-10-03
### API changes list ### API changes list
@ -1684,6 +1252,7 @@ All notable changes to this project will be documented in this file.
- Add thread storage. - Add thread storage.
### Changed ### Changed
- Use .find('x') instead of .find("x") in a string search. - Use .find('x') instead of .find("x") in a string search.
@ -1740,6 +1309,7 @@ All notable changes to this project will be documented in this file.
- Fix a bug in drogon_ctl (when creating orm models) - Fix a bug in drogon_ctl (when creating orm models)
## [1.0.0-beta6] - 2019-08-08 ## [1.0.0-beta6] - 2019-08-08
### API changes list ### API changes list
@ -1842,35 +1412,7 @@ All notable changes to this project will be documented in this file.
## [1.0.0-beta1] - 2019-06-11 ## [1.0.0-beta1] - 2019-06-11
[Unreleased]: https://github.com/an-tao/drogon/compare/v1.9.11...HEAD [Unreleased]: https://github.com/an-tao/drogon/compare/v1.8.5...HEAD
[1.9.11]: https://github.com/an-tao/drogon/compare/v1.9.10...v1.9.11
[1.9.10]: https://github.com/an-tao/drogon/compare/v1.9.9...v1.9.10
[1.9.9]: https://github.com/an-tao/drogon/compare/v1.9.8...v1.9.9
[1.9.8]: https://github.com/an-tao/drogon/compare/v1.9.7...v1.9.8
[1.9.7]: https://github.com/an-tao/drogon/compare/v1.9.6...v1.9.7
[1.9.6]: https://github.com/an-tao/drogon/compare/v1.9.5...v1.9.6
[1.9.5]: https://github.com/an-tao/drogon/compare/v1.9.4...v1.9.5
[1.9.4]: https://github.com/an-tao/drogon/compare/v1.9.3...v1.9.4
[1.9.3]: https://github.com/an-tao/drogon/compare/v1.9.2...v1.9.3
[1.9.2]: https://github.com/an-tao/drogon/compare/v1.9.1...v1.9.2
[1.9.1]: https://github.com/an-tao/drogon/compare/v1.9.0...v1.9.1
[1.9.0]: https://github.com/an-tao/drogon/compare/v1.9.0-rc.1...v1.9.0
[1.9.0-rc.1]: https://github.com/an-tao/drogon/compare/v1.8.6...v1.9.0-rc.1
[1.8.6]: https://github.com/an-tao/drogon/compare/v1.8.5...v1.8.6
[1.8.5]: https://github.com/an-tao/drogon/compare/v1.8.4...v1.8.5 [1.8.5]: https://github.com/an-tao/drogon/compare/v1.8.4...v1.8.5

View File

@ -1,14 +1,13 @@
![](https://github.com/an-tao/drogon/wiki/images/drogon-white17.jpg) ![](https://github.com/an-tao/drogon/wiki/images/drogon-white.jpg)
[![Build Status](https://github.com/drogonframework/drogon/actions/workflows/cmake.yml/badge.svg?branch=master)](https://github.com/drogonframework/drogon/actions) [![Build Status](https://github.com/an-tao/drogon/workflows/Build%20Drogon/badge.svg?branch=master)](https://github.com/drogonframework/drogon/actions)
[![Conan Center](https://img.shields.io/conan/v/drogon)](https://conan.io/center/recipes/drogon) [![Join the chat at https://gitter.im/drogon-web/community](https://badges.gitter.im/drogon-web/community.svg)](https://gitter.im/drogon-web/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Join the telegram group at https://t.me/joinchat/_mMNGv0748ZkMDAx](https://img.shields.io/badge/Telegram-2CA5E0?style=flat&logo=telegram&logoColor=white)](https://t.me/joinchat/_mMNGv0748ZkMDAx) [![Join the telegram group at https://t.me/joinchat/_mMNGv0748ZkMDAx](https://img.shields.io/badge/Telegram-2CA5E0?style=flat&logo=telegram&logoColor=white)](https://t.me/joinchat/_mMNGv0748ZkMDAx)
[![Join our Discord](https://dcbadge.vercel.app/api/server/3DvHY6Ewuj?style=flat)](https://discord.gg/3DvHY6Ewuj)
[![Docker image](https://img.shields.io/badge/Docker-image-blue.svg)](https://cloud.docker.com/u/drogonframework/repository/docker/drogonframework/drogon) [![Docker image](https://img.shields.io/badge/Docker-image-blue.svg)](https://cloud.docker.com/u/drogonframework/repository/docker/drogonframework/drogon)
English | [简体中文](./README.zh-CN.md) | [繁體中文](./README.zh-TW.md) English | [简体中文](./README.zh-CN.md) | [繁體中文](./README.zh-TW.md)
### Overview ### Overview
**Drogon** is a C++17/20 based HTTP application framework. Drogon can be used to easily build various types of web application server programs using C++. **Drogon** is the name of a dragon from the American TV series *Game of Thrones*, which I really enjoy. **Drogon** is a C++14/17-based HTTP application framework. Drogon can be used to easily build various types of web application server programs using C++. **Drogon** is the name of a dragon in the American TV series "Game of Thrones" that I really like.
Drogon is a cross-platform framework, It supports Linux, macOS, FreeBSD, OpenBSD, HaikuOS, and Windows. Its main features are as follows: Drogon is a cross-platform framework, It supports Linux, macOS, FreeBSD, OpenBSD, HaikuOS, and Windows. Its main features are as follows:
@ -34,7 +33,7 @@ Drogon is a cross-platform framework, It supports Linux, macOS, FreeBSD, OpenBSD
* Support ARM Architecture; * Support ARM Architecture;
* Provide a convenient lightweight ORM implementation that supports for regular object-to-database bidirectional mapping; * Provide a convenient lightweight ORM implementation that supports for regular object-to-database bidirectional mapping;
* Support plugins which can be installed by the configuration file at load time; * Support plugins which can be installed by the configuration file at load time;
* Support AOP with built-in joinpoints. * Support AOP with build-in joinpoints.
* Support C++ coroutines * Support C++ coroutines
## A very simple example ## A very simple example
@ -183,7 +182,7 @@ As you can see, users can use the `HttpController` to map paths and parameters a
In addition, you can also find that all handler interfaces are in asynchronous mode, where the response is returned by a callback object. This design is for performance reasons because in asynchronous mode the drogon application can handle a large number of concurrent requests with a small number of threads. In addition, you can also find that all handler interfaces are in asynchronous mode, where the response is returned by a callback object. This design is for performance reasons because in asynchronous mode the drogon application can handle a large number of concurrent requests with a small number of threads.
After compiling all of the above source files, we get a very simple web application. This is a good start. **For more information, please visit the [documentation](https://drogonframework.github.io/drogon-docs/#/) on GitHub**. After compiling all of the above source files, we get a very simple web application. This is a good start. **For more information, please visit the [wiki](https://github.com/an-tao/drogon/wiki/ENG-01-Overview)**
## Cross-compilation ## Cross-compilation

View File

@ -1,14 +1,13 @@
![](https://github.com/an-tao/drogon/wiki/images/drogon-white17.jpg) ![](https://github.com/an-tao/drogon/wiki/images/drogon-white.jpg)
[![Build Status](https://github.com/drogonframework/drogon/actions/workflows/cmake.yml/badge.svg?branch=master)](https://github.com/drogonframework/drogon/actions) [![Build Status](https://github.com/an-tao/drogon/workflows/Build%20Drogon/badge.svg?branch=master)](https://github.com/drogonframework/drogon/actions)
[![Conan Center](https://img.shields.io/conan/v/drogon)](https://conan.io/center/recipes/drogon) [![Join the chat at https://gitter.im/drogon-web/community](https://badges.gitter.im/drogon-web/community.svg)](https://gitter.im/drogon-web/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Join the telegram group at https://t.me/joinchat/_mMNGv0748ZkMDAx](https://img.shields.io/badge/Telegram-2CA5E0?style=flat&logo=telegram&logoColor=white)](https://t.me/joinchat/_mMNGv0748ZkMDAx) [![Join the telegram group at https://t.me/joinchat/_mMNGv0748ZkMDAx](https://img.shields.io/badge/Telegram-2CA5E0?style=flat&logo=telegram&logoColor=white)](https://t.me/joinchat/_mMNGv0748ZkMDAx)
[![Join our Discord](https://dcbadge.vercel.app/api/server/3DvHY6Ewuj?style=flat)](https://discord.gg/3DvHY6Ewuj)
[![Docker image](https://img.shields.io/badge/Docker-image-blue.svg)](https://cloud.docker.com/u/drogonframework/repository/docker/drogonframework/drogon) [![Docker image](https://img.shields.io/badge/Docker-image-blue.svg)](https://cloud.docker.com/u/drogonframework/repository/docker/drogonframework/drogon)
[English](./README.md) | 简体中文 | [繁體中文](./README.zh-TW.md) [English](./README.md) | 简体中文 | [繁體中文](./README.zh-TW.md)
**Drogon**是一个基于C++17/20的Http应用框架使用Drogon可以方便的使用C++构建各种类型的Web应用服务端程序。 **Drogon**是一个基于C++14/17的Http应用框架使用Drogon可以方便的使用C++构建各种类型的Web应用服务端程序。
本版本库是github上[Drogon工程](https://github.com/an-tao/drogon)的镜像库。**Drogon**是作者非常喜欢的美剧《权力的游戏》中的一条龙的名字(汉译作卓耿)和龙有关但并不是dragon的误写为了不至于引起不必要的误会这里说明一下。 本版本库是github上[Drogon工程](https://github.com/an-tao/drogon)的镜像库。**Drogon**是作者非常喜欢的美剧《权力的游戏》中的一条龙的名字(汉译作卓耿)和龙有关但并不是dragon的误写为了不至于引起不必要的误会这里说明一下。
Drogon是一个跨平台框架它支持Linux也支持macOS、FreeBSDOpenBSDHaikuOS和Windows。它的主要特点如下 Drogon是一个跨平台框架它支持Linux也支持macOS、FreeBSDOpenBSDHaikuOS和Windows。它的主要特点如下
@ -186,7 +185,7 @@ class User : public drogon::HttpController<User>
另外,你可以发现前面所有的处理函数接口都是异步的,处理器的响应是通过回调对象返回的。这种设计是出于对高性能的考虑,因为在异步模式下,可以使用少量的线程(比如和处理器核心数相等的线程)处理大量的并发请求。 另外,你可以发现前面所有的处理函数接口都是异步的,处理器的响应是通过回调对象返回的。这种设计是出于对高性能的考虑,因为在异步模式下,可以使用少量的线程(比如和处理器核心数相等的线程)处理大量的并发请求。
编译上述的所有源文件后我们得到了一个非常简单的web应用程序这是一个不错的开始。**请访问GitHub上的[文档](https://drogonframework.github.io/drogon-docs/#/CHN/CHN-01-%E6%A6%82%E8%BF%B0)** 编译上述的所有源文件后我们得到了一个非常简单的web应用程序这是一个不错的开始。**请访问[wiki](https://github.com/an-tao/drogon/wiki/CHN-01-概述)**
## 贡献方式 ## 贡献方式
@ -197,9 +196,3 @@ class User : public drogon::HttpController<User>
## QQ交流群1137909452 ## QQ交流群1137909452
欢迎交流探讨。 欢迎交流探讨。
## 微信公众号:
![](https://github.com/an-tao/drogon/wiki/images/qrcode_wechat.jpg)
会不定期推送一些Drogon的使用技巧和更新信息欢迎关注。

View File

@ -1,49 +1,47 @@
![](https://github.com/an-tao/drogon/wiki/images/drogon-white17.jpg) ![](https://github.com/an-tao/drogon/wiki/images/drogon-white.jpg)
[![Build Status](https://github.com/drogonframework/drogon/actions/workflows/cmake.yml/badge.svg?branch=master)](https://github.com/drogonframework/drogon/actions) [![Build Status](https://github.com/an-tao/drogon/workflows/Build%20Drogon/badge.svg?branch=master)](https://github.com/drogonframework/drogon/actions)
[![Conan Center](https://img.shields.io/conan/v/drogon)](https://conan.io/center/recipes/drogon) [![Join the chat at https://gitter.im/drogon-web/community](https://badges.gitter.im/drogon-web/community.svg)](https://gitter.im/drogon-web/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Join the telegram group at https://t.me/joinchat/_mMNGv0748ZkMDAx](https://img.shields.io/badge/Telegram-2CA5E0?style=flat&logo=telegram&logoColor=white)](https://t.me/joinchat/_mMNGv0748ZkMDAx) [![Join the telegram group at https://t.me/joinchat/_mMNGv0748ZkMDAx](https://img.shields.io/badge/Telegram-2CA5E0?style=flat&logo=telegram&logoColor=white)](https://t.me/joinchat/_mMNGv0748ZkMDAx)
[![Join our Discord](https://dcbadge.vercel.app/api/server/3DvHY6Ewuj?style=flat)](https://discord.gg/3DvHY6Ewuj)
[![Docker image](https://img.shields.io/badge/Docker-image-blue.svg)](https://cloud.docker.com/u/drogonframework/repository/docker/drogonframework/drogon) [![Docker image](https://img.shields.io/badge/Docker-image-blue.svg)](https://cloud.docker.com/u/drogonframework/repository/docker/drogonframework/drogon)
[English](./README.md) | [简体中文](./README.zh-CN.md) | 繁體中文 [English](./README.md) | [简体中文](./README.zh-CN.md) | 繁體中文
**Drogon** 是一個基於 C++17/20 的 HTTP 應用程式框架,使用 Drogon 可以方便地用 C++ 建立各種類型的 Web App 伺服器端程式。 **Drogon**是一個基於C++14/17的Http應用框架使用Drogon可以方便的使用C++構建各種類型的Web App伺服器程式。
本版本庫是github上[Drogon](https://github.com/an-tao/drogon)的鏡像庫。 **Drogon**是作者非常喜歡的美劇《冰與火之歌:權力遊戲》中的一條龍的名字(漢譯作卓耿)和龍有關但並不是dragon的誤寫為了不至於引起不必要的誤會這裡說明一下。
這個版本庫是 GitHub 上 [Drogon](https://github.com/an-tao/drogon) 的鏡像庫。**Drogon** 是作者非常喜歡的美劇《冰與火之歌:權力遊戲》中的一條龍的名字(中文譯作卓耿),和龍有關但並不是 dragon 的誤寫,為了避免不必要的誤會在此說明。 Drogon是一個跨平台框架它支援Linux也支援macOS、FreeBSD/OpenBSD、HaikuOS和Windows。它的主要特點如下
Drogon 是一個跨平台框架,支援 Linux、macOS、FreeBSD/OpenBSD、HaikuOS 和 Windows。主要特點如下 * 網路層使用基於epoll(macOS/FreeBSD下是kqueue)的非阻塞IO框架提供高並發、高性能的網路IO。詳細請見[TFB Tests Results](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=composite)
* 全異步程式設計;
* 網路層使用基於 epollmacOS/FreeBSD 下是 kqueue的非阻塞 IO 框架,提供高並行、高效能的網路 IO。詳細請見 [TFB Tests Results](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=composite) * 支援Http1.0/1.1(server端和client端)
* 完全非同步的程式撰寫邏輯; * 基於模板(template)實現了簡單的反射機制,使主程式框架、控制器(controller)和視圖(view)完全去耦;
* 支援 HTTP 1.0/1.1(伺服器端和用戶端);
* 基於樣板template實作的簡單反射機制使主程式框架、控制器controller和視圖view完全解耦
* 支援cookies和內建的session * 支援cookies和內建的session
* 支援後端算繪,將控制器產生的資料交給視圖產生 HTML 頁面,視圖由 CSP 樣板檔案描述,透過 CSP 標籤將 C++ 程式碼嵌入 HTML 頁面,由 drogon 的命令列工具在編譯階段自動產生 C++ 程式碼並編譯; * 支援後端渲染把控制器生成的數據交給視圖生成Html頁面視圖由CSP模板文件描述通過CSP標籤把C++程式碼嵌入到Html頁面由drogon的指令列工具在編譯階段自動生成C++程式碼並編譯;
* 支援執行期的視圖頁面動態載入(動態編譯和載入 so 檔案) * 支援運行期的視圖頁面動態加載(動態編譯和載入so文件)
* 非常方便靈活的路徑path到控制器處理函式handler的對應方案; * 非常方便靈活的路徑(path)到控制器處理函數(handler)的映射方案;
* 支援過濾器filter方便在控制器之前執行統一的邏輯如登入驗證、HTTP Method 限制驗證等) * 支援過濾器(filter)鏈,方便在控制器之前執行統一的邏輯(如登錄驗證、Http Method約束驗證等)
* 支援 HTTPS基於 OpenSSL * 支援https(基於OpenSSL);
* 支援 WebSocket伺服器端和用戶端 * 支援websocket(server端和client端);
* 支援 JSON 格式的請求和回應,方便開發 RESTful API * 支援Json格式的請求和回應, 方便開發Restful API;
* 支援檔案下載和上傳,支援 `sendfile` 系統呼叫; * 支援文件下載和上傳,支援sendfile系統呼叫;
* 支援 Gzip/Brotli 壓縮傳輸; * 支援gzip/brotli壓縮傳輸;
* 支援pipelining * 支援pipelining
* 提供輕量的命令列工具 `drogon_ctl`,幫助簡化各種類別的建立和視圖程式碼的產生過程; * 提供一個輕量的指令列工具drogon_ctl幫助簡化各種類的創造和視圖程式碼的生成過程;
* 非同步的讀寫資料庫,目前支援 PostgreSQL 和 MySQLMariaDB資料庫; * 非同步的讀寫資料庫,目前支援PostgreSQL和MySQL(MariaDB)資料庫;
* 支援非同步讀寫 Redis * 支援異步讀寫Redis;
* 基於執行緒池實作 sqlite3 資料庫的非同步讀寫,提供與上述資料庫相同的介面 * 基於執行序池實現sqlite3資料庫的異步讀寫提供與上文資料庫相同的接口
* 支援ARM架構 * 支援ARM架構
* 方便的輕量級 ORM 實現,一般物件到資料庫的雙向對應 * 方便的輕量級ORM實現一般物件到資料庫的雙向映射
* 支援外掛,可透過設定檔案在載入時動態載入; * 支援外掛,可通過設定文件在載入時動態載入;
* 支援內建插入點的 AOP * 支援內建插入點的AOP
* 支援 C++ coroutine * 支援C++ coroutine
## 一個非常簡單的例子 ## 一個非常簡單的例子
不像大多數 C++ 框架drogon 的主程式可以非常簡單。Drogon 使用了一些小技巧使主程式和控制器解耦。控制器的路由設定可以在控制器類別中定義或在設定檔案中完成。 不像大多數C++框架那樣drogon的主程式可以非常簡單。 Drogon使用了一些小技巧使主程式和控制器去耦. 控制器的路由設定可以在控制器類別中定義或者設定文件中完成.
下面是一個典型主程式的樣子: 下面是一個典型的主程式的樣子:
```c++ ```c++
#include <drogon/drogon.h> #include <drogon/drogon.h>
@ -59,7 +57,7 @@ int main()
} }
``` ```
如果使用設定檔案,可以進一步簡化成: 如果使用設定文件,可以進一步簡化成這樣:
```c++ ```c++
#include <drogon/drogon.h> #include <drogon/drogon.h>
@ -70,7 +68,7 @@ int main()
} }
``` ```
當然Drogon 也提供了一些函式,讓使用者可以在 `main()` 函式中直接加入控制器邏輯,例如,使用者可以註冊一個 lambda 處理常式到 drogon 框架中,如下所示: 當然Drogon也提供了一些函數使使用者可以在main()函數中直接添加控制器邏輯比如使用者可以註冊一個lambda處理器到drogon框架中,如下所示:
```c++ ```c++
app().registerHandler("/test?username={name}", app().registerHandler("/test?username={name}",
@ -87,7 +85,9 @@ app().registerHandler("/test?username={name}",
{Get,"LoginFilter"}); {Get,"LoginFilter"});
``` ```
這看起來很方便,但不適用於複雜的場景,試想如果有數十個或數百個處理函式要註冊進框架,`main()` 函式將變得難以閱讀。顯然,讓每個包含處理函式的類別在自己的定義中完成註冊是更好的選擇。所以,除非你的應用邏輯非常簡單,我們不建議使用上述介面,更好的做法是建立一個 HttpSimpleController 類別,如下:
這看起來是很方便但是這並不適用於復雜的場景試想假如有數十個或者數百個處理函數要註冊進框架main()函數將膨脹到不可讀的程度。顯然讓每個包含處理函數的類在自己的定義中完成註冊是更好的選擇。所以除非你的應用邏輯非常簡單我們不推薦使用上述接口更好的實踐是我們可以創造一個HttpSimpleController類別如下
```c++ ```c++
/// The TestCtrl.h file /// The TestCtrl.h file
@ -116,9 +116,9 @@ void TestCtrl::asyncHandleHttpRequest(const HttpRequestPtr& req,
} }
``` ```
**上述程式的大部分程式碼都可以由 `drogon_ctl` 指令產生**(使用指令 `drogon_ctl create controller TestCtr`)。使用者只需要加入自己的業務邏輯。在這個範例中,當用戶端存取 URL `http://ip/test` 時,控制器簡單地回傳一個 `Hello, world!` 頁面。 **上面程式的大部分程式碼都可以由`drogon_ctl`指令創造**(這個指令是`drogon_ctl create controller TestCtr`。使用者所需做的就是添加自己的業務邏輯。在這個例子中當客戶端訪問URL`http://ip/test`時,控制器簡單的回傳了一個`Hello, world!`頁面。
對於 JSON 格式的回應,我們可以這樣建立控制器: 對於JSON格式的回應我們可以像下面這樣創造控制器:
```c++ ```c++
/// The header file /// The header file
@ -147,7 +147,7 @@ void JsonCtrl::asyncHandleHttpRequest(const HttpRequestPtr &req,
} }
``` ```
讓我們更進一步,透過 HttpController 類別建立一個 RESTful API 的範例,如下所示(省略實作檔案 讓我們更進一步,通過HttpController類別創造一個RESTful API的例子如下所示忽略了實做文件
```c++ ```c++
/// The header file /// The header file
@ -181,18 +181,18 @@ class User : public drogon::HttpController<User>
} // namespace api } // namespace api
``` ```
如你所見,透過 `HttpController` 類別,使用者可以同時對應路徑和路徑參數,這對 RESTful API 應用來說非常方便。 如你所見,通過`HttpController`類別使用者可以同時映射路徑和路徑參數這對RESTful API應用來說非常方便。
另外,你可以發現前面所有的處理函式介面都是非同步的,處理器的回應是透過回呼物件回傳的。這種設計是考慮到效能,因為在非同步模式下,可以使用少量的執行緒(例如和處理器核心數相等的執行緒)處理大量的並行請求。 另外,你可以發現前面所有的處理函數接口都是異步的,處理器的回應是通過回調對象回傳的。這種設計是出於對高性能的考慮,因為在異步模式下,可以使用少量的執行序(比如和處理器核心數相等的執行序)處理大量的並發請求。
編譯上述所有原始檔案後,我們得到了一個非常簡單的網頁應用程式,這是一個不錯的開始。**請瀏覽 GitHub 上的[文件](https://drogonframework.github.io/drogon-docs/#/CHN/CHN-01-%E6%A6%82%E8%BF%B0)** 編譯上述的所有源文件後我們得到了一個非常簡單的web應用程式這是一個不錯的開始。 **請瀏覽[wiki](https://github.com/an-tao/drogon/wiki/CHN-01-概述)**
## 貢獻方式 ## 貢獻方式
歡迎您的貢獻。請閱讀[貢獻指南](CONTRIBUTING.md)以取得更多資訊 歡迎您的貢獻。請閱讀[貢獻指南](CONTRIBUTING.md)以獲取更多的信息
<a href="https://github.com/drogonframework/drogon/graphs/contributors"><img src="https://contributors-svg.opencollective.com/drogon/contributors.svg?width=890&button=false" alt="Code contributors" /></a> <a href="https://github.com/drogonframework/drogon/graphs/contributors"><img src="https://contributors-svg.opencollective.com/drogon/contributors.svg?width=890&button=false" alt="Code contributors" /></a>
## QQ交流群1137909452 ## QQ交流群1137909452
歡迎交流討 歡迎交流討。

View File

@ -17,7 +17,7 @@
# ParseAndAddDrogonTests(${PROJECT_NAME}) # # ParseAndAddDrogonTests(${PROJECT_NAME}) #
#==================================================================================================# #==================================================================================================#
cmake_minimum_required(VERSION 3.5...3.31) cmake_minimum_required(VERSION 3.5)
# This removes the contents between # This removes the contents between
# - block comments (i.e. /* ... */) # - block comments (i.e. /* ... */)

View File

@ -27,6 +27,10 @@ endif()
if(@MySQL_FOUND@) if(@MySQL_FOUND@)
find_dependency(MySQL) find_dependency(MySQL)
endif() endif()
if(@Boost_FOUND@)
find_dependency(Boost)
find_package(Boost COMPONENTS filesystem system)
endif()
if(@Brotli_FOUND@) if(@Brotli_FOUND@)
find_dependency(Brotli) find_dependency(Brotli)
endif() endif()

View File

@ -1,5 +1,3 @@
#pragma once
#define MAJOR @DROGON_MAJOR_VERSION@ #define MAJOR @DROGON_MAJOR_VERSION@
#define MINOR @DROGON_MINOR_VERSION@ #define MINOR @DROGON_MINOR_VERSION@
#define PATCH @DROGON_PATCH_VERSION@ #define PATCH @DROGON_PATCH_VERSION@

View File

@ -102,7 +102,7 @@ if(TARGET std::filesystem)
return() return()
endif() endif()
# Ignore filesystem check if version too low # Ignore fileystem check if version too low
if(CMAKE_VERSION VERSION_LESS 3.10) if(CMAKE_VERSION VERSION_LESS 3.10)
set(CXX_FILESYSTEM_HAVE_FS FALSE CACHE BOOL "TRUE if we have the C++ filesystem headers") set(CXX_FILESYSTEM_HAVE_FS FALSE CACHE BOOL "TRUE if we have the C++ filesystem headers")
set(Filesystem_FOUND FALSE CACHE BOOL "TRUE if we can run a program using std::filesystem" FORCE) set(Filesystem_FOUND FALSE CACHE BOOL "TRUE if we can run a program using std::filesystem" FORCE)
@ -212,7 +212,7 @@ if(CXX_FILESYSTEM_HAVE_FS)
]] code @ONLY) ]] code @ONLY)
# HACK: Needed to compile correctly on Yocto Linux # HACK: Needed to compile correctly on Yocto Linux
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang" if(CMAKE_CXX_COMPILER_ID STREQUAL "GCC" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang"
OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
set(CMAKE_REQUIRED_FLAGS ${prev_req_flags} -std=c++17) set(CMAKE_REQUIRED_FLAGS ${prev_req_flags} -std=c++17)
endif () endif ()

View File

@ -44,16 +44,13 @@ if(Jsoncpp_FOUND)
message(FATAL_ERROR "Error: jsoncpp lib is too old.....stop") message(FATAL_ERROR "Error: jsoncpp lib is too old.....stop")
endif() endif()
if(NOT WIN32) if(NOT WIN32)
execute_process( exec_program(
COMMAND cat ${JSONCPP_INCLUDE_DIRS}/json/version.h cat
COMMAND grep JSONCPP_VERSION_STRING ARGS
COMMAND sed -e "s/.*define/define/" "${JSONCPP_INCLUDE_DIRS}/json/version.h |grep JSONCPP_VERSION_STRING|sed s'/.*define/define/'|awk '{printf $3}'|sed s'/\"//g'"
COMMAND awk "{ printf \$3 }" OUTPUT_VARIABLE
COMMAND sed -e "s/\"//g" jsoncpp_ver)
OUTPUT_VARIABLE jsoncpp_ver) message(STATUS "jsoncpp verson:" ${jsoncpp_ver})
if(NOT Jsoncpp_FIND_QUIETLY)
message(STATUS "jsoncpp version:" ${jsoncpp_ver})
endif()
if(jsoncpp_ver LESS 1.7) if(jsoncpp_ver LESS 1.7)
message( message(
FATAL_ERROR FATAL_ERROR

View File

@ -27,26 +27,6 @@
# ############################################################################## # ##############################################################################
# -------------- FIND MYSQL_INCLUDE_DIRS ------------------ # -------------- FIND MYSQL_INCLUDE_DIRS ------------------
find_path(MARIADB_INCLUDE_DIRS
NAMES mysql.h
PATH_SUFFIXES mariadb
PATHS /usr/include/mysql
/usr/local/include/mysql
/usr/include/mariadb
/usr/local/include/mariadb
/opt/mysql/mysql/include
/opt/mysql/mysql/include/mysql
/opt/mysql/include
/opt/local/include/mysql5
/usr/local/mysql/include
/usr/local/mysql/include/mysql
/usr/local/mariadb/include
/usr/local/mariadb/include/mariadb
/opt/rh/rh-mariadb105/root/usr/include
/opt/rh/rh-mariadb105/root/usr/include/mysql
$ENV{ProgramFiles}/MySQL/*/include
$ENV{SystemDrive}/MySQL/*/include)
find_path(MYSQL_INCLUDE_DIRS find_path(MYSQL_INCLUDE_DIRS
NAMES mysql.h NAMES mysql.h
PATH_SUFFIXES mysql PATH_SUFFIXES mysql
@ -67,9 +47,7 @@ find_path(MYSQL_INCLUDE_DIRS
$ENV{ProgramFiles}/MySQL/*/include $ENV{ProgramFiles}/MySQL/*/include
$ENV{SystemDrive}/MySQL/*/include) $ENV{SystemDrive}/MySQL/*/include)
if(EXISTS "${MARIADB_INCLUDE_DIRS}/mysql.h") if(EXISTS "${MYSQL_INCLUDE_DIRS}/mysql.h")
set(MYSQL_INCLUDE_DIRS ${MARIADB_INCLUDE_DIRS})
elseif(EXISTS "${MYSQL_INCLUDE_DIRS}/mysql.h")
elseif(EXISTS "${MYSQL_INCLUDE_DIRS}/mysql/mysql.h") elseif(EXISTS "${MYSQL_INCLUDE_DIRS}/mysql/mysql.h")
set(MYSQL_INCLUDE_DIRS ${MYSQL_INCLUDE_DIRS}/mysql) set(MYSQL_INCLUDE_DIRS ${MYSQL_INCLUDE_DIRS}/mysql)
@ -99,7 +77,7 @@ if(WIN32)
$ENV{SystemDrive}/MySQL/*/lib/${libsuffixDist}) $ENV{SystemDrive}/MySQL/*/lib/${libsuffixDist})
else(WIN32) else(WIN32)
find_library(MYSQL_LIBRARIES find_library(MYSQL_LIBRARIES
NAMES mysqlclient_r mariadbclient mariadb NAMES mysqlclient_r mariadbclient
PATHS /usr/lib/mysql PATHS /usr/lib/mysql
/usr/lib/mariadb /usr/lib/mariadb
/usr/local/lib/mysql /usr/local/lib/mysql

View File

@ -39,7 +39,7 @@
"db_clients": [ "db_clients": [
{ {
//name: Name of the client,'default' by default //name: Name of the client,'default' by default
"name": "default", //"name":"",
//rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default //rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default
"rdbms": "postgresql", "rdbms": "postgresql",
//filename: Sqlite3 db file name //filename: Sqlite3 db file name
@ -66,18 +66,15 @@
//timeout: -1.0 by default, in seconds, the timeout for executing a SQL query. //timeout: -1.0 by default, in seconds, the timeout for executing a SQL query.
//zero or negative value means no timeout. //zero or negative value means no timeout.
"timeout": -1.0, "timeout": -1.0,
//auto_batch: this feature is only available for the PostgreSQL driver(version >= 14.0), see //"auto_batch": this feature is only available for the PostgreSQL driver(version >= 14.0), see
// the wiki for more details. // the wiki for more details.
"auto_batch": false "auto_batch": false
//connect_options: extra options for the connection. Only works for PostgreSQL now.
//For more information, see https://www.postgresql.org/docs/16/libpq-connect.html#LIBPQ-CONNECT-OPTIONS
//"connect_options": { "statement_timeout": "1s" }
} }
], ],
"redis_clients": [ "redis_clients": [
{ {
//name: Name of the client,'default' by default //name: Name of the client,'default' by default
"name": "default", //"name":"",
//host: Server IP, 127.0.0.1 by default //host: Server IP, 127.0.0.1 by default
"host": "127.0.0.1", "host": "127.0.0.1",
//port: Server port, 6379 by default //port: Server port, 6379 by default
@ -106,14 +103,10 @@
//enable_session: False by default //enable_session: False by default
"enable_session": true, "enable_session": true,
"session_timeout": 0, "session_timeout": 0,
//string value of SameSite attribute of the Set-Cookie HTTP response header //string value of SameSite attribute of the Set-Cookie HTTP respone header
//valid value is either 'Null' (default), 'Lax', 'Strict' or 'None' //valid value is either 'Null' (default), 'Lax', 'Strict' or 'None'
"session_same_site" : "Null", "session_same_site" : "Null",
//session_cookie_key: The cookie key of the session, "JSESSIONID" by default //document_root: Root path of HTTP document, defaut path is ./
"session_cookie_key": "JSESSIONID",
//session_max_age: The max age of the session cookie, -1 by default
"session_max_age": -1,
//document_root: Root path of HTTP document, default path is ./
"document_root": "./", "document_root": "./",
//home_page: Set the HTML file of the home page, the default value is "index.html" //home_page: Set the HTML file of the home page, the default value is "index.html"
//If there isn't any handler registered to the path "/", the home page file in the "document_root" is send to clients as a response //If there isn't any handler registered to the path "/", the home page file in the "document_root" is send to clients as a response
@ -187,7 +180,7 @@
], ],
//max_connections: maximum number of connections, 100000 by default //max_connections: maximum number of connections, 100000 by default
"max_connections": 100000, "max_connections": 100000,
//max_connections_per_ip: maximum number of connections per client, 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, "max_connections_per_ip": 0,
//Load_dynamic_views: False by default, when set to true, drogon //Load_dynamic_views: False by default, when set to true, drogon
//compiles and loads dynamically "CSP View Files" in directories defined //compiles and loads dynamically "CSP View Files" in directories defined
@ -217,8 +210,6 @@
}, },
//log: Set log output, drogon output logs to stdout by default //log: Set log output, drogon output logs to stdout by default
"log": { "log": {
//use_spdlog: Use spdlog library to log
"use_spdlog": false,
//log_path: Log file path,empty by default,in which case,logs are output to the stdout //log_path: Log file path,empty by default,in which case,logs are output to the stdout
//"log_path": "./", //"log_path": "./",
//logfile_base_name: Log file base name,empty by default which means drogon names logfile as //logfile_base_name: Log file base name,empty by default which means drogon names logfile as
@ -253,19 +244,19 @@
//0 means cache forever, the negative value means no cache //0 means cache forever, the negative value means no cache
"static_files_cache_time": 5, "static_files_cache_time": 5,
//simple_controllers_map: Used to configure mapping from path to simple controller //simple_controllers_map: Used to configure mapping from path to simple controller
//"simple_controllers_map": [ "simple_controllers_map": [
// { {
// "path": "/path/name", "path": "/path/name",
// "controller": "controllerClassName", "controller": "controllerClassName",
// "http_methods": [ "http_methods": [
// "get", "get",
// "post" "post"
// ], ],
// "filters": [ "filters": [
// "FilterClassName" "FilterClassName"
// ] ]
// } }
//], ],
//idle_connection_timeout: Defaults to 60 seconds, the lifetime //idle_connection_timeout: Defaults to 60 seconds, the lifetime
//of the connection without read or write //of the connection without read or write
"idle_connection_timeout": 60, "idle_connection_timeout": 60,
@ -310,38 +301,22 @@
// Currently only gzip and br are supported. Note: max_memory_body_size and max_body_size applies twice for compressed requests. // Currently only gzip and br are supported. Note: max_memory_body_size and max_body_size applies twice for compressed requests.
// Once when receiving and once when decompressing. i.e. if the decompressed body is larger than max_body_size, the request // Once when receiving and once when decompressing. i.e. if the decompressed body is larger than max_body_size, the request
// will be rejected. // will be rejected.
"enabled_compressed_request": false, "enabled_compressed_request": false
// enable_request_stream: Defaults to false. If true the server will enable stream mode for http requests.
// See the wiki for more details.
"enable_request_stream": false,
}, },
//plugins: Define all plugins running in the application //plugins: Define all plugins running in the application
"plugins": [ "plugins": [
{ {
//name: The class name of the plugin //name: The class name of the plugin
"name": "drogon::plugin::PromExporter", //"name": "drogon::plugin::SecureSSLRedirector",
//dependencies: Plugins that the plugin depends on. It can be commented out //dependencies: Plugins that the plugin depends on. It can be commented out
"dependencies": [], "dependencies": [],
//config: The configuration of the plugin. This json object is the parameter to initialize the plugin. //config: The configuration of the plugin. This json object is the parameter to initialize the plugin.
//It can be commented out //It can be commented out
"config": { "config": {
"path": "/metrics" "ssl_redirect_exempt": [
} ".*\\.jpg"
}, ],
{ "secure_ssl_host": "localhost:8849"
"name": "drogon::plugin::AccessLogger",
"dependencies": [],
"config": {
"use_spdlog": false,
"log_path": "",
"log_format": "",
"log_file": "access.log",
"log_size_limit": 0,
"use_local_time": true,
"log_index": 0,
// "show_microseconds": true,
// "custom_time_format": "",
// "use_real_ip": false
} }
} }
], ],

View File

@ -3,8 +3,8 @@
# ssl:The global SSL settings. "key" and "cert" are the path to the SSL key and certificate. While # ssl:The global SSL settings. "key" and "cert" are the path to the SSL key and certificate. While
# "conf" is an array of 1 or 2-element tuples that supplies file style options for `SSL_CONF_cmd`. # "conf" is an array of 1 or 2-element tuples that supplies file style options for `SSL_CONF_cmd`.
# ssl: # ssl:
# cert: ../../trantor/trantor/tests/server.crt # cert: ../../trantor/trantor/tests/server.pem
# key: ../../trantor/trantor/tests/server.key # key: ../../trantor/trantor/tests/server.pem
# conf: [ # conf: [
# # [Options, -SessionTicket], # # [Options, -SessionTicket],
# # [Options, Compression] # # [Options, Compression]
@ -30,11 +30,11 @@
# ] # ]
# db_clients: # db_clients:
# # name: Name of the client,'default' by default # # name: Name of the client,'default' by default
# - name: default # - name: ''
# # rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default # # rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default
# rdbms: postgresql # rdbms: postgresql
# # filename: Sqlite3 db file name # # filename: Sqlite3 db file name
# # filename: '' # # filename: '',
# # host: Server address,localhost by default # # host: Server address,localhost by default
# host: 127.0.0.1 # host: 127.0.0.1
# # port: Server port, 5432 by default # # port: Server port, 5432 by default
@ -50,23 +50,19 @@
# is_fast: false # is_fast: false
# # client_encoding: The character set used by the client. it is empty string by default which # # client_encoding: The character set used by the client. it is empty string by default which
# # means use the default character set. # # means use the default character set.
# # client_encoding: '' # # client_encoding: '',
# # number_of_connections: 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. # # connections per IO thread, otherwise it is the total number of all connections.
# number_of_connections: 1 # number_of_connections: 1
# # timeout: -1 by default, in seconds, the timeout for executing a SQL query. # # timeout: -1 by default, in seconds, the timeout for executing a SQL query.
# # zero or negative value means no timeout. # # zero or negative value means no timeout.
# timeout: -1 # timeout: -1
# # auto_batch: this feature is only available for the PostgreSQL driver(version >= 14.0), see # # "auto_batch": this feature is only available for the PostgreSQL driver(version >= 14.0), see
# # the wiki for more details. # # the wiki for more details.
# auto_batch: false # auto_batch: false
# # connect_options: extra options for the connection. Only works for PostgreSQL now.
# # For more information, see https://www.postgresql.org/docs/16/libpq-connect.html#LIBPQ-CONNECT-OPTIONS
# # connect_options:
# # statement_timeout: '1s'
# redis_clients: # redis_clients:
# # name: Name of the client,'default' by default # # name: Name of the client,'default' by default
# - name: default # - name: ''
# # host: Server IP, 127.0.0.1 by default # # host: Server IP, 127.0.0.1 by default
# host: 127.0.0.1 # host: 127.0.0.1
# # port: Server port, 6379 by default # # port: Server port, 6379 by default
@ -93,14 +89,10 @@ app:
# enable_session: False by default # enable_session: False by default
enable_session: true enable_session: true
session_timeout: 0 session_timeout: 0
# string value of SameSite attribute of the Set-Cookie HTTP response header # string value of SameSite attribute of the Set-Cookie HTTP respone header
# valid value is either 'Null' (default), 'Lax', 'Strict' or 'None' # valid value is either 'Null' (default), 'Lax', 'Strict' or 'None'
session_same_site: 'Null' session_same_site: 'Null'
# session_cookie_key: The cookie key of the session, "JSESSIONID" by default # document_root: Root path of HTTP document, defaut path is ./
session_cookie_key: 'JSESSIONID'
# session_max_age: The max age of the session cookie, -1 by default
session_max_age: -1
# document_root: Root path of HTTP document, default path is ./
document_root: ./ document_root: ./
# home_page: Set the HTML file of the home page, the default value is "index.html" # home_page: Set the HTML file of the home page, the default value is "index.html"
# If there isn't any handler registered to the path "/", the home page file in the "document_root" is send to clients as a response # If there isn't any handler registered to the path "/", the home page file in the "document_root" is send to clients as a response
@ -140,10 +132,8 @@ app:
# mime: A dictionary that extends the internal MIME type support. Maps extensions into new MIME types # mime: A dictionary that extends the internal MIME type support. Maps extensions into new MIME types
# note: This option only adds MIME to the sever. `file_types` above have to be set for the server to serve them. # note: This option only adds MIME to the sever. `file_types` above have to be set for the server to serve them.
mime: { mime: {
# text/markdown: md # text/markdown: md,
# text/gemini: # text/gemini: [gmi, gemini]
# - gmi
# - gemini
} }
# locations: An array of locations of static files for GET requests. # locations: An array of locations of static files for GET requests.
locations: locations:
@ -167,7 +157,7 @@ app:
filters: [] filters: []
# max_connections: maximum number of connections, 100000 by default # max_connections: maximum number of connections, 100000 by default
max_connections: 100000 max_connections: 100000
# max_connections_per_ip: maximum number of connections per client, 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 max_connections_per_ip: 0
# Load_dynamic_views: False by default, when set to true, drogon # Load_dynamic_views: False by default, when set to true, drogon
# compiles and loads dynamically "CSP View Files" in directories defined # compiles and loads dynamically "CSP View Files" in directories defined
@ -195,8 +185,6 @@ app:
precision_type: significant precision_type: significant
# log: Set log output, drogon output logs to stdout by default # log: Set log output, drogon output logs to stdout by default
log: log:
# use_spdlog: Use spdlog library to log
use_spdlog: false
# log_path: Log file path,empty by default,in which case,logs are output to the stdout # log_path: Log file path,empty by default,in which case,logs are output to the stdout
# log_path: ./ # log_path: ./
# logfile_base_name: Log file base name,empty by default which means drogon names logfile as # logfile_base_name: Log file base name,empty by default which means drogon names logfile as
@ -205,9 +193,6 @@ app:
# log_size_limit: 100000000 bytes by default, # log_size_limit: 100000000 bytes by default,
# When the log file size reaches "log_size_limit", the log file is switched. # When the log file size reaches "log_size_limit", the log file is switched.
log_size_limit: 100000000 log_size_limit: 100000000
# max_files: 0 by default,
# When the number of old log files exceeds "max_files", the oldest file will be deleted. 0 means never delete.
max_files: 0
# log_level: "DEBUG" by default,options:"TRACE","DEBUG","INFO","WARN" # log_level: "DEBUG" by default,options:"TRACE","DEBUG","INFO","WARN"
# The TRACE level is only valid when built in DEBUG mode. # The TRACE level is only valid when built in DEBUG mode.
log_level: DEBUG log_level: DEBUG
@ -230,14 +215,14 @@ app:
# 0 means cache forever, the negative value means no cache # 0 means cache forever, the negative value means no cache
static_files_cache_time: 5 static_files_cache_time: 5
# simple_controllers_map: Used to configure mapping from path to simple controller # simple_controllers_map: Used to configure mapping from path to simple controller
# simple_controllers_map: simple_controllers_map:
# - path: /path/name - path: /path/name
# controller: controllerClassName controller: controllerClassName
# http_methods: http_methods:
# - get - get
# - post - post
# filters: filters:
# - FilterClassName - FilterClassName
# idle_connection_timeout: Defaults to 60 seconds, the lifetime # idle_connection_timeout: Defaults to 60 seconds, the lifetime
# of the connection without read or write # of the connection without read or write
idle_connection_timeout: 60 idle_connection_timeout: 60
@ -278,37 +263,23 @@ app:
client_max_websocket_message_size: 128K client_max_websocket_message_size: 128K
# reuse_port: Defaults to false, users can run multiple processes listening on the same port at the same time. # reuse_port: Defaults to false, users can run multiple processes listening on the same port at the same time.
reuse_port: false reuse_port: false
# enabled_compressed_request: Defaults to false. If true the server will automatically decompress compressed request bodies. # enabled_compresed_request: Defaults to false. If true the server will automatically decompress compressed request bodies.
# Currently only gzip and br are supported. Note: max_memory_body_size and max_body_size applies twice for compressed requests. # Currently only gzip and br are supported. Note: max_memory_body_size and max_body_size applies twice for compressed requests.
# Once when receiving and once when decompressing. i.e. if the decompressed body is larger than max_body_size, the request # Once when receiving and once when decompressing. i.e. if the decompressed body is larger than max_body_size, the request
# will be rejected. # will be rejected.
enabled_compressed_request: false enabled_compresed_request: false
# enable_request_stream: Defaults to false. If true the server will enable stream mode for http requests.
# See the wiki for more details.
enable_request_stream: false
# plugins: Define all plugins running in the application # plugins: Define all plugins running in the application
plugins: plugins:
# name: The class name of the plugin # name: The class name of the plugin
- name: drogon::plugin::PromExporter - name: '' # drogon::plugin::SecureSSLRedirector
# dependencies: Plugins that the plugin depends on. It can be commented out # dependencies: Plugins that the plugin depends on. It can be commented out
dependencies: [] dependencies: []
# config: The configuration of the plugin. This json object is the parameter to initialize the plugin. # config: The configuration of the plugin. This json object is the parameter to initialize the plugin.
# It can be commented out # It can be commented out
config: config:
path: /metrics ssl_redirect_exempt:
- name: drogon::plugin::AccessLogger - .*\.jpg
dependencies: [] secure_ssl_host: 'localhost:8849'
config:
use_spdlog: false
log_path: ''
log_format: ''
log_file: access.log
log_size_limit: 0
use_local_time: true
log_index: 0
# show_microseconds: true
# custom_time_format: ''
# use_real_ip: false
# custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method. # custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method.
custom_config: custom_config:
realm: drogonRealm realm: drogonRealm
@ -316,3 +287,4 @@ custom_config:
credentials: credentials:
- user: drogon - user: drogon
password: dr0g0n password: dr0g0n

View File

@ -1,29 +1,29 @@
FROM ubuntu:22.04 FROM ubuntu:20.04
ENV TZ=UTC ENV TZ=UTC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN apt-get update -yqq \ RUN apt-get update -yqq \
&& apt-get install -yqq --no-install-recommends software-properties-common \ && apt-get install -yqq --no-install-recommends software-properties-common \
sudo curl wget cmake make pkg-config locales git gcc-11 g++-11 \ curl wget cmake make pkg-config locales git gcc-10 g++-10 \
openssl libssl-dev libjsoncpp-dev uuid-dev zlib1g-dev libc-ares-dev\ openssl libssl-dev libjsoncpp-dev uuid-dev zlib1g-dev libc-ares-dev\
postgresql-server-dev-all libmariadb-dev libsqlite3-dev libhiredis-dev\ postgresql-server-dev-all libmariadbclient-dev libsqlite3-dev libhiredis-dev\
&& rm -rf /var/lib/apt/lists/* \ && rm -rf /var/lib/apt/lists/* \
&& locale-gen en_US.UTF-8 && locale-gen en_US.UTF-8
ENV LANG=en_US.UTF-8 \ ENV LANG=en_US.UTF-8 \
LANGUAGE=en_US:en \ LANGUAGE=en_US:en \
LC_ALL=en_US.UTF-8 \ LC_ALL=en_US.UTF-8 \
CC=gcc-11 \ CC=gcc-10 \
CXX=g++-11 \ CXX=g++-10 \
AR=gcc-ar-11 \ AR=gcc-ar-10 \
RANLIB=gcc-ranlib-11 \ RANLIB=gcc-ranlib-10 \
IROOT=/install IROOT=/install
ENV DROGON_ROOT="$IROOT/drogon" ENV DROGON_ROOT="$IROOT/drogon"
ADD https://api.github.com/repos/drogonframework/drogon/git/refs/heads/master $IROOT/version.json ADD https://api.github.com/repos/an-tao/drogon/git/refs/heads/master $IROOT/version.json
RUN git clone https://github.com/drogonframework/drogon $DROGON_ROOT RUN git clone https://github.com/an-tao/drogon $DROGON_ROOT
WORKDIR $DROGON_ROOT WORKDIR $DROGON_ROOT

View File

@ -57,7 +57,7 @@ target_link_libraries(drogon_ctl PRIVATE ${PROJECT_NAME})
target_include_directories(drogon_ctl PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(drogon_ctl PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
add_dependencies(drogon_ctl _drogon_ctl) add_dependencies(drogon_ctl _drogon_ctl)
if(WIN32) if(WIN32)
target_link_libraries(drogon_ctl PRIVATE ws2_32 rpcrt4 iphlpapi) target_link_libraries(drogon_ctl PRIVATE ws2_32 Rpcrt4 iphlpapi)
endif(WIN32) endif(WIN32)
if(APPLE) if(APPLE)
target_link_libraries(drogon_ctl PRIVATE resolv) target_link_libraries(drogon_ctl PRIVATE resolv)

View File

@ -22,22 +22,18 @@ class CommandHandler : public virtual drogon::DrObjectBase
{ {
public: public:
virtual void handleCommand(std::vector<std::string> &parameters) = 0; virtual void handleCommand(std::vector<std::string> &parameters) = 0;
virtual bool isTopCommand() virtual bool isTopCommand()
{ {
return false; return false;
} }
virtual std::string script() virtual std::string script()
{ {
return ""; return "";
} }
virtual std::string detail() virtual std::string detail()
{ {
return ""; return "";
} }
virtual ~CommandHandler() virtual ~CommandHandler()
{ {
} }

View File

@ -18,7 +18,6 @@
#include <iostream> #include <iostream>
#include <memory> #include <memory>
using namespace drogon_ctl; using namespace drogon_ctl;
std::string create::detail() std::string create::detail()
{ {
return "Use create command to create some source files of drogon webapp\n\n" return "Use create command to create some source files of drogon webapp\n\n"
@ -42,8 +41,7 @@ std::string create::detail()
"create a plugin named class_name\n\n" "create a plugin named class_name\n\n"
"drogon_ctl create project <project_name> //" "drogon_ctl create project <project_name> //"
"create a project named project_name\n\n" "create a project named project_name\n\n"
"drogon_ctl create model <model_path> [-o <output path>] " "drogon_ctl create model <model_path> [--table=<table_name>] [-f]//"
"[--table=<table_name>] [-f]//"
"create model classes in model_path\n"; "create model classes in model_path\n";
} }

View File

@ -17,25 +17,21 @@
#include <drogon/DrObject.h> #include <drogon/DrObject.h>
#include "CommandHandler.h" #include "CommandHandler.h"
using namespace drogon; using namespace drogon;
namespace drogon_ctl namespace drogon_ctl
{ {
class create : public DrObject<create>, public CommandHandler class create : public DrObject<create>, public CommandHandler
{ {
public: public:
void handleCommand(std::vector<std::string> &parameters) override; void handleCommand(std::vector<std::string> &parameters) override;
std::string script() override std::string script() override
{ {
return "create some source files(Use 'drogon_ctl help create' for more " return "create some source files(Use 'drogon_ctl help create' for more "
"information)"; "information)";
} }
bool isTopCommand() override bool isTopCommand() override
{ {
return true; return true;
} }
std::string detail() override; std::string detail() override;
}; };
} // namespace drogon_ctl } // namespace drogon_ctl

View File

@ -135,7 +135,6 @@ void create_controller::newSimpleControllerHeaderFile(
file << "}\n"; file << "}\n";
} }
} }
void create_controller::newSimpleControllerSourceFile( void create_controller::newSimpleControllerSourceFile(
std::ofstream &file, std::ofstream &file,
const std::string &className, const std::string &className,
@ -208,7 +207,6 @@ void create_controller::newWebsockControllerHeaderFile(
file << "}\n"; file << "}\n";
} }
} }
void create_controller::newWebsockControllerSourceFile( void create_controller::newWebsockControllerSourceFile(
std::ofstream &file, std::ofstream &file,
const std::string &className, const std::string &className,
@ -305,7 +303,6 @@ void create_controller::newHttpControllerHeaderFile(
file << "}\n"; file << "}\n";
} }
} }
void create_controller::newHttpControllerSourceFile( void create_controller::newHttpControllerSourceFile(
std::ofstream &file, std::ofstream &file,
const std::string &className, const std::string &className,

View File

@ -18,7 +18,6 @@
#include <drogon/DrTemplateBase.h> #include <drogon/DrTemplateBase.h>
#include "CommandHandler.h" #include "CommandHandler.h"
using namespace drogon; using namespace drogon;
namespace drogon_ctl namespace drogon_ctl
{ {
class create_controller : public DrObject<create_controller>, class create_controller : public DrObject<create_controller>,
@ -26,7 +25,6 @@ class create_controller : public DrObject<create_controller>,
{ {
public: public:
void handleCommand(std::vector<std::string> &parameters) override; void handleCommand(std::vector<std::string> &parameters) override;
std::string script() override std::string script() override
{ {
return "create controller files"; return "create controller files";

View File

@ -69,7 +69,6 @@ static void createFilterSourceFile(std::ofstream &file,
data.insert("filename", fileName); data.insert("filename", fileName);
file << templ->genText(data); file << templ->genText(data);
} }
void create_filter::handleCommand(std::vector<std::string> &parameters) void create_filter::handleCommand(std::vector<std::string> &parameters)
{ {
if (parameters.size() < 1) if (parameters.size() < 1)

View File

@ -17,14 +17,12 @@
#include <drogon/DrObject.h> #include <drogon/DrObject.h>
#include "CommandHandler.h" #include "CommandHandler.h"
using namespace drogon; using namespace drogon;
namespace drogon_ctl namespace drogon_ctl
{ {
class create_filter : public DrObject<create_filter>, public CommandHandler class create_filter : public DrObject<create_filter>, public CommandHandler
{ {
public: public:
void handleCommand(std::vector<std::string> &parameters) override; void handleCommand(std::vector<std::string> &parameters) override;
std::string script() override std::string script() override
{ {
return "create filter class files"; return "create filter class files";

View File

@ -36,7 +36,6 @@
using namespace std::chrono_literals; using namespace std::chrono_literals;
using namespace drogon_ctl; using namespace drogon_ctl;
static std::string toLower(const std::string &str) static std::string toLower(const std::string &str)
{ {
auto ret = str; auto ret = str;
@ -66,17 +65,6 @@ static std::string escapeConnString(const std::string &str)
return escaped; return escaped;
} }
std::string drogon_ctl::escapeIdentifier(const std::string &identifier,
const std::string &rdbms)
{
if (rdbms != "postgresql")
{
return identifier;
}
return "\\\"" + identifier + "\\\"";
}
static std::map<std::string, std::vector<ConvertMethod>> getConvertMethods( static std::map<std::string, std::vector<ConvertMethod>> getConvertMethods(
const Json::Value &convertColumns) const Json::Value &convertColumns)
{ {
@ -177,7 +165,7 @@ void create_model::createModelClassFromPG(
auto className = nameTransform(tableName, true); auto className = nameTransform(tableName, true);
HttpViewData data; HttpViewData data;
data["className"] = className; data["className"] = className;
data["tableName"] = tableName; data["tableName"] = toLower(tableName);
data["hasPrimaryKey"] = (int)0; data["hasPrimaryKey"] = (int)0;
data["primaryKeyName"] = ""; data["primaryKeyName"] = "";
data["dbName"] = dbname_; data["dbName"] = dbname_;
@ -189,10 +177,10 @@ void create_model::createModelClassFromPG(
data["schema"] = schema; data["schema"] = schema;
} }
std::vector<ColumnInfo> cols; std::vector<ColumnInfo> cols;
*client << "SELECT * " *client << "SELECT * \
"FROM information_schema.columns " FROM information_schema.columns \
"WHERE table_schema = $1 " WHERE table_schema = $1 \
"AND table_name = $2" AND table_name = $2"
<< schema << tableName << Mode::Blocking >> << schema << tableName << Mode::Blocking >>
[&](const Result &r) { [&](const Result &r) {
if (r.size() == 0) if (r.size() == 0)
@ -295,14 +283,14 @@ void create_model::createModelClassFromPG(
exit(1); exit(1);
}; };
size_t pkNumber = 0; size_t pkNumber = 0;
*client << "SELECT " *client << "SELECT \
"pg_constraint.conname AS pk_name," pg_constraint.conname AS pk_name,\
"pg_constraint.conkey AS pk_vector " pg_constraint.conkey AS pk_vector \
"FROM pg_constraint " FROM pg_constraint \
"INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid " INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid \
"WHERE " WHERE \
"pg_class.relname = $1 " pg_class.relname = $1 \
"AND pg_constraint.contype = 'p'" AND pg_constraint.contype = 'p'"
<< tableName << Mode::Blocking >> << tableName << Mode::Blocking >>
[&](bool isNull, [&](bool isNull,
const std::string &pkName, const std::string &pkName,
@ -319,18 +307,16 @@ void create_model::createModelClassFromPG(
data["hasPrimaryKey"] = (int)pkNumber; data["hasPrimaryKey"] = (int)pkNumber;
if (pkNumber == 1) if (pkNumber == 1)
{ {
*client << "SELECT " *client << "SELECT \
"pg_attribute.attname AS colname," pg_attribute.attname AS colname,\
"pg_type.typname AS typename," pg_type.typname AS typename,\
"pg_constraint.contype AS contype " pg_constraint.contype AS contype \
"FROM pg_constraint " FROM pg_constraint \
"INNER JOIN pg_class ON pg_constraint.conrelid = " INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid \
"pg_class.oid " INNER JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid \
"INNER JOIN pg_attribute ON pg_attribute.attrelid = " AND pg_attribute.attnum = pg_constraint.conkey [ 1 ] \
"pg_class.oid " INNER JOIN pg_type ON pg_type.oid = pg_attribute.atttypid \
"AND pg_attribute.attnum = pg_constraint.conkey [ 1 ] " WHERE pg_class.relname = $1 and pg_constraint.contype='p'"
"INNER JOIN pg_type ON pg_type.oid = pg_attribute.atttypid "
"WHERE pg_class.relname = $1 and pg_constraint.contype='p'"
<< tableName << Mode::Blocking >> << tableName << Mode::Blocking >>
[&](bool isNull, [&](bool isNull,
const std::string &colName, const std::string &colName,
@ -358,20 +344,16 @@ void create_model::createModelClassFromPG(
std::vector<std::string> pkNames, pkTypes, pkValNames; std::vector<std::string> pkNames, pkTypes, pkValNames;
for (size_t i = 1; i <= pkNumber; ++i) for (size_t i = 1; i <= pkNumber; ++i)
{ {
*client << "SELECT " *client << "SELECT \
"pg_attribute.attname AS colname," pg_attribute.attname AS colname,\
"pg_type.typname AS typename," pg_type.typname AS typename,\
"pg_constraint.contype AS contype " pg_constraint.contype AS contype \
"FROM pg_constraint " FROM pg_constraint \
"INNER JOIN pg_class ON pg_constraint.conrelid = " INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid \
"pg_class.oid " INNER JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid \
"INNER JOIN pg_attribute ON pg_attribute.attrelid = " AND pg_attribute.attnum = pg_constraint.conkey [ $1 ] \
"pg_class.oid " INNER JOIN pg_type ON pg_type.oid = pg_attribute.atttypid \
"AND pg_attribute.attnum = pg_constraint.conkey [ $1 ] " WHERE pg_class.relname = $2 and pg_constraint.contype='p'"
"INNER JOIN pg_type ON pg_type.oid = "
"pg_attribute.atttypid "
"WHERE pg_class.relname = $2 and "
"pg_constraint.contype='p'"
<< (int)i << tableName << Mode::Blocking >> << (int)i << tableName << Mode::Blocking >>
[&](bool isNull, std::string colName, const std::string &type) { [&](bool isNull, std::string colName, const std::string &type) {
if (isNull) if (isNull)
@ -407,7 +389,6 @@ void create_model::createModelClassFromPG(
sourceFile << templ->genText(data); sourceFile << templ->genText(data);
createRestfulAPIController(data, restfulApiConfig); createRestfulAPIController(data, restfulApiConfig);
} }
void create_model::createModelFromPG( void create_model::createModelFromPG(
const std::string &path, const std::string &path,
const DbClientPtr &client, const DbClientPtr &client,
@ -471,7 +452,7 @@ void create_model::createModelClassFromMysql(
data["convertMethods"] = convertMethods; data["convertMethods"] = convertMethods;
std::vector<ColumnInfo> cols; std::vector<ColumnInfo> cols;
int i = 0; int i = 0;
*client << "desc `" + tableName + "`" << Mode::Blocking >> *client << "desc " + tableName << Mode::Blocking >>
[&i, &cols](bool isNull, [&i, &cols](bool isNull,
const std::string &field, const std::string &field,
const std::string &type, const std::string &type,
@ -603,7 +584,6 @@ void create_model::createModelClassFromMysql(
sourceFile << templ->genText(data); sourceFile << templ->genText(data);
createRestfulAPIController(data, restfulApiConfig); createRestfulAPIController(data, restfulApiConfig);
} }
void create_model::createModelFromMysql( void create_model::createModelFromMysql(
const std::string &path, const std::string &path,
const DbClientPtr &client, const DbClientPtr &client,
@ -710,7 +690,7 @@ void create_model::createModelClassFromSqlite3(
if (type.find("int") != std::string::npos) if (type.find("int") != std::string::npos)
{ {
info.colType_ = "int64_t"; info.colType_ = "uint64_t";
info.colLength_ = 8; info.colLength_ = 8;
} }
else if (type.find("char") != std::string::npos || type == "text" || else if (type.find("char") != std::string::npos || type == "text" ||
@ -826,7 +806,6 @@ void create_model::createModel(const std::string &path,
auto restfulApiConfig = config["restful_api_controllers"]; auto restfulApiConfig = config["restful_api_controllers"];
auto relationships = getRelationships(config["relationships"]); auto relationships = getRelationships(config["relationships"]);
auto convertMethods = getConvertMethods(config["convert"]); auto convertMethods = getConvertMethods(config["convert"]);
drogon::utils::createPath(path);
if (dbType == "postgresql") if (dbType == "postgresql")
{ {
#if USE_POSTGRESQL #if USE_POSTGRESQL
@ -1133,7 +1112,6 @@ void create_model::createModel(const std::string &path,
exit(1); exit(1);
} }
} }
void create_model::createModel(const std::string &path, void create_model::createModel(const std::string &path,
const std::string &singleModelName) const std::string &singleModelName)
{ {
@ -1174,9 +1152,7 @@ void create_model::createModel(const std::string &path,
try try
{ {
infile >> configJsonRoot; infile >> configJsonRoot;
createModel(outputPath_.empty() ? path : outputPath_, createModel(path, configJsonRoot, singleModelName);
configJsonRoot,
singleModelName);
} }
catch (const std::exception &exception) catch (const std::exception &exception)
{ {
@ -1214,22 +1190,6 @@ void create_model::handleCommand(std::vector<std::string> &parameters)
break; break;
} }
} }
for (auto iter = parameters.begin(); iter != parameters.end();)
{
auto &file = *iter;
if (file == "-o" || file == "--output")
{
iter = parameters.erase(iter);
if (iter != parameters.end())
{
outputPath_ = *iter;
iter = parameters.erase(iter);
}
continue;
}
++iter;
}
for (auto const &path : parameters) for (auto const &path : parameters)
{ {
createModel(path, singleModelName); createModel(path, singleModelName);

View File

@ -77,15 +77,10 @@ inline std::string nameTransform(const std::string &origName, bool isType)
ret[0] += ('A' - 'a'); ret[0] += ('A' - 'a');
return ret; return ret;
} }
std::string escapeIdentifier(const std::string &identifier,
const std::string &rdbms);
class PivotTable class PivotTable
{ {
public: public:
PivotTable() = default; PivotTable() = default;
PivotTable(const Json::Value &json) PivotTable(const Json::Value &json)
: tableName_(json.get("table_name", "").asString()) : tableName_(json.get("table_name", "").asString())
{ {
@ -104,7 +99,6 @@ class PivotTable
throw std::runtime_error("target_key can't be empty"); throw std::runtime_error("target_key can't be empty");
} }
} }
PivotTable reverse() const PivotTable reverse() const
{ {
PivotTable pivot; PivotTable pivot;
@ -113,17 +107,14 @@ class PivotTable
pivot.targetKey_ = originalKey_; pivot.targetKey_ = originalKey_;
return pivot; return pivot;
} }
const std::string &tableName() const const std::string &tableName() const
{ {
return tableName_; return tableName_;
} }
const std::string &originalKey() const const std::string &originalKey() const
{ {
return originalKey_; return originalKey_;
} }
const std::string &targetKey() const const std::string &targetKey() const
{ {
return targetKey_; return targetKey_;
@ -169,7 +160,6 @@ class ConvertMethod
includeFiles_.push_back(i.asString()); includeFiles_.push_back(i.asString());
} // for } // for
} }
ConvertMethod() = default; ConvertMethod() = default;
bool shouldConvert(const std::string &tableName, bool shouldConvert(const std::string &tableName,
@ -179,22 +169,18 @@ class ConvertMethod
{ {
return tableName_; return tableName_;
} }
const std::string &colName() const const std::string &colName() const
{ {
return colName_; return colName_;
} }
const std::string &methodBeforeDbWrite() const const std::string &methodBeforeDbWrite() const
{ {
return methodBeforeDbWrite_; return methodBeforeDbWrite_;
} }
const std::string &methodAfterDbRead() const const std::string &methodAfterDbRead() const
{ {
return methodAfterDbRead_; return methodAfterDbRead_;
} }
const std::vector<std::string> &includeFiles() const const std::vector<std::string> &includeFiles() const
{ {
return includeFiles_; return includeFiles_;
@ -217,7 +203,6 @@ class Relationship
HasMany, HasMany,
ManyToMany ManyToMany
}; };
Relationship(const Json::Value &relationship) Relationship(const Json::Value &relationship)
{ {
auto type = relationship.get("type", "has one").asString(); auto type = relationship.get("type", "has one").asString();
@ -279,9 +264,7 @@ class Relationship
pivotTable_ = PivotTable(pivot); pivotTable_ = PivotTable(pivot);
} }
} }
Relationship() = default; Relationship() = default;
Relationship reverse() const Relationship reverse() const
{ {
Relationship r; Relationship r;
@ -303,47 +286,38 @@ class Relationship
r.pivotTable_ = pivotTable_.reverse(); r.pivotTable_ = pivotTable_.reverse();
return r; return r;
} }
Type type() const Type type() const
{ {
return type_; return type_;
} }
bool enableReverse() const bool enableReverse() const
{ {
return enableReverse_; return enableReverse_;
} }
const std::string &originalTableName() const const std::string &originalTableName() const
{ {
return originalTableName_; return originalTableName_;
} }
const std::string &originalTableAlias() const const std::string &originalTableAlias() const
{ {
return originalTableAlias_; return originalTableAlias_;
} }
const std::string &originalKey() const const std::string &originalKey() const
{ {
return originalKey_; return originalKey_;
} }
const std::string &targetTableName() const const std::string &targetTableName() const
{ {
return targetTableName_; return targetTableName_;
} }
const std::string &targetTableAlias() const const std::string &targetTableAlias() const
{ {
return targetTableAlias_; return targetTableAlias_;
} }
const std::string &targetKey() const const std::string &targetKey() const
{ {
return targetKey_; return targetKey_;
} }
const PivotTable &pivotTable() const const PivotTable &pivotTable() const
{ {
return pivotTable_; return pivotTable_;
@ -360,12 +334,10 @@ class Relationship
bool enableReverse_{false}; bool enableReverse_{false};
PivotTable pivotTable_; PivotTable pivotTable_;
}; };
class create_model : public DrObject<create_model>, public CommandHandler class create_model : public DrObject<create_model>, public CommandHandler
{ {
public: public:
void handleCommand(std::vector<std::string> &parameters) override; void handleCommand(std::vector<std::string> &parameters) override;
std::string script() override std::string script() override
{ {
return "create Model classes files"; return "create Model classes files";
@ -429,6 +401,5 @@ class create_model : public DrObject<create_model>, public CommandHandler
const Json::Value &restfulApiConfig); const Json::Value &restfulApiConfig);
std::string dbname_; std::string dbname_;
bool forceOverwrite_{false}; bool forceOverwrite_{false};
std::string outputPath_;
}; };
} // namespace drogon_ctl } // namespace drogon_ctl

View File

@ -69,7 +69,6 @@ static void createPluginSourceFile(std::ofstream &file,
data.insert("filename", fileName); data.insert("filename", fileName);
file << templ->genText(data); file << templ->genText(data);
} }
void create_plugin::handleCommand(std::vector<std::string> &parameters) void create_plugin::handleCommand(std::vector<std::string> &parameters)
{ {
if (parameters.size() < 1) if (parameters.size() < 1)

View File

@ -17,14 +17,12 @@
#include <drogon/DrObject.h> #include <drogon/DrObject.h>
#include "CommandHandler.h" #include "CommandHandler.h"
using namespace drogon; using namespace drogon;
namespace drogon_ctl namespace drogon_ctl
{ {
class create_plugin : public DrObject<create_plugin>, public CommandHandler class create_plugin : public DrObject<create_plugin>, public CommandHandler
{ {
public: public:
void handleCommand(std::vector<std::string> &parameters) override; void handleCommand(std::vector<std::string> &parameters) override;
std::string script() override std::string script() override
{ {
return "create plugin class files"; return "create plugin class files";

View File

@ -38,7 +38,6 @@ void create_project::handleCommand(std::vector<std::string> &parameters)
auto pName = parameters[0]; auto pName = parameters[0];
createProject(pName); createProject(pName);
} }
static void newCmakeFile(std::ofstream &cmakeFile, static void newCmakeFile(std::ofstream &cmakeFile,
const std::string &projectName) const std::string &projectName)
{ {
@ -47,13 +46,11 @@ static void newCmakeFile(std::ofstream &cmakeFile,
auto templ = DrTemplateBase::newTemplate("cmake.csp"); auto templ = DrTemplateBase::newTemplate("cmake.csp");
cmakeFile << templ->genText(data); cmakeFile << templ->genText(data);
} }
static void newMainFile(std::ofstream &mainFile) static void newMainFile(std::ofstream &mainFile)
{ {
auto templ = DrTemplateBase::newTemplate("demoMain"); auto templ = DrTemplateBase::newTemplate("demoMain");
mainFile << templ->genText(); mainFile << templ->genText();
} }
static void newGitIgFile(std::ofstream &gitFile) static void newGitIgFile(std::ofstream &gitFile)
{ {
auto templ = DrTemplateBase::newTemplate("gitignore.csp"); auto templ = DrTemplateBase::newTemplate("gitignore.csp");
@ -65,25 +62,21 @@ static void newConfigJsonFile(std::ofstream &configJsonFile)
auto templ = DrTemplateBase::newTemplate("config_json"); auto templ = DrTemplateBase::newTemplate("config_json");
configJsonFile << templ->genText(); configJsonFile << templ->genText();
} }
static void newConfigYamlFile(std::ofstream &configYamlFile) static void newConfigYamlFile(std::ofstream &configYamlFile)
{ {
auto templ = DrTemplateBase::newTemplate("config_yaml"); auto templ = DrTemplateBase::newTemplate("config_yaml");
configYamlFile << templ->genText(); configYamlFile << templ->genText();
} }
static void newModelConfigFile(std::ofstream &configFile) static void newModelConfigFile(std::ofstream &configFile)
{ {
auto templ = DrTemplateBase::newTemplate("model_json"); auto templ = DrTemplateBase::newTemplate("model_json");
configFile << templ->genText(); configFile << templ->genText();
} }
static void newTestMainFile(std::ofstream &mainFile) static void newTestMainFile(std::ofstream &mainFile)
{ {
auto templ = DrTemplateBase::newTemplate("test_main"); auto templ = DrTemplateBase::newTemplate("test_main");
mainFile << templ->genText(); mainFile << templ->genText();
} }
static void newTestCmakeFile(std::ofstream &testCmakeFile, static void newTestCmakeFile(std::ofstream &testCmakeFile,
const std::string &projectName) const std::string &projectName)
{ {
@ -92,7 +85,6 @@ static void newTestCmakeFile(std::ofstream &testCmakeFile,
auto templ = DrTemplateBase::newTemplate("test_cmake"); auto templ = DrTemplateBase::newTemplate("test_cmake");
testCmakeFile << templ->genText(data); testCmakeFile << templ->genText(data);
} }
void create_project::createProject(const std::string &projectName) void create_project::createProject(const std::string &projectName)
{ {
#ifdef _WIN32 #ifdef _WIN32

View File

@ -16,14 +16,12 @@
#include <drogon/DrObject.h> #include <drogon/DrObject.h>
#include "CommandHandler.h" #include "CommandHandler.h"
using namespace drogon; using namespace drogon;
namespace drogon_ctl namespace drogon_ctl
{ {
class create_project : public DrObject<create_project>, public CommandHandler class create_project : public DrObject<create_project>, public CommandHandler
{ {
public: public:
void handleCommand(std::vector<std::string> &parameters) override; void handleCommand(std::vector<std::string> &parameters) override;
std::string script() override std::string script() override
{ {
return "create a project"; return "create a project";

View File

@ -53,7 +53,6 @@ static std::string &replace_all(std::string &str,
} }
return str; return str;
} }
static void parseCxxLine(std::ofstream &oSrcFile, static void parseCxxLine(std::ofstream &oSrcFile,
const std::string &line, const std::string &line,
const std::string &streamName, const std::string &streamName,
@ -67,7 +66,6 @@ static void parseCxxLine(std::ofstream &oSrcFile,
oSrcFile << tmp << "\n"; oSrcFile << tmp << "\n";
} }
} }
static void outputVal(std::ofstream &oSrcFile, static void outputVal(std::ofstream &oSrcFile,
const std::string &streamName, const std::string &streamName,
const std::string &viewDataName, const std::string &viewDataName,
@ -78,12 +76,12 @@ static void outputVal(std::ofstream &oSrcFile,
<< "\"];\n"; << "\"];\n";
oSrcFile << " if(val.type()==typeid(const char *)){\n"; oSrcFile << " if(val.type()==typeid(const char *)){\n";
oSrcFile << " " << streamName oSrcFile << " " << streamName
<< "<<*(std::any_cast<const char *>(&val));\n"; << "<<*any_cast<const char *>(&val);\n";
oSrcFile << " }else " oSrcFile << " }else "
"if(val.type()==typeid(std::string)||val.type()==typeid(const " "if(val.type()==typeid(std::string)||val.type()==typeid(const "
"std::string)){\n"; "std::string)){\n";
oSrcFile << " " << streamName oSrcFile << " " << streamName
<< "<<*(std::any_cast<const std::string>(&val));\n"; << "<<*any_cast<const std::string>(&val);\n";
oSrcFile << " }\n"; oSrcFile << " }\n";
oSrcFile << "}\n"; oSrcFile << "}\n";
} }
@ -288,7 +286,6 @@ void create_view::handleCommand(std::vector<std::string> &parameters)
} }
createViewFiles(parameters); createViewFiles(parameters);
} }
void create_view::createViewFiles(std::vector<std::string> &cspFileNames) void create_view::createViewFiles(std::vector<std::string> &cspFileNames)
{ {
for (auto const &file : cspFileNames) for (auto const &file : cspFileNames)
@ -298,7 +295,6 @@ void create_view::createViewFiles(std::vector<std::string> &cspFileNames)
exit(1); exit(1);
} }
} }
int create_view::createViewFile(const std::string &script_filename) int create_view::createViewFile(const std::string &script_filename)
{ {
std::cout << "create HttpView Class file by " << script_filename std::cout << "create HttpView Class file by " << script_filename
@ -378,7 +374,6 @@ int create_view::createViewFile(const std::string &script_filename)
} }
return 0; return 0;
} }
void create_view::newViewHeaderFile(std::ofstream &file, void create_view::newViewHeaderFile(std::ofstream &file,
const std::string &className) const std::string &className)
{ {
@ -411,7 +406,6 @@ void create_view::newViewSourceFile(std::ofstream &file,
"automatically,don't modify it!\n"; "automatically,don't modify it!\n";
file << "#include \"" << namespacePrefix << className << ".h\"\n"; file << "#include \"" << namespacePrefix << className << ".h\"\n";
file << "#include <drogon/utils/OStringStream.h>\n"; file << "#include <drogon/utils/OStringStream.h>\n";
file << "#include <drogon/utils/Utilities.h>\n";
file << "#include <string>\n"; file << "#include <string>\n";
file << "#include <map>\n"; file << "#include <map>\n";
file << "#include <vector>\n"; file << "#include <vector>\n";

View File

@ -17,14 +17,12 @@
#include <drogon/DrObject.h> #include <drogon/DrObject.h>
#include "CommandHandler.h" #include "CommandHandler.h"
using namespace drogon; using namespace drogon;
namespace drogon_ctl namespace drogon_ctl
{ {
class create_view : public DrObject<create_view>, public CommandHandler class create_view : public DrObject<create_view>, public CommandHandler
{ {
public: public:
void handleCommand(std::vector<std::string> &parameters) override; void handleCommand(std::vector<std::string> &parameters) override;
std::string script() override std::string script() override
{ {
return "create view class files"; return "create view class files";

View File

@ -17,7 +17,6 @@
#include <iostream> #include <iostream>
#include <memory> #include <memory>
using namespace drogon_ctl; using namespace drogon_ctl;
void help::handleCommand(std::vector<std::string> &parameters) void help::handleCommand(std::vector<std::string> &parameters)
{ {
if (parameters.size() == 0) if (parameters.size() == 0)

View File

@ -17,19 +17,16 @@
#include <drogon/DrObject.h> #include <drogon/DrObject.h>
#include "CommandHandler.h" #include "CommandHandler.h"
using namespace drogon; using namespace drogon;
namespace drogon_ctl namespace drogon_ctl
{ {
class help : public DrObject<help>, public CommandHandler class help : public DrObject<help>, public CommandHandler
{ {
public: public:
void handleCommand(std::vector<std::string> &parameters) override; void handleCommand(std::vector<std::string> &parameters) override;
std::string script() override std::string script() override
{ {
return "display this message"; return "display this message";
} }
bool isTopCommand() override bool isTopCommand() override
{ {
return true; return true;

View File

@ -18,17 +18,12 @@
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <iomanip> #include <iomanip>
#include <cstdlib> #include <stdlib.h>
#include <json/json.h>
#include <fstream>
#include <string>
#include <unordered_map>
#ifndef _WIN32 #ifndef _WIN32
#include <unistd.h> #include <unistd.h>
#endif #endif
using namespace drogon_ctl; using namespace drogon_ctl;
std::string press::detail() std::string press::detail()
{ {
return "Use press command to do stress testing\n" return "Use press command to do stress testing\n"
@ -36,19 +31,17 @@ std::string press::detail()
" -n num number of requests(default : 1)\n" " -n num number of requests(default : 1)\n"
" -t num number of threads(default : 1)\n" " -t num number of threads(default : 1)\n"
" -c num concurrent connections(default : 1)\n" " -c num concurrent connections(default : 1)\n"
" -k disable SSL certificate validation(default: enable)\n" // " -k keep alive(default: no)\n"
" -f customize http request json file(default: disenable)\n" " -q no progress indication(default: no)\n\n"
" -q no progress indication(default: show)\n\n"
"example: drogon_ctl press -n 10000 -c 100 -t 4 -q " "example: drogon_ctl press -n 10000 -c 100 -t 4 -q "
"http://localhost:8080/index.html -f ./http_request.json\n"; "http://localhost:8080/index.html\n";
} }
void outputErrorAndExit(const std::string_view &err) void outputErrorAndExit(const string_view &err)
{ {
std::cout << err << std::endl; std::cout << err << std::endl;
exit(1); exit(1);
} }
void press::handleCommand(std::vector<std::string> &parameters) void press::handleCommand(std::vector<std::string> &parameters)
{ {
for (auto iter = parameters.begin(); iter != parameters.end(); iter++) for (auto iter = parameters.begin(); iter != parameters.end(); iter++)
@ -156,29 +149,11 @@ void press::handleCommand(std::vector<std::string> &parameters)
continue; continue;
} }
} }
else if (param.find("-f") == 0) // else if (param == "-k")
{ // {
if (param == "-f") // keepAlive_ = true;
{ // continue;
++iter; // }
if (iter == parameters.end())
{
outputErrorAndExit("No http request json file!");
}
httpRequestJsonFile_ = *iter;
continue;
}
else
{
httpRequestJsonFile_ = param.substr(2);
continue;
}
}
else if (param == "-k")
{
certValidation_ = false;
continue;
}
else if (param == "-q") else if (param == "-q")
{ {
processIndication_ = false; processIndication_ = false;
@ -201,7 +176,7 @@ void press::handleCommand(std::vector<std::string> &parameters)
else else
{ {
auto pos = url_.find("://"); auto pos = url_.find("://");
auto posOfPath = url_.find('/', pos + 3); auto posOfPath = url_.find("/", pos + 3);
if (posOfPath == std::string::npos) if (posOfPath == std::string::npos)
{ {
host_ = url_; host_ = url_;
@ -213,118 +188,6 @@ void press::handleCommand(std::vector<std::string> &parameters)
path_ = url_.substr(posOfPath); path_ = url_.substr(posOfPath);
} }
} }
/*
http_request.json
{
"method": "POST",
"header": {
"token": "e2e9d0fe-dd14-4eaf-8ac1-0997730a805d"
},
"body": {
"passwd": "123456",
"account": "10001"
}
}
*/
if (!httpRequestJsonFile_.empty())
{
Json::Value httpRequestJson;
std::ifstream httpRequestFile(httpRequestJsonFile_,
std::ifstream::binary);
if (!httpRequestFile.is_open())
{
outputErrorAndExit(std::string{"No "} + httpRequestJsonFile_);
}
httpRequestFile >> httpRequestJson;
if (!httpRequestJson.isMember("method"))
{
outputErrorAndExit("No contain method");
}
auto methodStr = httpRequestJson["method"].asString();
std::transform(methodStr.begin(),
methodStr.end(),
methodStr.begin(),
::toupper);
auto toHttpMethod = [&]() -> drogon::HttpMethod {
if (methodStr == "GET")
{
return drogon::HttpMethod::Get;
}
else if (methodStr == "POST")
{
return drogon::HttpMethod::Post;
}
else if (methodStr == "HEAD")
{
return drogon::HttpMethod::Head;
}
else if (methodStr == "PUT")
{
return drogon::HttpMethod::Put;
}
else if (methodStr == "DELETE")
{
return drogon::HttpMethod::Delete;
}
else if (methodStr == "OPTIONS")
{
return drogon::HttpMethod::Options;
}
else if (methodStr == "PATCH")
{
return drogon::HttpMethod::Patch;
}
else
{
outputErrorAndExit("invalid method");
}
return drogon::HttpMethod::Get;
};
std::unordered_map<std::string, std::string> header;
if (httpRequestJson.isMember("header"))
{
auto &jsonValue = httpRequestJson["header"];
for (const auto &key : jsonValue.getMemberNames())
{
if (jsonValue[key].isString())
{
header[key] = jsonValue[key].asString();
}
else
{
header[key] = jsonValue[key].toStyledString();
}
}
}
std::string body;
if (httpRequestJson.isMember("body"))
{
Json::FastWriter fastWriter;
body = fastWriter.write(httpRequestJson["body"]);
}
createHttpRequestFunc_ = [this,
method = toHttpMethod(),
body = std::move(body),
header =
std::move(header)]() -> HttpRequestPtr {
auto request = HttpRequest::newHttpRequest();
request->setPath(path_);
request->setMethod(method);
for (const auto &[field, val] : header)
request->addHeader(field, val);
if (!body.empty())
request->setBody(body);
return request;
};
}
// std::cout << "host=" << host_ << std::endl; // std::cout << "host=" << host_ << std::endl;
// std::cout << "path=" << path_ << std::endl; // std::cout << "path=" << path_ << std::endl;
doTesting(); doTesting();
@ -351,10 +214,8 @@ void press::createRequestAndClients()
loopPool_->start(); loopPool_->start();
for (size_t i = 0; i < numOfConnections_; ++i) for (size_t i = 0; i < numOfConnections_; ++i)
{ {
auto client = HttpClient::newHttpClient(host_, auto client =
loopPool_->getNextLoop(), HttpClient::newHttpClient(host_, loopPool_->getNextLoop());
false,
certValidation_);
client->enableCookies(); client->enableCookies();
clients_.push_back(client); clients_.push_back(client);
} }
@ -367,19 +228,9 @@ void press::sendRequest(const HttpClientPtr &client)
{ {
return; return;
} }
auto request = HttpRequest::newHttpRequest();
HttpRequestPtr request;
if (createHttpRequestFunc_)
{
request = createHttpRequestFunc_();
}
else
{
request = HttpRequest::newHttpRequest();
request->setPath(path_); request->setPath(path_);
request->setMethod(Get); request->setMethod(Get);
}
// std::cout << "send!" << std::endl; // std::cout << "send!" << std::endl;
client->sendRequest( client->sendRequest(
request, request,
@ -431,6 +282,7 @@ void press::sendRequest(const HttpClientPtr &client)
void press::outputResults() void press::outputResults()
{ {
static std::mutex mtx;
size_t totalSent = 0; size_t totalSent = 0;
size_t totalRecv = 0; size_t totalRecv = 0;
for (auto &client : clients_) for (auto &client : clients_)
@ -442,7 +294,7 @@ void press::outputResults()
auto microSecs = now.microSecondsSinceEpoch() - auto microSecs = now.microSecondsSinceEpoch() -
statistics_.startDate_.microSecondsSinceEpoch(); statistics_.startDate_.microSecondsSinceEpoch();
double seconds = (double)microSecs / 1000000.0; double seconds = (double)microSecs / 1000000.0;
auto rps = static_cast<size_t>(statistics_.numOfGoodResponse_ / seconds); size_t rps = static_cast<size_t>(statistics_.numOfGoodResponse_ / seconds);
std::cout << std::endl; std::cout << std::endl;
std::cout << "TOTALS: " << numOfConnections_ << " connect, " std::cout << "TOTALS: " << numOfConnections_ << " connect, "
<< numOfRequests_ << " requests, " << numOfRequests_ << " requests, "

View File

@ -20,7 +20,6 @@
#include <drogon/HttpClient.h> #include <drogon/HttpClient.h>
#include <trantor/utils/Date.h> #include <trantor/utils/Date.h>
#include <trantor/net/EventLoopThreadPool.h> #include <trantor/net/EventLoopThreadPool.h>
#include <functional>
#include <string> #include <string>
#include <atomic> #include <atomic>
#include <memory> #include <memory>
@ -40,32 +39,26 @@ struct Statistics
trantor::Date startDate_; trantor::Date startDate_;
trantor::Date endDate_; trantor::Date endDate_;
}; };
class press : public DrObject<press>, public CommandHandler class press : public DrObject<press>, public CommandHandler
{ {
public: public:
void handleCommand(std::vector<std::string> &parameters) override; void handleCommand(std::vector<std::string> &parameters) override;
std::string script() override std::string script() override
{ {
return "Do stress testing(Use 'drogon_ctl help press' for more " return "Do stress testing(Use 'drogon_ctl help press' for more "
"information)"; "information)";
} }
bool isTopCommand() override bool isTopCommand() override
{ {
return true; return true;
} }
std::string detail() override; std::string detail() override;
private: private:
size_t numOfThreads_{1}; size_t numOfThreads_{1};
size_t numOfRequests_{1}; size_t numOfRequests_{1};
size_t numOfConnections_{1}; size_t numOfConnections_{1};
std::string httpRequestJsonFile_; // bool keepAlive_ = false;
std::function<HttpRequestPtr()> createHttpRequestFunc_;
bool certValidation_{true};
bool processIndication_{true}; bool processIndication_{true};
std::string url_; std::string url_;
std::string host_; std::string host_;

View File

@ -34,7 +34,10 @@ target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)
# ############################################################################## # ##############################################################################
if (CMAKE_CXX_STANDARD LESS 17) if (CMAKE_CXX_STANDARD LESS 17)
message(FATAL_ERROR "c++17 or higher is required") # With C++14, use boost to support any, string_view and filesystem
message(STATUS "use c++14")
find_package(Boost 1.61.0 REQUIRED)
target_link_libraries(${PROJECT_NAME} PUBLIC Boost::boost)
elseif (CMAKE_CXX_STANDARD LESS 20) elseif (CMAKE_CXX_STANDARD LESS 20)
message(STATUS "use c++17") message(STATUS "use c++17")
else () else ()

View File

@ -39,7 +39,7 @@
"db_clients": [ "db_clients": [
{ {
//name: Name of the client,'default' by default //name: Name of the client,'default' by default
"name": "default", //"name":"",
//rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default //rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default
"rdbms": "postgresql", "rdbms": "postgresql",
//filename: Sqlite3 db file name //filename: Sqlite3 db file name
@ -66,18 +66,15 @@
//timeout: -1.0 by default, in seconds, the timeout for executing a SQL query. //timeout: -1.0 by default, in seconds, the timeout for executing a SQL query.
//zero or negative value means no timeout. //zero or negative value means no timeout.
"timeout": -1.0, "timeout": -1.0,
//auto_batch: this feature is only available for the PostgreSQL driver(version >= 14.0), see //"auto_batch": this feature is only available for the PostgreSQL driver(version >= 14.0), see
// the wiki for more details. // the wiki for more details.
"auto_batch": false "auto_batch": false
//connect_options: extra options for the connection. Only works for PostgreSQL now.
//For more information, see https://www.postgresql.org/docs/16/libpq-connect.html#LIBPQ-CONNECT-OPTIONS
//"connect_options": { "statement_timeout": "1s" }
} }
], ],
"redis_clients": [ "redis_clients": [
{ {
//name: Name of the client,'default' by default //name: Name of the client,'default' by default
"name": "default", //"name":"",
//host: Server IP, 127.0.0.1 by default //host: Server IP, 127.0.0.1 by default
"host": "127.0.0.1", "host": "127.0.0.1",
//port: Server port, 6379 by default //port: Server port, 6379 by default
@ -106,14 +103,10 @@
//enable_session: False by default //enable_session: False by default
"enable_session": false, "enable_session": false,
"session_timeout": 0, "session_timeout": 0,
//string value of SameSite attribute of the Set-Cookie HTTP response header //string value of SameSite attribute of the Set-Cookie HTTP respone header
//valid value is either 'Null' (default), 'Lax', 'Strict' or 'None' //valid value is either 'Null' (default), 'Lax', 'Strict' or 'None'
"session_same_site" : "Null", "session_same_site" : "Null",
//session_cookie_key: The cookie key of the session, "JSESSIONID" by default //document_root: Root path of HTTP document, defaut path is ./
"session_cookie_key": "JSESSIONID",
//session_max_age: The max age of the session cookie, -1 by default
"session_max_age": -1,
//document_root: Root path of HTTP document, default path is ./
"document_root": "./", "document_root": "./",
//home_page: Set the HTML file of the home page, the default value is "index.html" //home_page: Set the HTML file of the home page, the default value is "index.html"
//If there isn't any handler registered to the path "/", the home page file in the "document_root" is send to clients as a response //If there isn't any handler registered to the path "/", the home page file in the "document_root" is send to clients as a response
@ -153,8 +146,8 @@
"apk", "apk",
"cur", "cur",
"xml", "xml",
"webp", "svg",
"svg" "webp"
], ],
// mime: A dictionary that extends the internal MIME type support. Maps extensions into new MIME types // mime: A dictionary that extends the internal MIME type support. Maps extensions into new MIME types
// note: This option only adds MIME to the sever. `file_types` above have to be set for the server to serve them. // note: This option only adds MIME to the sever. `file_types` above have to be set for the server to serve them.
@ -187,7 +180,7 @@
], ],
//max_connections: maximum number of connections, 100000 by default //max_connections: maximum number of connections, 100000 by default
"max_connections": 100000, "max_connections": 100000,
//max_connections_per_ip: maximum number of connections per client, 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, "max_connections_per_ip": 0,
//Load_dynamic_views: False by default, when set to true, drogon //Load_dynamic_views: False by default, when set to true, drogon
//compiles and loads dynamically "CSP View Files" in directories defined //compiles and loads dynamically "CSP View Files" in directories defined
@ -217,8 +210,6 @@
}, },
//log: Set log output, drogon output logs to stdout by default //log: Set log output, drogon output logs to stdout by default
"log": { "log": {
//use_spdlog: Use spdlog library to log
"use_spdlog": false,
//log_path: Log file path,empty by default,in which case,logs are output to the stdout //log_path: Log file path,empty by default,in which case,logs are output to the stdout
//"log_path": "./", //"log_path": "./",
//logfile_base_name: Log file base name,empty by default which means drogon names logfile as //logfile_base_name: Log file base name,empty by default which means drogon names logfile as
@ -253,19 +244,19 @@
//0 means cache forever, the negative value means no cache //0 means cache forever, the negative value means no cache
"static_files_cache_time": 5, "static_files_cache_time": 5,
//simple_controllers_map: Used to configure mapping from path to simple controller //simple_controllers_map: Used to configure mapping from path to simple controller
//"simple_controllers_map": [ "simple_controllers_map": [
// { {
// "path": "/path/name", "path": "/path/name",
// "controller": "controllerClassName", "controller": "controllerClassName",
// "http_methods": [ "http_methods": [
// "get", "get",
// "post" "post"
// ], ],
// "filters": [ "filters": [
// "FilterClassName" "FilterClassName"
// ] ]
// } }
//], ],
//idle_connection_timeout: Defaults to 60 seconds, the lifetime //idle_connection_timeout: Defaults to 60 seconds, the lifetime
//of the connection without read or write //of the connection without read or write
"idle_connection_timeout": 60, "idle_connection_timeout": 60,
@ -310,38 +301,22 @@
// Currently only gzip and br are supported. Note: max_memory_body_size and max_body_size applies twice for compressed requests. // Currently only gzip and br are supported. Note: max_memory_body_size and max_body_size applies twice for compressed requests.
// Once when receiving and once when decompressing. i.e. if the decompressed body is larger than max_body_size, the request // Once when receiving and once when decompressing. i.e. if the decompressed body is larger than max_body_size, the request
// will be rejected. // will be rejected.
"enabled_compressed_request": false, "enabled_compressed_request": false
// enable_request_stream: Defaults to false. If true the server will enable stream mode for http requests.
// See the wiki for more details.
"enable_request_stream": false,
}, },
//plugins: Define all plugins running in the application //plugins: Define all plugins running in the application
"plugins": [ "plugins": [
{ {
//name: The class name of the plugin //name: The class name of the plugin
"name": "drogon::plugin::PromExporter", //"name": "drogon::plugin::SecureSSLRedirector",
//dependencies: Plugins that the plugin depends on. It can be commented out //dependencies: Plugins that the plugin depends on. It can be commented out
"dependencies": [], "dependencies": [],
//config: The configuration of the plugin. This json object is the parameter to initialize the plugin. //config: The configuration of the plugin. This json object is the parameter to initialize the plugin.
//It can be commented out //It can be commented out
"config": { "config": {
"path": "/metrics" "ssl_redirect_exempt": [
} ".*\\.jpg"
}, ],
{ "secure_ssl_host": "localhost:8849"
"name": "drogon::plugin::AccessLogger",
"dependencies": [],
"config": {
"use_spdlog": false,
"log_path": "",
"log_format": "",
"log_file": "access.log",
"log_size_limit": 0,
"use_local_time": true,
"log_index": 0,
// "show_microseconds": true,
// "custom_time_format": "",
// "use_real_ip": false
} }
} }
], ],

View File

@ -3,8 +3,8 @@
# ssl:The global SSL settings. "key" and "cert" are the path to the SSL key and certificate. While # ssl:The global SSL settings. "key" and "cert" are the path to the SSL key and certificate. While
# "conf" is an array of 1 or 2-element tuples that supplies file style options for `SSL_CONF_cmd`. # "conf" is an array of 1 or 2-element tuples that supplies file style options for `SSL_CONF_cmd`.
# ssl: # ssl:
# cert: ../../trantor/trantor/tests/server.crt # cert: ../../trantor/trantor/tests/server.pem
# key: ../../trantor/trantor/tests/server.key # key: ../../trantor/trantor/tests/server.pem
# conf: [ # conf: [
# # [Options, -SessionTicket], # # [Options, -SessionTicket],
# # [Options, Compression] # # [Options, Compression]
@ -30,11 +30,11 @@
# ] # ]
# db_clients: # db_clients:
# # name: Name of the client,'default' by default # # name: Name of the client,'default' by default
# - name: default # - name: ''
# # rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default # # rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default
# rdbms: postgresql # rdbms: postgresql
# # filename: Sqlite3 db file name # # filename: Sqlite3 db file name
# # filename: '' # # filename: '',
# # host: Server address,localhost by default # # host: Server address,localhost by default
# host: 127.0.0.1 # host: 127.0.0.1
# # port: Server port, 5432 by default # # port: Server port, 5432 by default
@ -50,23 +50,19 @@
# is_fast: false # is_fast: false
# # client_encoding: The character set used by the client. it is empty string by default which # # client_encoding: The character set used by the client. it is empty string by default which
# # means use the default character set. # # means use the default character set.
# # client_encoding: '' # # client_encoding: '',
# # number_of_connections: 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. # # connections per IO thread, otherwise it is the total number of all connections.
# number_of_connections: 1 # number_of_connections: 1
# # timeout: -1 by default, in seconds, the timeout for executing a SQL query. # # timeout: -1 by default, in seconds, the timeout for executing a SQL query.
# # zero or negative value means no timeout. # # zero or negative value means no timeout.
# timeout: -1 # timeout: -1
# # auto_batch: this feature is only available for the PostgreSQL driver(version >= 14.0), see # # "auto_batch": this feature is only available for the PostgreSQL driver(version >= 14.0), see
# # the wiki for more details. # # the wiki for more details.
# auto_batch: false # auto_batch: false
# # connect_options: extra options for the connection. Only works for PostgreSQL now.
# # For more information, see https://www.postgresql.org/docs/16/libpq-connect.html#LIBPQ-CONNECT-OPTIONS
# # connect_options:
# # statement_timeout: '1s'
# redis_clients: # redis_clients:
# # name: Name of the client,'default' by default # # name: Name of the client,'default' by default
# - name: default # - name: ''
# # host: Server IP, 127.0.0.1 by default # # host: Server IP, 127.0.0.1 by default
# host: 127.0.0.1 # host: 127.0.0.1
# # port: Server port, 6379 by default # # port: Server port, 6379 by default
@ -91,16 +87,12 @@ app:
# is the number of CPU cores # is the number of CPU cores
number_of_threads: 1 number_of_threads: 1
# enable_session: False by default # enable_session: False by default
enable_session: false enable_session: true
session_timeout: 0 session_timeout: 0
# string value of SameSite attribute of the Set-Cookie HTTP response header # string value of SameSite attribute of the Set-Cookie HTTP respone header
# valid value is either 'Null' (default), 'Lax', 'Strict' or 'None' # valid value is either 'Null' (default), 'Lax', 'Strict' or 'None'
session_same_site: 'Null' session_same_site: 'Null'
# session_cookie_key: The cookie key of the session, "JSESSIONID" by default # document_root: Root path of HTTP document, defaut path is ./
session_cookie_key: 'JSESSIONID'
# session_max_age: The max age of the session cookie, -1 by default
session_max_age: -1
# document_root: Root path of HTTP document, default path is ./
document_root: ./ document_root: ./
# home_page: Set the HTML file of the home page, the default value is "index.html" # home_page: Set the HTML file of the home page, the default value is "index.html"
# If there isn't any handler registered to the path "/", the home page file in the "document_root" is send to clients as a response # If there isn't any handler registered to the path "/", the home page file in the "document_root" is send to clients as a response
@ -140,10 +132,8 @@ app:
# mime: A dictionary that extends the internal MIME type support. Maps extensions into new MIME types # mime: A dictionary that extends the internal MIME type support. Maps extensions into new MIME types
# note: This option only adds MIME to the sever. `file_types` above have to be set for the server to serve them. # note: This option only adds MIME to the sever. `file_types` above have to be set for the server to serve them.
mime: { mime: {
# text/markdown: md # text/markdown: md,
# text/gemini: # text/gemini: [gmi, gemini]
# - gmi
# - gemini
} }
# locations: An array of locations of static files for GET requests. # locations: An array of locations of static files for GET requests.
locations: locations:
@ -167,7 +157,7 @@ app:
filters: [] filters: []
# max_connections: maximum number of connections, 100000 by default # max_connections: maximum number of connections, 100000 by default
max_connections: 100000 max_connections: 100000
# max_connections_per_ip: maximum number of connections per client, 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 max_connections_per_ip: 0
# Load_dynamic_views: False by default, when set to true, drogon # Load_dynamic_views: False by default, when set to true, drogon
# compiles and loads dynamically "CSP View Files" in directories defined # compiles and loads dynamically "CSP View Files" in directories defined
@ -195,8 +185,6 @@ app:
precision_type: significant precision_type: significant
# log: Set log output, drogon output logs to stdout by default # log: Set log output, drogon output logs to stdout by default
log: log:
# use_spdlog: Use spdlog library to log
use_spdlog: false
# log_path: Log file path,empty by default,in which case,logs are output to the stdout # log_path: Log file path,empty by default,in which case,logs are output to the stdout
# log_path: ./ # log_path: ./
# logfile_base_name: Log file base name,empty by default which means drogon names logfile as # logfile_base_name: Log file base name,empty by default which means drogon names logfile as
@ -205,9 +193,6 @@ app:
# log_size_limit: 100000000 bytes by default, # log_size_limit: 100000000 bytes by default,
# When the log file size reaches "log_size_limit", the log file is switched. # When the log file size reaches "log_size_limit", the log file is switched.
log_size_limit: 100000000 log_size_limit: 100000000
# max_files: 0 by default,
# When the number of old log files exceeds "max_files", the oldest file will be deleted. 0 means never delete.
max_files: 0
# log_level: "DEBUG" by default,options:"TRACE","DEBUG","INFO","WARN" # log_level: "DEBUG" by default,options:"TRACE","DEBUG","INFO","WARN"
# The TRACE level is only valid when built in DEBUG mode. # The TRACE level is only valid when built in DEBUG mode.
log_level: DEBUG log_level: DEBUG
@ -230,14 +215,14 @@ app:
# 0 means cache forever, the negative value means no cache # 0 means cache forever, the negative value means no cache
static_files_cache_time: 5 static_files_cache_time: 5
# simple_controllers_map: Used to configure mapping from path to simple controller # simple_controllers_map: Used to configure mapping from path to simple controller
# simple_controllers_map: simple_controllers_map:
# - path: /path/name - path: /path/name
# controller: controllerClassName controller: controllerClassName
# http_methods: http_methods:
# - get - get
# - post - post
# filters: filters:
# - FilterClassName - FilterClassName
# idle_connection_timeout: Defaults to 60 seconds, the lifetime # idle_connection_timeout: Defaults to 60 seconds, the lifetime
# of the connection without read or write # of the connection without read or write
idle_connection_timeout: 60 idle_connection_timeout: 60
@ -278,36 +263,28 @@ app:
client_max_websocket_message_size: 128K client_max_websocket_message_size: 128K
# reuse_port: Defaults to false, users can run multiple processes listening on the same port at the same time. # reuse_port: Defaults to false, users can run multiple processes listening on the same port at the same time.
reuse_port: false reuse_port: false
# enabled_compressed_request: Defaults to false. If true the server will automatically decompress compressed request bodies. # enabled_compresed_request: Defaults to false. If true the server will automatically decompress compressed request bodies.
# Currently only gzip and br are supported. Note: max_memory_body_size and max_body_size applies twice for compressed requests. # Currently only gzip and br are supported. Note: max_memory_body_size and max_body_size applies twice for compressed requests.
# Once when receiving and once when decompressing. i.e. if the decompressed body is larger than max_body_size, the request # Once when receiving and once when decompressing. i.e. if the decompressed body is larger than max_body_size, the request
# will be rejected. # will be rejected.
enabled_compressed_request: false enabled_compresed_request: false
# enable_request_stream: Defaults to false. If true the server will enable stream mode for http requests.
# See the wiki for more details.
enable_request_stream: false
# plugins: Define all plugins running in the application # plugins: Define all plugins running in the application
plugins: plugins:
# name: The class name of the plugin # name: The class name of the plugin
- name: drogon::plugin::PromExporter - name: '' # drogon::plugin::SecureSSLRedirector
# dependencies: Plugins that the plugin depends on. It can be commented out # dependencies: Plugins that the plugin depends on. It can be commented out
dependencies: [] dependencies: []
# config: The configuration of the plugin. This json object is the parameter to initialize the plugin. # config: The configuration of the plugin. This json object is the parameter to initialize the plugin.
# It can be commented out # It can be commented out
config: config:
path: /metrics ssl_redirect_exempt:
- name: drogon::plugin::AccessLogger - .*\.jpg
dependencies: [] secure_ssl_host: 'localhost:8849'
config:
use_spdlog: false
log_path: ''
log_format: ''
log_file: access.log
log_size_limit: 0
use_local_time: true
log_index: 0
# show_microseconds: true
# custom_time_format: ''
# use_real_ip: false
# custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method. # custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method.
custom_config: {} custom_config:
realm: drogonRealm
opaque: drogonOpaque
credentials:
- user: drogon
password: dr0g0n

View File

@ -1,7 +1,7 @@
#include <drogon/drogon.h> #include <drogon/drogon.h>
int main() { int main() {
//Set HTTP listener address and port //Set HTTP listener address and port
drogon::app().addListener("0.0.0.0", 5555); drogon::app().addListener("0.0.0.0",80);
//Load config file //Load config file
//drogon::app().loadConfigFile("../config.json"); //drogon::app().loadConfigFile("../config.json");
//drogon::app().loadConfigFile("../config.yaml"); //drogon::app().loadConfigFile("../config.yaml");

View File

@ -76,7 +76,7 @@ else
<%c++for(auto col:cols){ <%c++for(auto col:cols){
%> %>
const std::string [[className]]::Cols::_{%col.colName_%} = "{%escapeIdentifier(col.colName_, rdbms)%}"; const std::string [[className]]::Cols::_{%col.colName_%} = "{%col.colName_%}";
<%c++ <%c++
}%> }%>
<%c++if(@@.get<int>("hasPrimaryKey")<=1){%> <%c++if(@@.get<int>("hasPrimaryKey")<=1){%>
@ -102,7 +102,7 @@ if(!schema.empty())
{ {
$$<<schema<<"."; $$<<schema<<".";
} }
%>{%escapeIdentifier(@@.get<std::string>("tableName"), rdbms)%}"; %>[[tableName]]";
const std::vector<typename [[className]]::MetaData> [[className]]::metaData_={ const std::vector<typename [[className]]::MetaData> [[className]]::metaData_={
<%c++for(size_t i=0;i<cols.size();i++){ <%c++for(size_t i=0;i<cols.size();i++){
@ -180,7 +180,7 @@ const std::string &[[className]]::getColumnName(size_t index) noexcept(false)
} }
else if(col.colDatabaseType_=="bytea") else if(col.colDatabaseType_=="bytea")
{ {
$$<<" auto str = r[\""<<col.colName_<<"\"].as<std::string_view>();\n"; $$<<" auto str = r[\""<<col.colName_<<"\"].as<string_view>();\n";
$$<<" if(str.length()>=2&&\n"; $$<<" if(str.length()>=2&&\n";
$$<<" str[0]=='\\\\'&&str[1]=='x')\n"; $$<<" str[0]=='\\\\'&&str[1]=='x')\n";
$$<<" {\n"; $$<<" {\n";
@ -271,7 +271,7 @@ const std::string &[[className]]::getColumnName(size_t index) noexcept(false)
} }
else if(col.colDatabaseType_=="bytea") else if(col.colDatabaseType_=="bytea")
{ {
$$<<" auto str = r[index].as<std::string_view>();\n"; $$<<" auto str = r[index].as<string_view>();\n";
$$<<" if(str.length()>=2&&\n"; $$<<" if(str.length()>=2&&\n";
$$<<" str[0]=='\\\\'&&str[1]=='x')\n"; $$<<" str[0]=='\\\\'&&str[1]=='x')\n";
$$<<" {\n"; $$<<" {\n";
@ -975,7 +975,7 @@ void [[className]]::updateByJson(const Json::Value &pJson) noexcept(false)
{ {
$$<<"const "<<col.colType_<<" &"<<className<<"::getValueOf"<<col.colTypeName_<<"() const noexcept\n"; $$<<"const "<<col.colType_<<" &"<<className<<"::getValueOf"<<col.colTypeName_<<"() const noexcept\n";
$$<<"{\n"; $$<<"{\n";
$$<<" static const "<<col.colType_<<" defaultValue = "<<col.colType_<<"();\n"; $$<<" const static "<<col.colType_<<" defaultValue = "<<col.colType_<<"();\n";
$$<<" if("<<col.colValName_<<"_)\n"; $$<<" if("<<col.colValName_<<"_)\n";
$$<<" return *"<<col.colValName_<<"_;\n"; $$<<" return *"<<col.colValName_<<"_;\n";
$$<<" return defaultValue;\n"; $$<<" return defaultValue;\n";
@ -984,7 +984,7 @@ void [[className]]::updateByJson(const Json::Value &pJson) noexcept(false)
{ {
$$<<"std::string "<<className<<"::getValueOf"<<col.colTypeName_<<"AsString() const noexcept\n"; $$<<"std::string "<<className<<"::getValueOf"<<col.colTypeName_<<"AsString() const noexcept\n";
$$<<"{\n"; $$<<"{\n";
$$<<" static const std::string defaultValue = std::string();\n"; $$<<" const static std::string defaultValue = std::string();\n";
$$<<" if("<<col.colValName_<<"_)\n"; $$<<" if("<<col.colValName_<<"_)\n";
$$<<" return std::string("<<col.colValName_<<"_->data(),"<<col.colValName_<<"_->size());\n"; $$<<" return std::string("<<col.colValName_<<"_->data(),"<<col.colValName_<<"_->size());\n";
$$<<" return defaultValue;\n"; $$<<" return defaultValue;\n";
@ -1534,7 +1534,8 @@ if(!col.notNull_){%>
if(col.colType_ == "std::string" && col.colLength_>0) if(col.colType_ == "std::string" && col.colLength_>0)
{ {
%> %>
if(pJson.isString() && std::strlen(pJson.asCString()) > {%col.colLength_%}) // asString().length() creates a string object, is there any better way to validate the length?
if(pJson.isString() && pJson.asString().length() > {%col.colLength_%})
{ {
err="String length exceeds limit for the " + err="String length exceeds limit for the " +
fieldName + fieldName +
@ -1567,54 +1568,64 @@ for(auto &relationship : relationships)
auto relationshipValName=nameTransform(name, false); auto relationshipValName=nameTransform(name, false);
auto alias=relationship.targetTableAlias(); auto alias=relationship.targetTableAlias();
auto aliasValName=nameTransform(alias, false); auto aliasValName=nameTransform(alias, false);
if(relationship.type() == Relationship::Type::HasOne)
{
if(!alias.empty()) if(!alias.empty())
{ {
if(alias[0] <= 'z' && alias[0] >= 'a') if(alias[0] <= 'z' && alias[0] >= 'a')
{ {
alias[0] += ('A' - 'a'); alias[0] += ('A' - 'a');
}
}
else
{
alias = relationshipClassName;
} }
std::string alind(alias.length(), ' '); std::string alind(alias.length(), ' ');
if(relationship.type() == Relationship::Type::HasOne)
{
%> %>
{%relationshipClassName%} [[className]]::get{%alias%}(const DbClientPtr &clientPtr) const {
static const std::string sql = "select * from {%name%} where {%relationship.targetKey()%} = <%c++
if(rdbms=="postgresql")
{
$$<<"$1";
}
else
{
$$<<"?";
}%>";
Result r(nullptr);
{
auto binder = *clientPtr << sql;
binder << *{%nameTransform(relationship.originalKey(), false)%}_ << Mode::Blocking >>
[&r](const Result &result) { r = result; };
binder.exec();
}
if (r.size() == 0)
{
throw UnexpectedRows("0 rows found");
}
else if (r.size() > 1)
{
throw UnexpectedRows("Found more than one row");
}
return {%relationshipClassName%}(r[0]);
}
{%relationshipClassName%} [[className]]::get{%alias%}(const drogon::orm::DbClientPtr &clientPtr) const {
std::shared_ptr<std::promise<{%relationshipClassName%}>> pro(new std::promise<{%relationshipClassName%}>);
std::future<{%relationshipClassName%}> f = pro->get_future();
get{%alias%}(clientPtr, [&pro] ({%relationshipClassName%} result) {
try {
pro->set_value(result);
}
catch (...) {
pro->set_exception(std::current_exception());
}
}, [&pro] (const DrogonDbException &err) {
pro->set_exception(std::make_exception_ptr(err));
});
return f.get();
}
void [[className]]::get{%alias%}(const DbClientPtr &clientPtr, void [[className]]::get{%alias%}(const DbClientPtr &clientPtr,
{%indentStr%} {%alind%} const std::function<void({%relationshipClassName%})> &rcb, {%indentStr%} {%alind%} const std::function<void({%relationshipClassName%})> &rcb,
{%indentStr%} {%alind%} const ExceptionCallback &ecb) const {%indentStr%} {%alind%} const ExceptionCallback &ecb) const
<%c++
}
else
{ {
static const std::string sql = "select * from {%name%} where {%relationship.targetKey()%} = <%c++ std::string relationshipClassInde(relationshipClassName.length(), ' ');
%>
{%relationshipClassName%} [[className]]::get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr) const {
std::shared_ptr<std::promise<{%relationshipClassName%}>> pro(new std::promise<{%relationshipClassName%}>);
std::future<{%relationshipClassName%}> f = pro->get_future();
get{%relationshipClassName%}(clientPtr, [&pro] ({%relationshipClassName%} result) {
try {
pro->set_value(result);
}
catch (...) {
pro->set_exception(std::current_exception());
}
}, [&pro] (const DrogonDbException &err) {
pro->set_exception(std::make_exception_ptr(err));
});
return f.get();
}
void [[className]]::get{%relationshipClassName%}(const DbClientPtr &clientPtr,
{%indentStr%} {%relationshipClassInde%} const std::function<void({%relationshipClassName%})> &rcb,
{%indentStr%} {%relationshipClassInde%} const ExceptionCallback &ecb) const
<%c++
}
%>
{
const static std::string sql = "select * from {%name%} where {%relationship.targetKey()%} = <%c++
if(rdbms=="postgresql") if(rdbms=="postgresql")
{ {
$$<<"$1"; $$<<"$1";
@ -1645,38 +1656,61 @@ void [[className]]::get{%alias%}(const DbClientPtr &clientPtr,
} }
else if(relationship.type() == Relationship::Type::HasMany) else if(relationship.type() == Relationship::Type::HasMany)
{ {
if(!alias.empty())
{
if(alias[0] <= 'z' && alias[0] >= 'a')
{
alias[0] += ('A' - 'a');
}
std::string alind(alias.length(), ' ');
%> %>
std::vector<{%relationshipClassName%}> [[className]]::get{%alias%}(const DbClientPtr &clientPtr) const { std::vector<{%relationshipClassName%}> [[className]]::get{%alias%}(const drogon::orm::DbClientPtr &clientPtr) const {
static const std::string sql = "select * from {%name%} where {%relationship.targetKey()%} = <%c++ std::shared_ptr<std::promise<std::vector<{%relationshipClassName%}>>> pro(new std::promise<std::vector<{%relationshipClassName%}>>);
if(rdbms=="postgresql") std::future<std::vector<{%relationshipClassName%}>> f = pro->get_future();
{ get{%alias%}(clientPtr, [&pro] (std::vector<{%relationshipClassName%}> result) {
$$<<"$1"; try {
pro->set_value(result);
} }
else catch (...) {
{ pro->set_exception(std::current_exception());
$$<<"?";
}%>";
Result r(nullptr);
{
auto binder = *clientPtr << sql;
binder << *{%nameTransform(relationship.originalKey(), false)%}_ << Mode::Blocking >>
[&r](const Result &result) { r = result; };
binder.exec();
} }
std::vector<{%relationshipClassName%}> ret; }, [&pro] (const DrogonDbException &err) {
ret.reserve(r.size()); pro->set_exception(std::make_exception_ptr(err));
for (auto const &row : r) });
{ return f.get();
ret.emplace_back({%relationshipClassName%}(row));
} }
return ret;
}
void [[className]]::get{%alias%}(const DbClientPtr &clientPtr, void [[className]]::get{%alias%}(const DbClientPtr &clientPtr,
{%indentStr%} {%alind%} const std::function<void(std::vector<{%relationshipClassName%}>)> &rcb, {%indentStr%} {%alind%} const std::function<void(std::vector<{%relationshipClassName%}>)> &rcb,
{%indentStr%} {%alind%} const ExceptionCallback &ecb) const {%indentStr%} {%alind%} const ExceptionCallback &ecb) const
<%c++
}
else
{ {
static const std::string sql = "select * from {%name%} where {%relationship.targetKey()%} = <%c++ std::string relationshipClassInde(relationshipClassName.length(), ' ');
%>
std::vector<{%relationshipClassName%}> [[className]]::get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr) const {
std::shared_ptr<std::promise<std::vector<{%relationshipClassName%}>>> pro(new std::promise<std::vector<{%relationshipClassName%}>>);
std::future<std::vector<{%relationshipClassName%}>> f = pro->get_future();
get{%relationshipClassName%}(clientPtr, [&pro] (std::vector<{%relationshipClassName%}> result) {
try {
pro->set_value(result);
}
catch (...) {
pro->set_exception(std::current_exception());
}
}, [&pro] (const DrogonDbException &err) {
pro->set_exception(std::make_exception_ptr(err));
});
return f.get();
}
void [[className]]::get{%relationshipClassName%}(const DbClientPtr &clientPtr,
{%indentStr%} {%relationshipClassInde%} const std::function<void(std::vector<{%relationshipClassName%}>)> &rcb,
{%indentStr%} {%relationshipClassInde%} const ExceptionCallback &ecb) const
<%c++
}
%>
{
const static std::string sql = "select * from {%name%} where {%relationship.targetKey()%} = <%c++
if(rdbms=="postgresql") if(rdbms=="postgresql")
{ {
$$<<"$1"; $$<<"$1";
@ -1706,39 +1740,61 @@ void [[className]]::get{%alias%}(const DbClientPtr &clientPtr,
auto pivotTableClassName=nameTransform(pivotTableName, true); auto pivotTableClassName=nameTransform(pivotTableName, true);
auto &pivotOriginalKey=relationship.pivotTable().originalKey(); auto &pivotOriginalKey=relationship.pivotTable().originalKey();
auto &pivotTargetKey=relationship.pivotTable().targetKey(); auto &pivotTargetKey=relationship.pivotTable().targetKey();
if(!alias.empty())
{
if(alias[0] <= 'z' && alias[0] >= 'a')
{
alias[0] += ('A' - 'a');
}
std::string alind(alias.length(), ' ');
%> %>
std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>> [[className]]::get{%alias%}(const DbClientPtr &clientPtr) const { std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>> [[className]]::get{%alias%}(const drogon::orm::DbClientPtr &clientPtr) const {
static const std::string sql = "select * from {%name%},{%pivotTableName%} where {%pivotTableName%}.{%pivotOriginalKey%} = <%c++ std::shared_ptr<std::promise<std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>>> pro(new std::promise<std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>>);
if(rdbms=="postgresql") std::future<std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>> f = pro->get_future();
{ get{%alias%}(clientPtr, [&pro] (std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>> result) {
$$<<"$1"; try {
pro->set_value(result);
} }
else catch (...) {
{ pro->set_exception(std::current_exception());
$$<<"?";
}%> and {%pivotTableName%}.{%pivotTargetKey%} = {%name%}.{%relationship.targetKey()%}";
Result r(nullptr);
{
auto binder = *clientPtr << sql;
binder << *{%nameTransform(relationship.originalKey(), false)%}_ << Mode::Blocking >>
[&r](const Result &result) { r = result; };
binder.exec();
} }
std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>> ret; }, [&pro] (const DrogonDbException &err) {
ret.reserve(r.size()); pro->set_exception(std::make_exception_ptr(err));
for (auto const &row : r) });
{ return f.get();
ret.emplace_back(std::pair<{%relationshipClassName%},{%pivotTableClassName%}>(
{%relationshipClassName%}(row),{%pivotTableClassName%}(row,{%relationshipClassName%}::getColumnNumber())));
} }
return ret;
}
void [[className]]::get{%alias%}(const DbClientPtr &clientPtr, void [[className]]::get{%alias%}(const DbClientPtr &clientPtr,
{%indentStr%} {%alind%} const std::function<void(std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>)> &rcb, {%indentStr%} {%alind%} const std::function<void(std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>)> &rcb,
{%indentStr%} {%alind%} const ExceptionCallback &ecb) const {%indentStr%} {%alind%} const ExceptionCallback &ecb) const
<%c++
}
else
{ {
static const std::string sql = "select * from {%name%},{%pivotTableName%} where {%pivotTableName%}.{%pivotOriginalKey%} = <%c++ std::string relationshipClassInde(relationshipClassName.length(), ' ');
%>
std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>> [[className]]::get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr) const {
std::shared_ptr<std::promise<std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>>> pro(new std::promise<std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>>);
std::future<std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>> f = pro->get_future();
get{%relationshipClassName%}(clientPtr, [&pro] (std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>> result) {
try {
pro->set_value(result);
}
catch (...) {
pro->set_exception(std::current_exception());
}
}, [&pro] (const DrogonDbException &err) {
pro->set_exception(std::make_exception_ptr(err));
});
return f.get();
}
void [[className]]::get{%relationshipClassName%}(const DbClientPtr &clientPtr,
{%indentStr%} {%relationshipClassInde%} const std::function<void(std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>)> &rcb,
{%indentStr%} {%relationshipClassInde%} const ExceptionCallback &ecb) const
<%c++
}
%>
{
const static std::string sql = "select * from {%name%},{%pivotTableName%} where {%pivotTableName%}.{%pivotOriginalKey%} = <%c++
if(rdbms=="postgresql") if(rdbms=="postgresql")
{ {
$$<<"$1"; $$<<"$1";

View File

@ -22,7 +22,6 @@ using namespace drogon_ctl;
#include <trantor/utils/Logger.h> #include <trantor/utils/Logger.h>
#include <json/json.h> #include <json/json.h>
#include <string> #include <string>
#include <string_view>
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <tuple> #include <tuple>
@ -86,11 +85,11 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
%> %>
}; };
static const int primaryKeyNumber; const static int primaryKeyNumber;
static const std::string tableName; const static std::string tableName;
static const bool hasPrimaryKey; const static bool hasPrimaryKey;
<%c++if(@@.get<int>("hasPrimaryKey")<=1){%> <%c++if(@@.get<int>("hasPrimaryKey")<=1){%>
static const std::string primaryKeyName; const static std::string primaryKeyName;
<%c++if(!@@.get<std::string>("primaryKeyType").empty()){%> <%c++if(!@@.get<std::string>("primaryKeyType").empty()){%>
using PrimaryKeyType = [[primaryKeyType]]; using PrimaryKeyType = [[primaryKeyType]];
const PrimaryKeyType &getPrimaryKey() const; const PrimaryKeyType &getPrimaryKey() const;
@ -108,7 +107,7 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
typelist += ","; typelist += ",";
} }
%> %>
static const std::vector<std::string> primaryKeyName; const static std::vector<std::string> primaryKeyName;
using PrimaryKeyType = std::tuple<{%typelist%}>;//<%c++ using PrimaryKeyType = std::tuple<{%typelist%}>;//<%c++
auto pkName=@@.get<std::vector<std::string>>("primaryKeyName"); auto pkName=@@.get<std::vector<std::string>>("primaryKeyName");
for(size_t i=0;i<pkName.size();i++) for(size_t i=0;i<pkName.size();i++)
@ -508,7 +507,7 @@ if(@@.get<std::string>("rdbms")=="postgresql")
if(@@.get<std::string>("rdbms")=="postgresql") if(@@.get<std::string>("rdbms")=="postgresql")
{ {
%> %>
n = snprintf(placeholderStr,sizeof(placeholderStr),"$%d,",placeholder++); n = sprintf(placeholderStr,"$%d,",placeholder++);
sql.append(placeholderStr, n); sql.append(placeholderStr, n);
<%c++ <%c++
}else }else

View File

@ -6,7 +6,7 @@ add_executable(${PROJECT_NAME} test_main.cc)
# ############################################################################## # ##############################################################################
# If you include the drogon source code locally in your project, use this method # If you include the drogon source code locally in your project, use this method
# to add drogon # to add drogon
# target_link_libraries(${PROJECT_NAME} PRIVATE drogon) # target_link_libraries(${PROJECT_NAME}_test PRIVATE drogon)
# #
# and comment out the following lines # and comment out the following lines
target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon) target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)

View File

@ -53,6 +53,11 @@ void version::handleCommand(std::vector<std::string> &parameters)
#else #else
std::cout << " brotli: no\n"; std::cout << " brotli: no\n";
#endif #endif
#ifdef Boost_FOUND
std::cout << " boost: yes\n";
#else
std::cout << " boost: no\n";
#endif
#ifdef USE_REDIS #ifdef USE_REDIS
std::cout << " hiredis: yes\n"; std::cout << " hiredis: yes\n";
#else #else

View File

@ -17,24 +17,20 @@
#include <drogon/DrObject.h> #include <drogon/DrObject.h>
#include "CommandHandler.h" #include "CommandHandler.h"
using namespace drogon; using namespace drogon;
namespace drogon_ctl namespace drogon_ctl
{ {
class version : public DrObject<version>, public CommandHandler class version : public DrObject<version>, public CommandHandler
{ {
public: public:
void handleCommand(std::vector<std::string> &parameters) override; void handleCommand(std::vector<std::string> &parameters) override;
std::string script() override std::string script() override
{ {
return "display version of this tool"; return "display version of this tool";
} }
bool isTopCommand() override bool isTopCommand() override
{ {
return true; return true;
} }
version() version()
{ {
} }

View File

@ -31,11 +31,6 @@ add_executable(redis_simple redis/main.cc
add_executable(redis_chat redis_chat/main.cc add_executable(redis_chat redis_chat/main.cc
redis_chat/controllers/Chat.cc) redis_chat/controllers/Chat.cc)
add_executable(async_stream async_stream/main.cc
async_stream/RequestStreamExampleCtrl.cc)
add_executable(cors cors/main.cc)
set(example_targets set(example_targets
benchmark benchmark
client client
@ -46,13 +41,11 @@ set(example_targets
login_session login_session
jsonstore jsonstore
redis_simple redis_simple
redis_chat redis_chat)
async_stream
cors)
# Add warnings for our example targets--some warnings (such as -Wunused-parameter) only appear # Add warnings for our example targets--some warnings (such as -Wunused-parameter) only appear
# when the templated functions are instantiated at their point of use. # when the templated functions are instantiated at their point of use.
if(NOT ${CMAKE_PLATFORM_NAME} STREQUAL "Windows" AND CMAKE_CXX_COMPILER_ID MATCHES GNU) if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
foreach(target ${example_targets}) foreach(target ${example_targets})
target_compile_options(${target} PRIVATE -Wall -Wextra -Werror) target_compile_options(${target} PRIVATE -Wall -Wextra -Werror)
endforeach() endforeach()

View File

@ -2,21 +2,19 @@
The following examples can help you understand how to use Drogon: The following examples can help you understand how to use Drogon:
1. [helloworld](https://github.com/drogonframework/drogon/tree/master/examples/helloworld) - The multiple ways of "Hello, World!" 1. [helloworld](https://github.com/an-tao/drogon/tree/master/examples/helloworld) - The multiple ways of "Hello, World!"
2. [client_example](https://github.com/drogonframework/drogon/tree/master/examples/client_example/main.cc) - A client example 2. [client_example](https://github.com/an-tao/drogon/tree/master/examples/client_example/main.cc) - A client example.
3. [websocket_client](https://github.com/drogonframework/drogon/tree/master/examples/websocket_client/WebSocketClient.cc) - An example on how to use the WebSocket client 3. [websocket_client](https://github.com/an-tao/drogon/tree/master/examples/websocket_client/WebSocketClient.cc) - An example on how to use the WebSocket client
4. [login_session](https://github.com/drogonframework/drogon/tree/master/examples/login_session) - How to use the built-in session system to handle login and out 4. [login_session](https://github.com/an-tao/drogon/tree/master/examples/login_session) - How to use the built-in session system to handle login and out
5. [file_upload](https://github.com/drogonframework/drogon/tree/master/examples/file_upload) - How to handle file uploads in Drogon 5. [file_upload](https://github.com/an-tao/drogon/tree/master/examples/file_upload) - How to handle file uploads in Drogon
6. [simple_reverse_proxy](https://github.com/drogonframework/drogon/tree/master/examples/simple_reverse_proxy) - An example showing how to use Drogon as a HTTP reverse 6. [simple_reverse_proxy](https://github.com/an-tao/drogon/tree/master/examples/simple_reverse_proxy) - A Example showing how to use drogon as a http reverse
proxy with a simple round robin proxy with a simple round robin.
7. [benchmark](https://github.com/drogonframework/drogon/tree/master/examples/benchmark) - Basic benchmark(https://github.com/drogonframework/drogon/wiki/13-Benchmarks) example 7. [benchmark](https://github.com/an-tao/drogon/tree/master/examples/benchmark) - Basic benchmark example. see [wiki benchmarks](https://github.com/an-tao/drogon/wiki/13-Benchmarks)
8. [jsonstore](https://github.com/drogonframework/drogon/tree/master/examples/jsonstore) - Implementation of a [jsonstore](https://github.com/bluzi/jsonstore)-like storage service that is concurrent and stores in memory. Serving as a showcase on how to build a minimally useful RESTful APIs in Drogon 8. [jsonstore](https://github.com/an-tao/drogon/tree/master/examples/jsonstore) - Implementation of a [jsonstore](https://github.com/bluzi/jsonstore)-like storage service that is concurrent and stores in memory. Serving as a showcase on how to build a minimally useful RESTful APIs in Drogon.
9. [redis](https://github.com/drogonframework/drogon/tree/master/examples/redis) - A simple example of Redis 9. [redis](https://github.com/an-tao/drogon/tree/master/examples/redis) - A simple example of Redis
10. [websocket_server](https://github.com/drogonframework/drogon/tree/master/examples/websocket_server) - A example websocket chat room server 10. [websocket_server](https://github.com/drogonframework/drogon/tree/master/examples/websocket_server) - Example WebSocker chat room server
11. [redis_cache](https://github.com/drogonframework/drogon/tree/master/examples/redis_cache) - An example for using coroutines of Redis clients 11. [redis_cache](https://github.com/an-tao/drogon/tree/master/examples/redis_cache) - An example for using coroutines of redis clients
12. [redis_chat](https://github.com/drogonframework/drogon/tree/master/examples/redis_chat) - A chatroom server built with websocket and Redis pub/sub service 12. [redis_chat](https://github.com/an-tao/drogon/tree/master/examples/redis_chat) - A chatroom server built with websocket and redis pub/sub service.
13. [prometheus_example](https://github.com/drogonframework/drogon/tree/master/examples/prometheus_example) - An example of how to use the Prometheus exporter in Drogon
14. [cors](https://github.com/drogonframework/drogon/tree/master/examples/cors) - An example demonstrating how to implement CORS (Cross-Origin Resource Sharing) support in Drogon
### [TechEmpower Framework Benchmarks](https://github.com/TechEmpower/FrameworkBenchmarks) test suite ### [TechEmpower Framework Benchmarks](https://github.com/TechEmpower/FrameworkBenchmarks) test suite
@ -24,4 +22,4 @@ I created a benchmark suite for the `tfb`, see [here](https://github.com/TechEmp
### Another test suite ### Another test suite
I also created a test suite for another web frameworks benchmark repository, see [here](https://github.com/the-benchmarker/web-frameworks/tree/master/cpp/drogon). In this project, Drogon is used as a sub-module (locally include in the project). I also created a test suite for another web frameworks benchmark repository, see [here](https://github.com/the-benchmarker/web-frameworks/tree/master/cpp/drogon), in this project, drogon is used as a sub-module (locally include in the project).

View File

@ -1,167 +0,0 @@
#include <drogon/drogon.h>
#include <drogon/HttpController.h>
#include <drogon/HttpRequest.h>
#include <fstream>
using namespace drogon;
class StreamEchoReader : public RequestStreamReader
{
public:
StreamEchoReader(ResponseStreamPtr respStream)
: respStream_(std::move(respStream))
{
}
void onStreamData(const char *data, size_t length) override
{
LOG_INFO << "onStreamData[" << length << "]";
respStream_->send({data, length});
}
void onStreamFinish(std::exception_ptr ptr) override
{
if (ptr)
{
try
{
std::rethrow_exception(ptr);
}
catch (const std::exception &e)
{
LOG_ERROR << "onStreamError: " << e.what();
}
}
else
{
LOG_INFO << "onStreamFinish";
}
respStream_->close();
}
private:
ResponseStreamPtr respStream_;
};
class RequestStreamExampleCtrl : public HttpController<RequestStreamExampleCtrl>
{
public:
METHOD_LIST_BEGIN
ADD_METHOD_TO(RequestStreamExampleCtrl::stream_echo, "/stream_echo", Post);
ADD_METHOD_TO(RequestStreamExampleCtrl::stream_upload,
"/stream_upload",
Post);
METHOD_LIST_END
void stream_echo(
const HttpRequestPtr &,
RequestStreamPtr &&stream,
std::function<void(const HttpResponsePtr &)> &&callback) const
{
auto resp = drogon::HttpResponse::newAsyncStreamResponse(
[stream](ResponseStreamPtr respStream) {
stream->setStreamReader(
std::make_shared<StreamEchoReader>(std::move(respStream)));
});
callback(resp);
}
void stream_upload(
const HttpRequestPtr &req,
RequestStreamPtr &&stream,
std::function<void(const HttpResponsePtr &)> &&callback) const
{
struct Entry
{
MultipartHeader header;
std::string tmpName;
std::ofstream file;
};
auto files = std::make_shared<std::vector<Entry>>();
auto reader = RequestStreamReader::newMultipartReader(
req,
[files](MultipartHeader &&header) {
LOG_INFO << "Multipart name: " << header.name
<< ", filename:" << header.filename
<< ", contentType:" << header.contentType;
files->push_back({std::move(header)});
auto tmpName = drogon::utils::genRandomString(40);
if (!files->back().header.filename.empty())
{
files->back().tmpName = tmpName;
files->back().file.open("uploads/" + tmpName,
std::ios::trunc);
}
},
[files](const char *data, size_t length) {
if (files->back().tmpName.empty())
{
return;
}
auto &currentFile = files->back().file;
if (length == 0)
{
LOG_INFO << "file finish";
if (currentFile.is_open())
{
currentFile.flush();
currentFile.close();
}
return;
}
LOG_INFO << "data[" << length << "]: ";
if (currentFile.is_open())
{
LOG_INFO << "write file";
currentFile.write(data, length);
}
else
{
LOG_ERROR << "file not open";
}
},
[files, callback = std::move(callback)](std::exception_ptr ex) {
if (ex)
{
try
{
std::rethrow_exception(std::move(ex));
}
catch (const StreamError &e)
{
LOG_ERROR << "stream error: " << e.what();
}
catch (const std::exception &e)
{
LOG_ERROR << "multipart error: " << e.what();
}
auto resp = HttpResponse::newHttpResponse();
resp->setStatusCode(k400BadRequest);
resp->setBody("error\n");
callback(resp);
}
else
{
LOG_INFO << "stream finish, received " << files->size()
<< " files";
Json::Value respJson;
for (const auto &item : *files)
{
if (item.tmpName.empty())
continue;
Json::Value entry;
entry["name"] = item.header.name;
entry["filename"] = item.header.filename;
entry["tmpName"] = item.tmpName;
respJson.append(entry);
}
auto resp = HttpResponse::newHttpJsonResponse(respJson);
callback(resp);
}
});
stream->setStreamReader(std::move(reader));
}
};

View File

@ -1,113 +0,0 @@
#include <drogon/drogon.h>
#include <chrono>
#include <functional>
#include <mutex>
#include <unordered_map>
#include <trantor/utils/Logger.h>
#include <trantor/net/callbacks.h>
#include <trantor/net/TcpConnection.h>
using namespace drogon;
using namespace std::chrono_literals;
std::mutex mutex;
std::unordered_map<trantor::TcpConnectionPtr, std::function<void()>>
connMapping;
int main()
{
app().registerHandler(
"/stream",
[](const HttpRequestPtr &req,
std::function<void(const HttpResponsePtr &)> &&callback) {
const auto &weakConnPtr = req->getConnectionPtr();
if (auto connPtr = weakConnPtr.lock())
{
std::lock_guard lk(mutex);
connMapping.emplace(std::move(connPtr), [] {
LOG_INFO << "call stop or other options!!!!";
});
}
auto resp = drogon::HttpResponse::newAsyncStreamResponse(
[](drogon::ResponseStreamPtr stream) {
std::thread([stream =
std::shared_ptr<drogon::ResponseStream>{
std::move(stream)}]() mutable {
std::cout << std::boolalpha << stream->send("hello ")
<< std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << std::boolalpha << stream->send("world");
std::this_thread::sleep_for(std::chrono::seconds(2));
stream->close();
}).detach();
});
resp->setContentTypeCodeAndCustomString(
ContentType::CT_APPLICATION_JSON, "application/json");
callback(resp);
});
// Example: register a stream-mode function handler
app().registerHandler(
"/stream_req",
[](const HttpRequestPtr &req,
RequestStreamPtr &&stream,
std::function<void(const HttpResponsePtr &)> &&callback) {
if (!stream)
{
LOG_INFO << "stream mode is not enabled";
auto resp = HttpResponse::newHttpResponse();
resp->setStatusCode(k400BadRequest);
resp->setBody("no stream");
callback(resp);
return;
}
auto reader = RequestStreamReader::newReader(
[](const char *data, size_t length) {
LOG_INFO << "piece[" << length
<< "]: " << std::string_view{data, length};
},
[callback = std::move(callback)](std::exception_ptr ex) {
auto resp = HttpResponse::newHttpResponse();
if (ex)
{
try
{
std::rethrow_exception(std::move(ex));
}
catch (const std::exception &e)
{
LOG_ERROR << "stream error: " << e.what();
}
resp->setStatusCode(k400BadRequest);
resp->setBody("error\n");
callback(resp);
}
else
{
LOG_INFO << "stream finish";
resp->setBody("success\n");
callback(resp);
}
});
stream->setStreamReader(std::move(reader));
},
{Post});
LOG_INFO << "Server running on 127.0.0.1:8848";
app().enableRequestStream(); // This is for request stream.
app().setConnectionCallback([](const trantor::TcpConnectionPtr &conn) {
if (conn->disconnected())
{
std::lock_guard lk(mutex);
if (auto it = connMapping.find(conn); it != connMapping.end())
{
LOG_INFO << "disconnect";
connMapping[conn]();
connMapping.erase(conn);
}
}
});
app().addListener("127.0.0.1", 8848).run();
}

View File

@ -1,5 +1,4 @@
#include "BenchmarkCtrl.h" #include "BenchmarkCtrl.h"
void BenchmarkCtrl::asyncHandleHttpRequest( void BenchmarkCtrl::asyncHandleHttpRequest(
const HttpRequestPtr &, const HttpRequestPtr &,
std::function<void(const HttpResponsePtr &)> &&callback) std::function<void(const HttpResponsePtr &)> &&callback)

View File

@ -1,11 +1,10 @@
#pragma once #pragma once
#include <drogon/HttpSimpleController.h> #include <drogon/HttpSimpleController.h>
using namespace drogon; using namespace drogon;
class BenchmarkCtrl : public drogon::HttpSimpleController<BenchmarkCtrl> class BenchmarkCtrl : public drogon::HttpSimpleController<BenchmarkCtrl>
{ {
public: public:
void asyncHandleHttpRequest( virtual void asyncHandleHttpRequest(
const HttpRequestPtr &req, const HttpRequestPtr &req,
std::function<void(const HttpResponsePtr &)> &&callback) override; std::function<void(const HttpResponsePtr &)> &&callback) override;
PATH_LIST_BEGIN PATH_LIST_BEGIN

View File

@ -1,11 +1,10 @@
#include "JsonCtrl.h" #include "JsonCtrl.h"
void JsonCtrl::asyncHandleHttpRequest( void JsonCtrl::asyncHandleHttpRequest(
const HttpRequestPtr &, const HttpRequestPtr &,
std::function<void(const HttpResponsePtr &)> &&callback) std::function<void(const HttpResponsePtr &)> &&callback)
{ {
Json::Value ret; Json::Value ret;
ret["message"] = "Hello, World!"; ret["message"] = "Hello, World!";
auto resp = HttpResponse::newHttpJsonResponse(std::move(ret)); auto resp = HttpResponse::newHttpJsonResponse(ret);
callback(resp); callback(resp);
} }

View File

@ -1,11 +1,10 @@
#pragma once #pragma once
#include <drogon/HttpSimpleController.h> #include <drogon/HttpSimpleController.h>
using namespace drogon; using namespace drogon;
class JsonCtrl : public drogon::HttpSimpleController<JsonCtrl> class JsonCtrl : public drogon::HttpSimpleController<JsonCtrl>
{ {
public: public:
void asyncHandleHttpRequest( virtual void asyncHandleHttpRequest(
const HttpRequestPtr &req, const HttpRequestPtr &req,
std::function<void(const HttpResponsePtr &)> &&callback) override; std::function<void(const HttpResponsePtr &)> &&callback) override;
PATH_LIST_BEGIN PATH_LIST_BEGIN

View File

@ -1,7 +1,6 @@
#include <drogon/drogon.h> #include <drogon/drogon.h>
using namespace drogon; using namespace drogon;
int main() int main()
{ {
app() app()

View File

@ -72,8 +72,6 @@ int main()
std::cout << "count=" << nth_resp << std::endl; std::cout << "count=" << nth_resp << std::endl;
}); });
} }
std::cout << "requestsBufferSize:" << client->requestsBufferSize()
<< std::endl;
} }
app().run(); app().run();

View File

@ -1,153 +0,0 @@
#include <drogon/HttpAppFramework.h>
#include <drogon/HttpResponse.h>
#include <drogon/drogon.h>
#include "trantor/utils/Logger.h"
using namespace drogon;
/// Configure Cross-Origin Resource Sharing (CORS) support.
///
/// This function registers both synchronous pre-processing advice for handling
/// OPTIONS preflight requests and post-handling advice to inject CORS headers
/// into all responses dynamically based on the incoming request headers.
void setupCors()
{
// Register sync advice to handle CORS preflight (OPTIONS) requests
drogon::app().registerSyncAdvice([](const drogon::HttpRequestPtr &req)
-> drogon::HttpResponsePtr {
if (req->method() == drogon::HttpMethod::Options)
{
auto resp = drogon::HttpResponse::newHttpResponse();
// Set Access-Control-Allow-Origin header based on the Origin
// request header
const auto &origin = req->getHeader("Origin");
if (!origin.empty())
{
resp->addHeader("Access-Control-Allow-Origin", origin);
}
// Set Access-Control-Allow-Methods based on the requested method
const auto &requestMethod =
req->getHeader("Access-Control-Request-Method");
if (!requestMethod.empty())
{
resp->addHeader("Access-Control-Allow-Methods", requestMethod);
}
// Allow credentials to be included in cross-origin requests
resp->addHeader("Access-Control-Allow-Credentials", "true");
// Set allowed headers from the Access-Control-Request-Headers
// header
const auto &requestHeaders =
req->getHeader("Access-Control-Request-Headers");
if (!requestHeaders.empty())
{
resp->addHeader("Access-Control-Allow-Headers", requestHeaders);
}
return std::move(resp);
}
return {};
});
// Register post-handling advice to add CORS headers to all responses
drogon::app().registerPostHandlingAdvice(
[](const drogon::HttpRequestPtr &req,
const drogon::HttpResponsePtr &resp) -> void {
// Set Access-Control-Allow-Origin based on the Origin request
// header
const auto &origin = req->getHeader("Origin");
if (!origin.empty())
{
resp->addHeader("Access-Control-Allow-Origin", origin);
}
// Reflect the requested Access-Control-Request-Method back in the
// response
const auto &requestMethod =
req->getHeader("Access-Control-Request-Method");
if (!requestMethod.empty())
{
resp->addHeader("Access-Control-Allow-Methods", requestMethod);
}
// Allow credentials to be included in cross-origin requests
resp->addHeader("Access-Control-Allow-Credentials", "true");
// Reflect the requested Access-Control-Request-Headers back
const auto &requestHeaders =
req->getHeader("Access-Control-Request-Headers");
if (!requestHeaders.empty())
{
resp->addHeader("Access-Control-Allow-Headers", requestHeaders);
}
});
}
/**
* Main function to start the Drogon application with CORS-enabled routes.
* This example includes:
* - A simple GET endpoint `/hello` that returns a greeting message.
* - A POST endpoint `/echo` that echoes back the request body.
* You can test with curl to test the CORS support:
*
```
curl -i -X OPTIONS http://localhost:8000/echo \
-H "Origin: http://localhost:3000" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type"
```
or
```
curl -i -X POST http://localhost:8000/echo \
-H "Origin: http://localhost:3000" \
-H "Content-Type: application/json" \
-d '{"key":"value"}'
```
*/
int main()
{
// Listen on port 8000 for all interfaces
app().addListener("0.0.0.0", 8000);
// Setup CORS support
setupCors();
// Register /hello route for GET and OPTIONS methods
app().registerHandler(
"/hello",
[](const HttpRequestPtr &req,
std::function<void(const HttpResponsePtr &)> &&callback) {
auto resp = HttpResponse::newHttpResponse();
resp->setBody("Hello from Drogon!");
// Log client IP address
LOG_INFO << "Request to /hello from " << req->getPeerAddr().toIp();
callback(resp);
},
{Get, Options});
// Register /echo route for POST and OPTIONS methods
app().registerHandler(
"/echo",
[](const HttpRequestPtr &req,
std::function<void(const HttpResponsePtr &)> &&callback) {
auto resp = HttpResponse::newHttpResponse();
resp->setBody(std::string("Echo: ").append(req->getBody()));
// Log client IP and request body
LOG_INFO << "Request to /echo from " << req->getPeerAddr().toIp();
LOG_INFO << "Echo content: " << req->getBody();
callback(resp);
},
{Post, Options});
// Start the application main loop
app().run();
return 0;
}

View File

@ -1,11 +1,3 @@
#include <trantor/utils/Logger.h>
#ifdef _WIN32
#include <ws2tcpip.h>
#else
#include <netinet/tcp.h>
#include <sys/socket.h>
#endif
#include <drogon/drogon.h> #include <drogon/drogon.h>
using namespace drogon; using namespace drogon;
@ -16,10 +8,8 @@ int main()
// sent to Drogon // sent to Drogon
app().registerHandler( app().registerHandler(
"/", "/",
[](const HttpRequestPtr &request, [](const HttpRequestPtr &,
std::function<void(const HttpResponsePtr &)> &&callback) { std::function<void(const HttpResponsePtr &)> &&callback) {
LOG_INFO << "connected:"
<< (request->connected() ? "true" : "false");
auto resp = HttpResponse::newHttpResponse(); auto resp = HttpResponse::newHttpResponse();
resp->setBody("Hello, World!"); resp->setBody("Hello, World!");
callback(resp); callback(resp);
@ -71,23 +61,6 @@ int main()
}, },
{Get}); {Get});
app()
.setBeforeListenSockOptCallback([](int fd) {
LOG_INFO << "setBeforeListenSockOptCallback:" << fd;
#ifdef _WIN32
#elif __linux__
int enable = 1;
if (setsockopt(
fd, IPPROTO_TCP, TCP_FASTOPEN, &enable, sizeof(enable)) ==
-1)
{
LOG_INFO << "setsockopt TCP_FASTOPEN failed";
}
#else
#endif
})
.setAfterAcceptSockOptCallback([](int) {});
// Ask Drogon to listen on 127.0.0.1 port 8848. Drogon supports listening // Ask Drogon to listen on 127.0.0.1 port 8848. Drogon supports listening
// on multiple IP addresses by adding multiple listeners. For example, if // on multiple IP addresses by adding multiple listeners. For example, if
// you want the server also listen on 127.0.0.1 port 5555. Just add another // you want the server also listen on 127.0.0.1 port 5555. Just add another

View File

@ -24,7 +24,7 @@ Create a new JSON object associated with the token
* **method**: POST * **method**: POST
* **URL params**: None * **URL params**: None
* **Body**: The initial JSON object to store * **Body**: The inital JSON object to store
* **Success response** * **Success response**
* **Code**: 200 * **Code**: 200
* **Content**: `{"ok":true}` * **Content**: `{"ok":true}`
@ -121,7 +121,7 @@ Creating new data
> {"ok":true} > {"ok":true}
Retrieving value of data["foo"]["bar"] Retrieving value of data["foo"]["bar"]
> 42 > 42
Modifying data Modifing data
> {"ok":true} > {"ok":true}
Now data["foo"]["bar"] no longer exists Now data["foo"]["bar"] no longer exists
> {"ok":false} > {"ok":false}

View File

@ -1,561 +0,0 @@
# Created by https://www.toptal.com/developers/gitignore/api/intellij+all,visualstudio,visualstudiocode,cmake,c,c++
# Edit at https://www.toptal.com/developers/gitignore?templates=intellij+all,visualstudio,visualstudiocode,cmake,c,c++
### C ###
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
### C++ ###
# Prerequisites
# Compiled Object files
*.slo
# Precompiled Headers
# Linker files
# Debugger Files
# Compiled Dynamic libraries
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
# Executables
### CMake ###
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
CMakeUserPresets.json
### CMake Patch ###
# External projects
*-prefix/
### Intellij+all ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### Intellij+all Patch ###
# Ignores the whole .idea folder and all .iml files
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
.idea/
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
*.iml
modules.xml
.idea/misc.xml
*.ipr
# Sonarlint plugin
.idea/sonarlint
### VisualStudioCode ###
.vscode/*
!.vscode/tasks.json
!.vscode/launch.json
*.code-workspace
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
### VisualStudio ###
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.meta
*.iobj
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*[.json, .xml, .info]
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
### VisualStudio Patch ###
# Additional files built by Visual Studio
*.tlog
# End of https://www.toptal.com/developers/gitignore/api/intellij+all,visualstudio,visualstudiocode,cmake,c,c++

View File

@ -1,75 +0,0 @@
cmake_minimum_required(VERSION 3.5)
project(prometheus_example CXX)
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 (NOT "${CMAKE_CXX_STANDARD}" STREQUAL "")
# Do nothing
elseif (HAS_ANY AND HAS_STRING_VIEW AND HAS_COROUTINE)
set(CMAKE_CXX_STANDARD 20)
elseif (HAS_ANY AND HAS_STRING_VIEW)
set(CMAKE_CXX_STANDARD 17)
else ()
set(CMAKE_CXX_STANDARD 14)
endif ()
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
add_executable(${PROJECT_NAME} main.cc)
# ##############################################################################
# If you include the drogon source code locally in your project, use this method
# to add drogon
# add_subdirectory(drogon)
# target_link_libraries(${PROJECT_NAME} PRIVATE drogon)
#
# and comment out the following lines
find_package(Drogon CONFIG REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)
# ##############################################################################
if (CMAKE_CXX_STANDARD LESS 17)
message(FATAL_ERROR "c++17 or higher is required")
elseif (CMAKE_CXX_STANDARD LESS 20)
message(STATUS "use c++17")
else ()
message(STATUS "use c++20")
endif ()
aux_source_directory(controllers CTL_SRC)
aux_source_directory(filters FILTER_SRC)
aux_source_directory(plugins PLUGIN_SRC)
aux_source_directory(models MODEL_SRC)
drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views
${CMAKE_CURRENT_BINARY_DIR})
# use the following line to create views with namespaces.
# drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views
# ${CMAKE_CURRENT_BINARY_DIR} TRUE)
# use the following line to create views with namespace CHANGE_ME prefixed
# and path namespaces.
# drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views
# ${CMAKE_CURRENT_BINARY_DIR} TRUE CHANGE_ME)
target_include_directories(${PROJECT_NAME}
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/models)
target_sources(${PROJECT_NAME}
PRIVATE
${SRC_DIR}
${CTL_SRC}
${FILTER_SRC}
${PLUGIN_SRC}
${MODEL_SRC})
# ##############################################################################
# uncomment the following line for dynamically loading views
# set_property(TARGET ${PROJECT_NAME} PROPERTY ENABLE_EXPORTS ON)
# ##############################################################################
add_subdirectory(test)

View File

@ -1,33 +0,0 @@
{
"listeners": [
{
"address": "0.0.0.0",
"port": 5555,
"https": false
}
],
"plugins": [
{
"name": "drogon::plugin::PromExporter",
"dependencies": [],
"config": {
"path": "/metrics",
"collectors":[
{
"name": "http_requests_total",
"help": "The total number of http requests",
"type": "counter",
"labels": ["method", "path"]
},
{
"name": "http_request_duration_seconds",
"help": "The processing time of http requests, in seconds",
"type": "histogram",
"labels": ["method", "path"]
}
]
}
}
]
}

View File

@ -1,27 +0,0 @@
#include "PromTestCtrl.h"
using namespace drogon;
void PromTestCtrl::fast(const HttpRequestPtr &req,
std::function<void(const HttpResponsePtr &)> &&callback)
{
auto resp = HttpResponse::newHttpResponse();
resp->setBody("Hello, world!");
callback(resp);
}
drogon::AsyncTask PromTestCtrl::slow(
const HttpRequestPtr req,
std::function<void(const HttpResponsePtr &)> callback)
{
// sleep for a random time between 1 and 3 seconds
static std::once_flag flag;
std::call_once(flag, []() { srand(time(nullptr)); });
auto duration = 1 + (rand() % 3);
auto loop = trantor::EventLoop::getEventLoopOfCurrentThread();
co_await drogon::sleepCoro(loop, std::chrono::seconds(duration));
auto resp = HttpResponse::newHttpResponse();
resp->setBody("Hello, world!");
callback(resp);
co_return;
}

View File

@ -1,21 +0,0 @@
#pragma once
#include <drogon/HttpController.h>
#include <drogon/utils/coroutine.h>
using namespace drogon;
class PromTestCtrl : public drogon::HttpController<PromTestCtrl>
{
public:
METHOD_LIST_BEGIN
ADD_METHOD_TO(PromTestCtrl::fast, "/fast", "PromStat");
ADD_METHOD_TO(PromTestCtrl::slow, "/slow", "PromStat");
METHOD_LIST_END
void fast(const HttpRequestPtr &req,
std::function<void(const HttpResponsePtr &)> &&callback);
drogon::AsyncTask slow(
const HttpRequestPtr req,
std::function<void(const HttpResponsePtr &)> callback);
};

View File

@ -1,52 +0,0 @@
/**
*
* PromStat.cc
*
*/
#include "PromStat.h"
#include <drogon/plugins/PromExporter.h>
#include <drogon/utils/monitoring/Counter.h>
#include <drogon/utils/monitoring/Histogram.h>
#include <drogon/HttpAppFramework.h>
#include <chrono>
using namespace std::literals::chrono_literals;
using namespace drogon;
Task<HttpResponsePtr> PromStat::invoke(const HttpRequestPtr &req,
MiddlewareNextAwaiter &&next)
{
std::string path{req->matchedPathPattern()};
auto method = req->methodString();
auto promExporter = app().getPlugin<drogon::plugin::PromExporter>();
if (promExporter)
{
auto collector =
promExporter->getCollector<drogon::monitoring::Counter>(
"http_requests_total");
if (collector)
{
collector->metric({method, path})->increment();
}
}
auto start = trantor::Date::date();
auto resp = co_await next;
if (promExporter)
{
auto collector =
promExporter->getCollector<drogon::monitoring::Histogram>(
"http_request_duration_seconds");
if (collector)
{
static const std::vector<double> boundaries{
0.0001, 0.001, 0.01, 0.1, 0.5, 1, 2, 3};
auto end = trantor::Date::date();
auto duration =
end.microSecondsSinceEpoch() - start.microSecondsSinceEpoch();
collector->metric({method, path}, boundaries, 1h, 6)
->observe((double)duration / 1000000);
}
}
co_return resp;
}

View File

@ -1,27 +0,0 @@
/**
*
* PromStat.h
*
*/
#pragma once
#include <drogon/HttpMiddleware.h>
using namespace drogon;
class PromStat : public HttpCoroMiddleware<PromStat>
{
public:
PromStat()
{
void(0);
}
virtual ~PromStat()
{
}
Task<HttpResponsePtr> invoke(const HttpRequestPtr &req,
MiddlewareNextAwaiter &&next) override;
};

View File

@ -1,7 +0,0 @@
#include <drogon/drogon.h>
int main()
{
drogon::app().loadConfigFile("../config.json").run();
return 0;
}

View File

@ -1,14 +0,0 @@
cmake_minimum_required(VERSION 3.5)
project(prometheus_example_test CXX)
add_executable(${PROJECT_NAME} test_main.cc)
# ##############################################################################
# If you include the drogon source code locally in your project, use this method
# to add drogon
# target_link_libraries(${PROJECT_NAME} PRIVATE drogon)
#
# and comment out the following lines
target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)
ParseAndAddDrogonTests(${PROJECT_NAME})

View File

@ -1,32 +0,0 @@
#define DROGON_TEST_MAIN
#include <drogon/drogon_test.h>
#include <drogon/drogon.h>
DROGON_TEST(BasicTest)
{
// Add your tests here
}
int main(int argc, char **argv)
{
using namespace drogon;
std::promise<void> p1;
std::future<void> f1 = p1.get_future();
// Start the main loop on another thread
std::thread thr([&]() {
// Queues the promise to be fulfilled after starting the loop
app().getLoop()->queueInLoop([&p1]() { p1.set_value(); });
app().run();
});
// The future is only satisfied after the event loop started
f1.get();
int status = test::run(argc, argv);
// Ask the event loop to shutdown and wait
app().getLoop()->queueInLoop([]() { app().quit(); });
thr.join();
return status;
}

View File

@ -32,7 +32,7 @@ target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)
# ############################################################################## # ##############################################################################
if (CMAKE_CXX_STANDARD LESS 17) if (CMAKE_CXX_STANDARD LESS 17)
# With C++14, use boost to support any and std::string_view # With C++14, use boost to support any and string_view
message(STATUS "use c++14") message(STATUS "use c++14")
find_package(Boost 1.61.0 REQUIRED) find_package(Boost 1.61.0 REQUIRED)
target_include_directories(${PROJECT_NAME} PRIVATE ${Boost_INCLUDE_DIRS}) target_include_directories(${PROJECT_NAME} PRIVATE ${Boost_INCLUDE_DIRS})

View File

@ -1,7 +1,6 @@
#pragma once #pragma once
#include <drogon/HttpController.h> #include <drogon/HttpController.h>
using namespace drogon; using namespace drogon;
class Client : public drogon::HttpController<Client> class Client : public drogon::HttpController<Client>
{ {
public: public:

View File

@ -7,12 +7,13 @@ using namespace drogon;
class WsClient : public drogon::WebSocketController<WsClient> class WsClient : public drogon::WebSocketController<WsClient>
{ {
public: public:
void handleNewMessage(const WebSocketConnectionPtr &, virtual void handleNewMessage(const WebSocketConnectionPtr &,
std::string &&, std::string &&,
const WebSocketMessageType &) override; const WebSocketMessageType &) override;
void handleNewConnection(const HttpRequestPtr &, virtual void handleNewConnection(const HttpRequestPtr &,
const WebSocketConnectionPtr &) override;
virtual void handleConnectionClosed(
const WebSocketConnectionPtr &) override; const WebSocketConnectionPtr &) override;
void handleConnectionClosed(const WebSocketConnectionPtr &) override;
WS_PATH_LIST_BEGIN WS_PATH_LIST_BEGIN
WS_PATH_ADD("/sub"); WS_PATH_ADD("/sub");
WS_PATH_ADD("/pub"); WS_PATH_ADD("/pub");

View File

@ -6,7 +6,6 @@ inline trantor::Date fromString<trantor::Date>(const std::string &str)
{ {
return trantor::Date(std::atoll(str.data())); return trantor::Date(std::atoll(str.data()));
} }
template <> template <>
inline std::string toString<trantor::Date>(const trantor::Date &date) inline std::string toString<trantor::Date>(const trantor::Date &date)
{ {

View File

@ -41,11 +41,7 @@
//enable_session: False by default //enable_session: False by default
"enable_session": false, "enable_session": false,
"session_timeout": 0, "session_timeout": 0,
//session_cookie_key: The cookie key of the session, "JSESSIONID" by default //document_root: Root path of HTTP document, defaut path is ./
"session_cookie_key": "JSESSIONID",
//session_max_age: The max age of the session cookie, -1 by default
"session_max_age": -1,
//document_root: Root path of HTTP document, default path is ./
"document_root": "./", "document_root": "./",
//home_page: Set the HTML file of the home page, the default value is "index.html" //home_page: Set the HTML file of the home page, the default value is "index.html"
//If there isn't any handler registered to the path "/", the home page file in the "document_root" is send to clients as a response //If there isn't any handler registered to the path "/", the home page file in the "document_root" is send to clients as a response
@ -111,7 +107,7 @@
], ],
//max_connections: maximum number of connections, 100000 by default //max_connections: maximum number of connections, 100000 by default
"max_connections": 100000, "max_connections": 100000,
//max_connections_per_ip: maximum number of connections per client, 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, "max_connections_per_ip": 0,
//Load_dynamic_views: False by default, when set to true, drogon //Load_dynamic_views: False by default, when set to true, drogon
//compiles and loads dynamically "CSP View Files" in directories defined //compiles and loads dynamically "CSP View Files" in directories defined

View File

@ -3,7 +3,6 @@
#include "DateFuncs.h" #include "DateFuncs.h"
#include <drogon/drogon.h> #include <drogon/drogon.h>
#define VDate "visitDate" #define VDate "visitDate"
void SlowCtrl::hello(const HttpRequestPtr &req, void SlowCtrl::hello(const HttpRequestPtr &req,
std::function<void(const HttpResponsePtr &)> &&callback, std::function<void(const HttpResponsePtr &)> &&callback,
std::string &&userid) std::string &&userid)

View File

@ -1,7 +1,6 @@
#pragma once #pragma once
#include <drogon/HttpController.h> #include <drogon/HttpController.h>
using namespace drogon; using namespace drogon;
class SlowCtrl : public drogon::HttpController<SlowCtrl> class SlowCtrl : public drogon::HttpController<SlowCtrl>
{ {
public: public:

View File

@ -10,10 +10,9 @@ using namespace drogon;
class TimeFilter : public drogon::HttpFilter<TimeFilter> class TimeFilter : public drogon::HttpFilter<TimeFilter>
{ {
public: public:
void doFilter(const HttpRequestPtr &req, virtual void doFilter(const HttpRequestPtr &req,
FilterCallback &&cb, FilterCallback &&cb,
FilterChainCallback &&ccb) override; FilterChainCallback &&ccb) override;
TimeFilter() TimeFilter()
{ {
LOG_DEBUG << "TimeFilter constructor"; LOG_DEBUG << "TimeFilter constructor";

View File

@ -1,5 +1,4 @@
#include <drogon/drogon.h> #include <drogon/drogon.h>
int main() int main()
{ {
drogon::app().loadConfigFile("../config.json"); drogon::app().loadConfigFile("../config.json");

View File

@ -32,7 +32,7 @@ target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)
# ############################################################################## # ##############################################################################
if (CMAKE_CXX_STANDARD LESS 17) if (CMAKE_CXX_STANDARD LESS 17)
# With C++14, use boost to support any and std::string_view # With C++14, use boost to support any and string_view
message(STATUS "use c++14") message(STATUS "use c++14")
find_package(Boost 1.61.0 REQUIRED) find_package(Boost 1.61.0 REQUIRED)
target_include_directories(${PROJECT_NAME} PRIVATE ${Boost_INCLUDE_DIRS}) target_include_directories(${PROJECT_NAME} PRIVATE ${Boost_INCLUDE_DIRS})

View File

@ -7,12 +7,13 @@ using namespace drogon;
class Chat : public drogon::WebSocketController<Chat> class Chat : public drogon::WebSocketController<Chat>
{ {
public: public:
void handleNewMessage(const WebSocketConnectionPtr &, virtual void handleNewMessage(const WebSocketConnectionPtr &,
std::string &&, std::string &&,
const WebSocketMessageType &) override; const WebSocketMessageType &) override;
void handleNewConnection(const HttpRequestPtr &, virtual void handleNewConnection(const HttpRequestPtr &,
const WebSocketConnectionPtr &) override;
virtual void handleConnectionClosed(
const WebSocketConnectionPtr &) override; const WebSocketConnectionPtr &) override;
void handleConnectionClosed(const WebSocketConnectionPtr &) override;
WS_PATH_LIST_BEGIN WS_PATH_LIST_BEGIN
WS_PATH_ADD("/chat"); WS_PATH_ADD("/chat");
WS_PATH_LIST_END WS_PATH_LIST_END

View File

@ -22,11 +22,7 @@
//enable_session: False by default //enable_session: False by default
"enable_session": false, "enable_session": false,
"session_timeout": 0, "session_timeout": 0,
//session_cookie_key: The cookie key of the session, "JSESSIONID" by default //document_root: Root path of HTTP document, defaut path is ./
"session_cookie_key": "JSESSIONID",
//session_max_age: The max age of the session cookie, -1 by default
"session_max_age": -1,
//document_root: Root path of HTTP document, default path is ./
"document_root": "./", "document_root": "./",
//home_page: Set the HTML file of the home page, the default value is "index.html" //home_page: Set the HTML file of the home page, the default value is "index.html"
//If there isn't any handler registered to the path "/", the home page file in the "document_root" is send to clients as a response //If there isn't any handler registered to the path "/", the home page file in the "document_root" is send to clients as a response
@ -64,7 +60,7 @@
], ],
//max_connections: maximum connections number, 100000 by default //max_connections: maximum connections number, 100000 by default
"max_connections": 100000, "max_connections": 100000,
//max_connections_per_ip: maximum connections number per client,0 by default which means no limit //max_connections_per_ip: maximum connections number per clinet,0 by default which means no limit
"max_connections_per_ip": 0, "max_connections_per_ip": 0,
//Load_dynamic_views: False by default, when set to true, drogon //Load_dynamic_views: False by default, when set to true, drogon
//compiles and loads dynamically "CSP View Files" in directories defined //compiles and loads dynamically "CSP View Files" in directories defined

View File

@ -1,5 +1,4 @@
#include <drogon/drogon.h> #include <drogon/drogon.h>
int main() int main()
{ {
// Set HTTP listener address and port // Set HTTP listener address and port

View File

@ -19,7 +19,6 @@ class SimpleReverseProxy : public drogon::Plugin<SimpleReverseProxy>
SimpleReverseProxy() SimpleReverseProxy()
{ {
} }
/// This method must be called by drogon to initialize and start the plugin. /// This method must be called by drogon to initialize and start the plugin.
/// It must be implemented by the user. /// It must be implemented by the user.
void initAndStart(const Json::Value &config) override; void initAndStart(const Json::Value &config) override;

View File

@ -10,11 +10,11 @@ int main(int argc, char *argv[])
{ {
std::string server; std::string server;
std::string path; std::string path;
std::optional<uint16_t> port; optional<uint16_t> port;
// Connect to a public echo server // Connect to a public echo server
if (argc > 1 && std::string(argv[1]) == "-p") if (argc > 1 && std::string(argv[1]) == "-p")
{ {
server = "wss://echo.websocket.events/.ws"; server = "wss://echo.websocket.org";
path = "/"; path = "/";
} }
else else

View File

@ -6,15 +6,15 @@ using namespace drogon;
class WebSocketChat : public drogon::WebSocketController<WebSocketChat> class WebSocketChat : public drogon::WebSocketController<WebSocketChat>
{ {
public: public:
void handleNewMessage(const WebSocketConnectionPtr &, virtual void handleNewMessage(const WebSocketConnectionPtr &,
std::string &&, std::string &&,
const WebSocketMessageType &) override; const WebSocketMessageType &) override;
void handleConnectionClosed(const WebSocketConnectionPtr &) override; virtual void handleConnectionClosed(
void handleNewConnection(const HttpRequestPtr &, const WebSocketConnectionPtr &) override;
virtual void handleNewConnection(const HttpRequestPtr &,
const WebSocketConnectionPtr &) override; const WebSocketConnectionPtr &) override;
WS_PATH_LIST_BEGIN WS_PATH_LIST_BEGIN
WS_PATH_ADD("/chat", Get); WS_PATH_ADD("/chat", Get);
WS_ADD_PATH_VIA_REGEX("/[^/]*", Get);
WS_PATH_LIST_END WS_PATH_LIST_END
private: private:
PubSubService<std::string> chatRooms_; PubSubService<std::string> chatRooms_;
@ -25,7 +25,6 @@ struct Subscriber
std::string chatRoomName_; std::string chatRoomName_;
drogon::SubscriberID id_; drogon::SubscriberID id_;
}; };
void WebSocketChat::handleNewMessage(const WebSocketConnectionPtr &wsConnPtr, void WebSocketChat::handleNewMessage(const WebSocketConnectionPtr &wsConnPtr,
std::string &&message, std::string &&message,
const WebSocketMessageType &type) const WebSocketMessageType &type)
@ -60,7 +59,7 @@ void WebSocketChat::handleNewConnection(const HttpRequestPtr &req,
s.id_ = chatRooms_.subscribe(s.chatRoomName_, s.id_ = chatRooms_.subscribe(s.chatRoomName_,
[conn](const std::string &topic, [conn](const std::string &topic,
const std::string &message) { const std::string &message) {
// Suppress unused variable warning // Supress unused variable warning
(void)topic; (void)topic;
conn->send(message); conn->send(message);
}); });

View File

@ -1,14 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# You can customize the clang-format path by setting the CLANG_FORMAT environment variable clang-format --version
CLANG_FORMAT=${CLANG_FORMAT:-clang-format}
# Check if clang-format version is 17 to avoid inconsistent formatting
$CLANG_FORMAT --version
if [[ ! $($CLANG_FORMAT --version) =~ "version 17" ]]; then
echo "Error: clang-format version must be 17"
exit 1
fi
find lib orm_lib nosql_lib examples drogon_ctl -name *.h -o -name *.cc -exec dos2unix {} \; find lib orm_lib nosql_lib examples drogon_ctl -name *.h -o -name *.cc -exec dos2unix {} \;
find lib orm_lib nosql_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

View File

@ -14,10 +14,10 @@
#pragma once #pragma once
#include <drogon/utils/any.h>
#include <trantor/utils/Logger.h> #include <trantor/utils/Logger.h>
#include <map> #include <map>
#include <memory> #include <memory>
#include <any>
namespace drogon namespace drogon
{ {
@ -39,13 +39,13 @@ class Attributes
template <typename T> template <typename T>
const T &get(const std::string &key) const const T &get(const std::string &key) const
{ {
static const T nullVal = T(); const static T nullVal = T();
auto it = attributesMap_.find(key); auto it = attributesMap_.find(key);
if (it != attributesMap_.end()) if (it != attributesMap_.end())
{ {
if (typeid(T) == it->second.type()) if (typeid(T) == it->second.type())
{ {
return *(std::any_cast<T>(&(it->second))); return *(any_cast<T>(&(it->second)));
} }
else else
{ {
@ -58,7 +58,7 @@ class Attributes
/** /**
* @brief Get the 'any' object identified by the given key * @brief Get the 'any' object identified by the given key
*/ */
std::any &operator[](const std::string &key) any &operator[](const std::string &key)
{ {
return attributesMap_[key]; return attributesMap_[key];
} }
@ -70,7 +70,7 @@ class Attributes
attributesPtr->insert("user name", userNameString); attributesPtr->insert("user name", userNameString);
@endcode @endcode
*/ */
void insert(const std::string &key, const std::any &obj) void insert(const std::string &key, const any &obj)
{ {
attributesMap_[key] = obj; attributesMap_[key] = obj;
} }
@ -82,7 +82,7 @@ class Attributes
attributesPtr->insert("user name", userNameString); attributesPtr->insert("user name", userNameString);
@endcode @endcode
*/ */
void insert(const std::string &key, std::any &&obj) void insert(const std::string &key, any &&obj)
{ {
attributesMap_[key] = std::move(obj); attributesMap_[key] = std::move(obj);
} }
@ -96,7 +96,7 @@ class Attributes
} }
/** /**
* @brief Return true if the data identified by the key exists. * @brief Retrun true if the data identified by the key exists.
*/ */
bool find(const std::string &key) bool find(const std::string &key)
{ {
@ -121,7 +121,7 @@ class Attributes
Attributes() = default; Attributes() = default;
private: private:
using AttributesMap = std::map<std::string, std::any>; using AttributesMap = std::map<std::string, any>;
AttributesMap attributesMap_; AttributesMap attributesMap_;
}; };

View File

@ -42,7 +42,6 @@ class CallbackEntry
CallbackEntry(std::function<void()> cb) : cb_(std::move(cb)) CallbackEntry(std::function<void()> cb) : cb_(std::move(cb))
{ {
} }
~CallbackEntry() ~CallbackEntry()
{ {
cb_(); cb_();
@ -81,11 +80,7 @@ class CacheMap
* number of wheels * number of wheels
* @param bucketsNumPerWheel * @param bucketsNumPerWheel
* buckets number per wheel * buckets number per wheel
* @param fnOnInsert * The max delay of the CacheMap is about
* function to execute on insertion
* @param fnOnErase
* function to execute on erase
* @details The max delay of the CacheMap is about
* tickInterval*(bucketsNumPerWheel^wheelsNum) seconds. * tickInterval*(bucketsNumPerWheel^wheelsNum) seconds.
*/ */
CacheMap(trantor::EventLoop *loop, CacheMap(trantor::EventLoop *loop,
@ -144,7 +139,6 @@ class CacheMap
noWheels_ = true; noWheels_ = true;
} }
}; };
~CacheMap() ~CacheMap()
{ {
std::lock_guard<std::mutex> lock(ctrlBlockPtr_->mtx); std::lock_guard<std::mutex> lock(ctrlBlockPtr_->mtx);
@ -160,7 +154,6 @@ class CacheMap
} }
LOG_TRACE << "CacheMap destruct!"; LOG_TRACE << "CacheMap destruct!";
} }
struct MapValue struct MapValue
{ {
MapValue(const T2 &value, MapValue(const T2 &value,
@ -171,32 +164,26 @@ class CacheMap
timeoutCallback_(std::move(callback)) timeoutCallback_(std::move(callback))
{ {
} }
MapValue(T2 &&value, size_t timeout, std::function<void()> &&callback) MapValue(T2 &&value, size_t timeout, std::function<void()> &&callback)
: value_(std::move(value)), : value_(std::move(value)),
timeout_(timeout), timeout_(timeout),
timeoutCallback_(std::move(callback)) timeoutCallback_(std::move(callback))
{ {
} }
MapValue(T2 &&value, size_t timeout) MapValue(T2 &&value, size_t timeout)
: value_(std::move(value)), timeout_(timeout) : value_(std::move(value)), timeout_(timeout)
{ {
} }
MapValue(const T2 &value, size_t timeout) MapValue(const T2 &value, size_t timeout)
: value_(value), timeout_(timeout) : value_(value), timeout_(timeout)
{ {
} }
MapValue(T2 &&value) : value_(std::move(value)) MapValue(T2 &&value) : value_(std::move(value))
{ {
} }
MapValue(const T2 &value) : value_(value) MapValue(const T2 &value) : value_(value)
{ {
} }
MapValue() = default; MapValue() = default;
T2 value_; T2 value_;
size_t timeout_{0}; size_t timeout_{0};
@ -235,7 +222,6 @@ class CacheMap
if (fnOnInsert_) if (fnOnInsert_)
fnOnInsert_(key); fnOnInsert_(key);
} }
/** /**
* @brief Insert a key-value pair into the cache. * @brief Insert a key-value pair into the cache.
* *
@ -394,7 +380,6 @@ class CacheMap
if (fnOnErase_) if (fnOnErase_)
fnOnErase_(key); fnOnErase_(key);
} }
/** /**
* @brief Get the event loop object * @brief Get the event loop object
* *
@ -419,7 +404,6 @@ class CacheMap
std::lock_guard<std::mutex> lock(bucketMutex_); std::lock_guard<std::mutex> lock(bucketMutex_);
insertEntry(delay, std::make_shared<CallbackEntry>(std::move(task))); insertEntry(delay, std::make_shared<CallbackEntry>(std::move(task)));
} }
void runAfter(size_t delay, const std::function<void()> &task) void runAfter(size_t delay, const std::function<void()> &task)
{ {
std::lock_guard<std::mutex> lock(bucketMutex_); std::lock_guard<std::mutex> lock(bucketMutex_);
@ -441,12 +425,10 @@ class CacheMap
ControlBlock() : destructed(false), loopEnded(false) ControlBlock() : destructed(false), loopEnded(false)
{ {
} }
bool destructed; bool destructed;
bool loopEnded; bool loopEnded;
std::mutex mtx; std::mutex mtx;
}; };
std::unordered_map<T1, MapValue> map_; std::unordered_map<T1, MapValue> map_;
std::vector<CallbackBucketQueue> wheels_; std::vector<CallbackBucketQueue> wheels_;
@ -504,7 +486,6 @@ class CacheMap
t = t / bucketsNumPerWheel_; t = t / bucketsNumPerWheel_;
} }
} }
void eraseAfter(size_t delay, const T1 &key) void eraseAfter(size_t delay, const T1 &key)
{ {
if (noWheels_) if (noWheels_)

View File

@ -14,14 +14,13 @@
#pragma once #pragma once
#include <drogon/exports.h> #include <drogon/exports.h>
#include <drogon/utils/optional.h>
#include <drogon/utils/string_view.h>
#include <trantor/utils/Date.h> #include <trantor/utils/Date.h>
#include <trantor/utils/Logger.h> #include <trantor/utils/Logger.h>
#include <drogon/utils/Utilities.h>
#include <cctype> #include <cctype>
#include <string> #include <string>
#include <limits> #include <limits>
#include <optional>
#include <string_view>
namespace drogon namespace drogon
{ {
@ -36,11 +35,14 @@ class DROGON_EXPORT Cookie
* @param key key of the cookie * @param key key of the cookie
* @param value value of the cookie * @param value value of the cookie
*/ */
Cookie(std::string key, std::string value) Cookie(const std::string &key, const std::string &value)
: key_(key), value_(value)
{
}
Cookie(std::string &&key, std::string &&value)
: key_(std::move(key)), value_(std::move(value)) : key_(std::move(key)), value_(std::move(value))
{ {
} }
Cookie() = default; Cookie() = default;
enum class SameSite enum class SameSite
{ {
@ -49,7 +51,6 @@ class DROGON_EXPORT Cookie
kStrict, kStrict,
kNone kNone
}; };
/** /**
* @brief Set the Expires Date * @brief Set the Expires Date
* *
@ -83,10 +84,6 @@ class DROGON_EXPORT Cookie
{ {
domain_ = domain; domain_ = domain;
} }
/**
* @brief Set the domain of the cookie.
*/
void setDomain(std::string &&domain) void setDomain(std::string &&domain)
{ {
domain_ = std::move(domain); domain_ = std::move(domain);
@ -99,10 +96,6 @@ class DROGON_EXPORT Cookie
{ {
path_ = path; path_ = path;
} }
/**
* @brief Set the path of the cookie.
*/
void setPath(std::string &&path) void setPath(std::string &&path)
{ {
path_ = std::move(path); path_ = std::move(path);
@ -115,15 +108,10 @@ class DROGON_EXPORT Cookie
{ {
key_ = key; key_ = key;
} }
/**
* @brief Set the key of the cookie.
*/
void setKey(std::string &&key) void setKey(std::string &&key)
{ {
key_ = std::move(key); key_ = std::move(key);
} }
/** /**
* @brief Set the value of the cookie. * @brief Set the value of the cookie.
*/ */
@ -131,15 +119,10 @@ class DROGON_EXPORT Cookie
{ {
value_ = value; value_ = value;
} }
/**
* @brief Set the value of the cookie.
*/
void setValue(std::string &&value) void setValue(std::string &&value)
{ {
value_ = std::move(value); value_ = std::move(value);
} }
/** /**
* @brief Set the max-age of the cookie. * @brief Set the max-age of the cookie.
*/ */
@ -147,7 +130,6 @@ class DROGON_EXPORT Cookie
{ {
maxAge_ = value; maxAge_ = value;
} }
/** /**
* @brief Set the same site of the cookie. * @brief Set the same site of the cookie.
*/ */
@ -156,18 +138,6 @@ class DROGON_EXPORT Cookie
sameSite_ = sameSite; sameSite_ = sameSite;
} }
/**
* @brief Set the partitioned status of the cookie
*/
void setPartitioned(bool partitioned)
{
partitioned_ = partitioned;
if (partitioned)
{
setSecure(true);
}
}
/** /**
* @brief Get the string value of the cookie * @brief Get the string value of the cookie
*/ */
@ -294,21 +264,10 @@ class DROGON_EXPORT Cookie
return secure_; return secure_;
} }
/**
* @brief Check if the cookie is partitioned.
*
* @return true means the cookie is partitioned.
* @return false means the cookie is not partitioned.
*/
bool isPartitioned() const
{
return partitioned_;
}
/** /**
* @brief Get the max-age of the cookie * @brief Get the max-age of the cookie
*/ */
std::optional<int> maxAge() const optional<int> maxAge() const
{ {
return maxAge_; return maxAge_;
} }
@ -316,7 +275,7 @@ class DROGON_EXPORT Cookie
/** /**
* @brief Get the max-age of the cookie * @brief Get the max-age of the cookie
*/ */
std::optional<int> getMaxAge() const optional<int> getMaxAge() const
{ {
return maxAge_; return maxAge_;
} }
@ -348,40 +307,49 @@ class DROGON_EXPORT Cookie
* str2. so the function doesn't apply tolower to the second argument * str2. so the function doesn't apply tolower to the second argument
* str2 as it's always in lower case. * str2 as it's always in lower case.
* *
* @return true if both strings are equal ignoring case * @return 0 if both strings are equall ignoring case, negative value if lhs
* is smaller than rhs and vice versa
*/ */
static bool stricmp(const std::string_view str1, static int stricmp(const string_view str1, const string_view str2)
const std::string_view str2)
{ {
auto str1Len{str1.length()}; auto str1Len{str1.length()};
auto str2Len{str2.length()}; auto str2Len{str2.length()};
if (str1Len != str2Len) if (str1Len != str2Len)
return false; return str1Len - str2Len;
for (size_t idx{0}; idx < str1Len; ++idx) for (size_t idx{0}; idx < str1Len; ++idx)
{ {
auto lowerChar{tolower(str1[idx])}; auto lowerChar{tolower(str1[idx])};
if (lowerChar != str2[idx]) if (lowerChar != str2[idx])
{ {
return false; return lowerChar - str2[idx];
} }
} }
return true;
return 0;
} }
/** /**
* @brief Converts a string value to its associated enum class SameSite * @brief Converts a string value to its associated enum class SameSite
* value * value
*/ */
static SameSite convertString2SameSite(std::string_view sameSite) static SameSite convertString2SameSite(const string_view &sameSite)
{
if (stricmp(sameSite, "lax") == 0)
{ {
if (stricmp(sameSite, "lax"))
return Cookie::SameSite::kLax; return Cookie::SameSite::kLax;
if (stricmp(sameSite, "strict")) }
else if (stricmp(sameSite, "strict") == 0)
{
return Cookie::SameSite::kStrict; return Cookie::SameSite::kStrict;
if (stricmp(sameSite, "none")) }
else if (stricmp(sameSite, "none") == 0)
{
return Cookie::SameSite::kNone; return Cookie::SameSite::kNone;
if (!stricmp(sameSite, "null")) }
else if (stricmp(sameSite, "null") != 0)
{ {
LOG_WARN LOG_WARN
<< "'" << sameSite << "'" << sameSite
@ -389,6 +357,7 @@ class DROGON_EXPORT Cookie
"or " "or "
"'None' are proper values. Return value is SameSite::kNull."; "'None' are proper values. Return value is SameSite::kNull.";
} }
return Cookie::SameSite::kNull; return Cookie::SameSite::kNull;
} }
@ -396,20 +365,34 @@ class DROGON_EXPORT Cookie
* @brief Converts an enum class SameSite value to its associated string * @brief Converts an enum class SameSite value to its associated string
* value * value
*/ */
static std::string_view convertSameSite2String(SameSite sameSite) static const string_view &convertSameSite2String(SameSite sameSite)
{ {
switch (sameSite) switch (sameSite)
{ {
case SameSite::kLax: case SameSite::kLax:
return "Lax"; {
static string_view sv{"Lax"};
return sv;
}
case SameSite::kStrict: case SameSite::kStrict:
return "Strict"; {
static string_view sv{"Strict"};
return sv;
}
case SameSite::kNone: case SameSite::kNone:
return "None"; {
static string_view sv{"None"};
return sv;
}
case SameSite::kNull: case SameSite::kNull:
return "Null"; {
default: static string_view sv{"Null"};
return "UNDEFINED"; return sv;
}
}
{
static string_view sv{"UNDEFINED"};
return sv;
} }
} }
@ -417,12 +400,11 @@ class DROGON_EXPORT Cookie
trantor::Date expiresDate_{(std::numeric_limits<int64_t>::max)()}; trantor::Date expiresDate_{(std::numeric_limits<int64_t>::max)()};
bool httpOnly_{true}; bool httpOnly_{true};
bool secure_{false}; bool secure_{false};
bool partitioned_{false};
std::string domain_; std::string domain_;
std::string path_; std::string path_;
std::string key_; std::string key_;
std::string value_; std::string value_;
std::optional<int> maxAge_; optional<int> maxAge_;
SameSite sameSite_{SameSite::kNull}; SameSite sameSite_{SameSite::kNull};
}; };

View File

@ -138,7 +138,7 @@ class DROGON_EXPORT DrClassMap
protected: protected:
static std::unordered_map<std::string, static std::unordered_map<std::string,
std::pair<DrAllocFunc, DrSharedAllocFunc>> & std::pair<DrAllocFunc, DrSharedAllocFunc>>
getMap(); &getMap();
}; };
} // namespace drogon } // namespace drogon

View File

@ -51,32 +51,11 @@ class DROGON_EXPORT DrObjectBase
{ {
return (className() == class_name); return (className() == class_name);
} }
virtual ~DrObjectBase() virtual ~DrObjectBase()
{ {
} }
}; };
template <typename T>
struct isAutoCreationClass
{
template <class C>
static constexpr auto check(C *) -> std::enable_if_t<
std::is_same_v<decltype(C::isAutoCreation), const bool>,
bool>
{
return C::isAutoCreation;
}
template <typename>
static constexpr bool check(...)
{
return false;
}
static constexpr bool value = check<T>(nullptr);
};
/** /**
* a class template to * a class template to
* implement the reflection function of creating the class object by class name * implement the reflection function of creating the class object by class name
@ -89,7 +68,6 @@ class DrObject : public virtual DrObjectBase
{ {
return alloc_.className(); return alloc_.className();
} }
static const std::string &classTypeName() static const std::string &classTypeName()
{ {
return alloc_.className(); return alloc_.className();
@ -113,18 +91,16 @@ class DrObject : public virtual DrObjectBase
{ {
registerClass<T>(); registerClass<T>();
} }
const std::string &className() const const std::string &className() const
{ {
static std::string className = static std::string className =
DrClassMap::demangle(typeid(T).name()); DrClassMap::demangle(typeid(T).name());
return className; return className;
} }
template <typename D> template <typename D>
void registerClass() typename std::enable_if<std::is_default_constructible<D>::value,
{ void>::type
if constexpr (std::is_default_constructible<D>::value) registerClass()
{ {
DrClassMap::registerClass( DrClassMap::registerClass(
className(), className(),
@ -133,18 +109,17 @@ class DrObject : public virtual DrObjectBase
return std::make_shared<T>(); return std::make_shared<T>();
}); });
} }
else if constexpr (isAutoCreationClass<D>::value) template <typename D>
typename std::enable_if<!std::is_default_constructible<D>::value,
void>::type
registerClass()
{ {
static_assert(std::is_default_constructible<D>::value,
"Class is not default constructable!");
}
} }
}; };
// use static val to register allocator function for class T; // use static val to register allocator function for class T;
static DrAllocator alloc_; static DrAllocator alloc_;
}; };
template <typename T> template <typename T>
typename DrObject<T>::DrAllocator DrObject<T>::alloc_; typename DrObject<T>::DrAllocator DrObject<T>::alloc_;

View File

@ -16,7 +16,6 @@
#include <drogon/DrObject.h> #include <drogon/DrObject.h>
#include <drogon/DrTemplateBase.h> #include <drogon/DrTemplateBase.h>
namespace drogon namespace drogon
{ {
template <typename T> template <typename T>

View File

@ -30,7 +30,6 @@
#include <drogon/HttpRequest.h> #include <drogon/HttpRequest.h>
#include <drogon/HttpResponse.h> #include <drogon/HttpResponse.h>
#include <drogon/orm/DbClient.h> #include <drogon/orm/DbClient.h>
#include <drogon/orm/DbConfig.h>
#include <drogon/nosql/RedisClient.h> #include <drogon/nosql/RedisClient.h>
#include <drogon/Cookie.h> #include <drogon/Cookie.h>
#include <trantor/net/Resolver.h> #include <trantor/net/Resolver.h>
@ -43,16 +42,6 @@
#include <vector> #include <vector>
#include <chrono> #include <chrono>
#if defined(__APPLE__) && defined(__MACH__) && \
(defined(__ENVIRONMENT_IPHONE_OS__) || \
defined(__IPHONE_OS_VERSION_MIN_REQUIRED))
// iOS
#define TARGET_OS_IOS 1
#else
// not iOS
#define TARGET_OS_IOS 0
#endif
namespace drogon namespace drogon
{ {
// the drogon banner // the drogon banner
@ -77,11 +66,8 @@ using ExceptionHandler =
using DefaultHandler = using DefaultHandler =
std::function<void(const HttpRequestPtr &, std::function<void(const HttpRequestPtr &,
std::function<void(const HttpResponsePtr &)> &&)>; std::function<void(const HttpResponsePtr &)> &&)>;
using HttpHandlerInfo = std::tuple<std::string, HttpMethod, std::string>;
#ifdef __cpp_impl_coroutine #ifdef __cpp_impl_coroutine
class HttpAppFramework; class HttpAppFramework;
namespace internal namespace internal
{ {
struct [[nodiscard]] ForwardAwaiter struct [[nodiscard]] ForwardAwaiter
@ -98,7 +84,6 @@ struct [[nodiscard]] ForwardAwaiter
app_(app) app_(app)
{ {
} }
void await_suspend(std::coroutine_handle<> handle) noexcept; void await_suspend(std::coroutine_handle<> handle) noexcept;
private: private:
@ -157,7 +142,7 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
*/ */
virtual trantor::EventLoop *getLoop() const = 0; virtual trantor::EventLoop *getLoop() const = 0;
/// Get an IO loop with id. E.g. 0 <= id < \#Total thread-loops /// Get an IO loop with id. E.g. 0 <= id < #Total thread-loops
/** /**
* @note * @note
* The event loop is one of the network IO loops. Use the loop * The event loop is one of the network IO loops. Use the loop
@ -184,19 +169,7 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
* be sent to the client to provide a custom layout. * be sent to the client to provide a custom layout.
*/ */
virtual HttpAppFramework &setCustomErrorHandler( virtual HttpAppFramework &setCustomErrorHandler(
std::function<HttpResponsePtr(HttpStatusCode, std::function<HttpResponsePtr(HttpStatusCode)> &&resp_generator) = 0;
const HttpRequestPtr &req)>
&&resp_generator) = 0;
HttpAppFramework &setCustomErrorHandler(
std::function<HttpResponsePtr(HttpStatusCode)> &&resp_generator)
{
return setCustomErrorHandler(
[cb = std::move(resp_generator)](HttpStatusCode code,
const HttpRequestPtr &) {
return cb(code);
});
}
/// Get custom error handler /// Get custom error handler
/** /**
@ -204,9 +177,8 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
* setCustomErrorHandler. If none was provided, the default error handler is * setCustomErrorHandler. If none was provided, the default error handler is
* returned. * returned.
*/ */
virtual const std::function<HttpResponsePtr(HttpStatusCode, virtual const std::function<HttpResponsePtr(HttpStatusCode)>
const HttpRequestPtr &req)> & &getCustomErrorHandler() const = 0;
getCustomErrorHandler() const = 0;
/// Get the plugin object registered in the framework /// Get the plugin object registered in the framework
/** /**
@ -333,7 +305,7 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
[Check Method]---------------->[405]----------->+ [Check Method]---------------->[405]----------->+
| | | |
v | v |
[Filters/Middlewares]------>[Filter callback]------>+ [Filters]------->[Filter callback]----------->+
| | | |
v Y | v Y |
[Is OPTIONS method?]------------->[200]----------->+ [Is OPTIONS method?]------------->[200]----------->+
@ -346,9 +318,6 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
| | | |
v | v |
Post-handling join point o---------------------------------------->+ Post-handling join point o---------------------------------------->+
| |
v |
[Middlewares post logic]--->[Middleware callback]--->+
@endcode @endcode
* *
@ -359,7 +328,7 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
/// Register an advice called before routing /// Register an advice called before routing
/** /**
* @param advice is called after all the synchronous advice return * @param advice is called after all the synchronous advices return
* nullptr and before the request is routed to any handler. The parameters * nullptr and before the request is routed to any handler. The parameters
* of the advice are same as those of the doFilter method of the Filter * of the advice are same as those of the doFilter method of the Filter
* class. * class.
@ -382,7 +351,7 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
/// Register an advice called after routing /// Register an advice called after routing
/** /**
* @param advice is called immediately after the request matches a handler * @param advice is called immediately after the request matches a handler
* path and before any filters/middlewares applies. The parameters * 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 * of the advice are same as those of the doFilter method of the Filter
* class. * class.
*/ */
@ -404,8 +373,8 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
/// Register an advice called before the request is handled /// Register an advice called before the request is handled
/** /**
* @param advice is called immediately after the request is approved by all * @param advice is called immediately after the request is approved by all
* filters/middlewares and before it is handled. The parameters of the * filters and before it is handled. The parameters of the advice are
* advice are same as those of the doFilter method of the Filter class. * same as those of the doFilter method of the Filter class.
*/ */
virtual HttpAppFramework &registerPreHandlingAdvice( virtual HttpAppFramework &registerPreHandlingAdvice(
const std::function<void(const HttpRequestPtr &, const std::function<void(const HttpRequestPtr &,
@ -455,14 +424,14 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
/// Load the configuration file with json format. /// Load the configuration file with json format.
/** /**
* @param fileName the configuration file * @param filename the configuration file
*/ */
virtual HttpAppFramework &loadConfigFile( virtual HttpAppFramework &loadConfigFile(
const std::string &fileName) noexcept(false) = 0; const std::string &fileName) noexcept(false) = 0;
/// Load the configuration from a Json::Value Object. /// Load the configuration from a Json::Value Object.
/** /**
* @param data Json::Value Object containing the configuration. * @param Json::Value Object containing the configuration.
* @note Please refer to the configuration file for the content of the json * @note Please refer to the configuration file for the content of the json
* object. * object.
*/ */
@ -471,7 +440,7 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
/// Load the configuration from a Json::Value Object. /// Load the configuration from a Json::Value Object.
/** /**
* @param data rvalue reference to a Json::Value object containing the * @param rvalue reference to a Json::Value object containing the
* configuration. * configuration.
* @note Please refer to the configuration file for the content of the json * @note Please refer to the configuration file for the content of the json
* object. * object.
@ -486,8 +455,8 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
* called. * called.
* @param ctrlName is the name of the controller. It includes the namespace * @param ctrlName is the name of the controller. It includes the namespace
* to which the controller belongs. * to which the controller belongs.
* @param constraints is a vector containing Http methods or middleware * @param filtersAndMethods is a vector containing Http methods or filter
names * name constraints.
* *
* Example: * Example:
* @code * @code
@ -501,7 +470,8 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
virtual HttpAppFramework &registerHttpSimpleController( virtual HttpAppFramework &registerHttpSimpleController(
const std::string &pathName, const std::string &pathName,
const std::string &ctrlName, const std::string &ctrlName,
const std::vector<internal::HttpConstraint> &constraints = {}) = 0; const std::vector<internal::HttpConstraint> &filtersAndMethods =
std::vector<internal::HttpConstraint>{}) = 0;
/// Register a handler into the framework. /// Register a handler into the framework.
/** /**
@ -509,7 +479,7 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
* pathPattern, the handler indicated by the function parameter is called. * pathPattern, the handler indicated by the function parameter is called.
* @param function indicates any type of callable object with a valid * @param function indicates any type of callable object with a valid
* processing interface. * processing interface.
* @param constraints is the same as the third parameter in the above * @param filtersAndMethods is the same as the third parameter in the above
* method. * method.
* *
* Example: * Example:
@ -535,7 +505,8 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
HttpAppFramework &registerHandler( HttpAppFramework &registerHandler(
const std::string &pathPattern, const std::string &pathPattern,
FUNCTION &&function, FUNCTION &&function,
const std::vector<internal::HttpConstraint> &constraints = {}, const std::vector<internal::HttpConstraint> &filtersAndMethods =
std::vector<internal::HttpConstraint>{},
const std::string &handlerName = "") const std::string &handlerName = "")
{ {
LOG_TRACE << "pathPattern:" << pathPattern; LOG_TRACE << "pathPattern:" << pathPattern;
@ -545,16 +516,17 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
getLoop()->queueInLoop([binder]() { binder->createHandlerInstance(); }); getLoop()->queueInLoop([binder]() { binder->createHandlerInstance(); });
std::vector<HttpMethod> validMethods; std::vector<HttpMethod> validMethods;
std::vector<std::string> middlewares; std::vector<std::string> filters;
for (auto const &constraint : constraints) for (auto const &filterOrMethod : filtersAndMethods)
{ {
if (constraint.type() == internal::ConstraintType::HttpMiddleware) if (filterOrMethod.type() == internal::ConstraintType::HttpFilter)
{ {
middlewares.push_back(constraint.getMiddlewareName()); filters.push_back(filterOrMethod.getFilterName());
} }
else if (constraint.type() == internal::ConstraintType::HttpMethod) else if (filterOrMethod.type() ==
internal::ConstraintType::HttpMethod)
{ {
validMethods.push_back(constraint.getHttpMethod()); validMethods.push_back(filterOrMethod.getHttpMethod());
} }
else else
{ {
@ -563,10 +535,9 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
} }
} }
registerHttpController( registerHttpController(
pathPattern, binder, validMethods, middlewares, handlerName); pathPattern, binder, validMethods, filters, handlerName);
return *this; return *this;
} }
/** /**
* @brief Register a handler into the framework via a regular expression. * @brief Register a handler into the framework via a regular expression.
* *
@ -577,8 +548,8 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
* subexpression is sequentially mapped to a handler parameter. * subexpression is sequentially mapped to a handler parameter.
* @param function indicates any type of callable object with a valid * @param function indicates any type of callable object with a valid
* processing interface. * processing interface.
* @param constraints is the same as the third parameter in the * @param filtersAndMethods is the same as the third parameter in the above
* above method. * method.
* @param handlerName a name for the handler. * @param handlerName a name for the handler.
* @return HttpAppFramework& * @return HttpAppFramework&
*/ */
@ -586,7 +557,8 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
HttpAppFramework &registerHandlerViaRegex( HttpAppFramework &registerHandlerViaRegex(
const std::string &regExp, const std::string &regExp,
FUNCTION &&function, FUNCTION &&function,
const std::vector<internal::HttpConstraint> &constraints = {}, const std::vector<internal::HttpConstraint> &filtersAndMethods =
std::vector<internal::HttpConstraint>{},
const std::string &handlerName = "") const std::string &handlerName = "")
{ {
LOG_TRACE << "regex:" << regExp; LOG_TRACE << "regex:" << regExp;
@ -596,16 +568,17 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
std::forward<FUNCTION>(function)); std::forward<FUNCTION>(function));
std::vector<HttpMethod> validMethods; std::vector<HttpMethod> validMethods;
std::vector<std::string> middlewares; std::vector<std::string> filters;
for (auto const &constraint : constraints) for (auto const &filterOrMethod : filtersAndMethods)
{ {
if (constraint.type() == internal::ConstraintType::HttpMiddleware) if (filterOrMethod.type() == internal::ConstraintType::HttpFilter)
{ {
middlewares.push_back(constraint.getMiddlewareName()); filters.push_back(filterOrMethod.getFilterName());
} }
else if (constraint.type() == internal::ConstraintType::HttpMethod) else if (filterOrMethod.type() ==
internal::ConstraintType::HttpMethod)
{ {
validMethods.push_back(constraint.getHttpMethod()); validMethods.push_back(filterOrMethod.getHttpMethod());
} }
else else
{ {
@ -614,7 +587,7 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
} }
} }
registerHttpControllerViaRegex( registerHttpControllerViaRegex(
regExp, binder, validMethods, middlewares, handlerName); regExp, binder, validMethods, filters, handlerName);
return *this; return *this;
} }
@ -626,18 +599,7 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
virtual HttpAppFramework &registerWebSocketController( virtual HttpAppFramework &registerWebSocketController(
const std::string &pathName, const std::string &pathName,
const std::string &ctrlName, const std::string &ctrlName,
const std::vector<internal::HttpConstraint> &constraints = {}) = 0; const std::vector<internal::HttpConstraint> &filtersAndMethods =
/// Register a WebSocketController into the framework.
/**
* The parameters of this method are the same as those in the
* registerHttpSimpleController() method but using regular
* expression string for path.
*/
virtual HttpAppFramework &registerWebSocketControllerRegex(
const std::string &regExp,
const std::string &ctrlName,
const std::vector<internal::HttpConstraint> &constraints =
std::vector<internal::HttpConstraint>{}) = 0; std::vector<internal::HttpConstraint>{}) = 0;
/// Register controller objects created and initialized by the user /// Register controller objects created and initialized by the user
@ -701,31 +663,13 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
return *this; return *this;
} }
/// Register middleware objects created and initialized by the user
/**
* This method is similar to the above method.
*/
template <typename T>
HttpAppFramework &registerMiddleware(
const std::shared_ptr<T> &middlewarePtr)
{
static_assert(std::is_base_of<HttpMiddlewareBase, T>::value,
"Error! Only middleware objects can be registered here");
static_assert(!T::isAutoCreation,
"Middleware created and initialized "
"automatically by drogon cannot be "
"registered here");
DrClassMap::setSingleInstance(middlewarePtr);
return *this;
}
/// Register a default handler into the framework when no handler matches /// Register a default handler into the framework when no handler matches
/// the request. If set, it is executed if the static file router does /// the request. If set, it is executed if the static file router does
/// not find any file corresponding to the request. Thus it replaces /// not find any file corresponding to the request. Thus it replaces
/// the default 404 not found response. /// the default 404 not found response.
/** /**
* @param handler function indicates any type of callable object with * @param function indicates any type of callable object with a valid
* a valid processing interface. * processing interface.
*/ */
virtual HttpAppFramework &setDefaultHandler(DefaultHandler handler) = 0; virtual HttpAppFramework &setDefaultHandler(DefaultHandler handler) = 0;
@ -784,7 +728,8 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
* pattern of the handler; * pattern of the handler;
* The last item in std::tuple is the description of the handler. * The last item in std::tuple is the description of the handler.
*/ */
virtual std::vector<HttpHandlerInfo> getHandlersInfo() const = 0; virtual std::vector<std::tuple<std::string, HttpMethod, std::string>>
getHandlersInfo() const = 0;
/// Get the custom configuration defined by users in the configuration file. /// Get the custom configuration defined by users in the configuration file.
virtual const Json::Value &getCustomConfig() const = 0; virtual const Json::Value &getCustomConfig() const = 0;
@ -816,15 +761,6 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
const std::vector<std::pair<std::string, std::string>> const std::vector<std::pair<std::string, std::string>>
&sslConfCmds) = 0; &sslConfCmds) = 0;
/// Reload the global cert file and private key file for https server
/// Note: The goal of this method is not to make the framework
/// use the new SSL path, but rather to reload the new content
/// from the old path while the framework is still running.
/// Typically, when our SSL is about to expire,
/// we need to reload the SSL. The purpose of this function
/// is to use the new SSL certificate without stopping the framework.
virtual HttpAppFramework &reloadSSLFiles() = 0;
/// Add plugins /// Add plugins
/** /**
* @param configs The plugins array * @param configs The plugins array
@ -854,9 +790,8 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
* @param keyFile specify the cert file and the private key file for this * @param keyFile specify the cert file and the private key file for this
* listener. If they are empty, the global configuration set by the above * listener. If they are empty, the global configuration set by the above
* method is used. * method is used.
* @param useOldTLS if true, the TLS1.0/1.1 are enabled for HTTPS * @param useOldTLS If true, the TLS1.0/1.1 are enabled for HTTPS
* connections. * connections.
* @param sslConfCmds vector of ssl configuration key/value pairs.
* *
* @note * @note
* This operation can be performed by an option in the configuration file. * This operation can be performed by an option in the configuration file.
@ -875,7 +810,6 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
/** /**
* @param timeout The number of seconds which is the timeout of a session * @param timeout The number of seconds which is the timeout of a session
* @param sameSite The default value of SameSite attribute * @param sameSite The default value of SameSite attribute
* @param cookieKey The key of the session cookie
* *
* @note * @note
* Session support is disabled by default. * Session support is disabled by default.
@ -886,10 +820,7 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
*/ */
virtual HttpAppFramework &enableSession( virtual HttpAppFramework &enableSession(
const size_t timeout = 0, const size_t timeout = 0,
Cookie::SameSite sameSite = Cookie::SameSite::kNull, Cookie::SameSite sameSite = Cookie::SameSite::kNull) = 0;
const std::string &cookieKey = "JSESSIONID",
int maxAge = -1,
std::function<std::string()> idGeneratorCallback = nullptr) = 0;
/// A wrapper of the above method. /// A wrapper of the above method.
/** /**
@ -901,16 +832,9 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
*/ */
inline HttpAppFramework &enableSession( inline HttpAppFramework &enableSession(
const std::chrono::duration<double> &timeout, const std::chrono::duration<double> &timeout,
Cookie::SameSite sameSite = Cookie::SameSite::kNull, Cookie::SameSite sameSite = Cookie::SameSite::kNull)
const std::string &cookieKey = "JSESSIONID",
int maxAge = -1,
std::function<std::string()> idGeneratorCallback = nullptr)
{ {
return enableSession((size_t)timeout.count(), return enableSession((size_t)timeout.count(), sameSite);
sameSite,
cookieKey,
maxAge,
idGeneratorCallback);
} }
/// Register an advice called when starting a new session. /// Register an advice called when starting a new session.
@ -966,8 +890,7 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
* extension can be accessed. * extension can be accessed.
* @param isRecursive If it is set to false, files in sub directories can't * @param isRecursive If it is set to false, files in sub directories can't
* be accessed. * be accessed.
* @param middlewareNames The list of middlewares which acting on the * @param filters The list of filters which acting on the location.
* location.
* @return HttpAppFramework& * @return HttpAppFramework&
*/ */
virtual HttpAppFramework &addALocation( virtual HttpAppFramework &addALocation(
@ -977,7 +900,7 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
bool isCaseSensitive = false, bool isCaseSensitive = false,
bool allowAll = true, bool allowAll = true,
bool isRecursive = true, bool isRecursive = true,
const std::vector<std::string> &middlewareNames = {}) = 0; const std::vector<std::string> &filters = {}) = 0;
/// Set the path to store uploaded files. /// Set the path to store uploaded files.
/** /**
@ -1007,21 +930,21 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
virtual HttpAppFramework &setFileTypes( virtual HttpAppFramework &setFileTypes(
const std::vector<std::string> &types) = 0; const std::vector<std::string> &types) = 0;
#if !defined(_WIN32) && !TARGET_OS_IOS
/// Enable supporting for dynamic views loading. /// Enable supporting for dynamic views loading.
/** /**
* *
* @param libPaths is a vector 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. * @param outputPath is the directory where the output source files locate. if
* If it is set to an empty string, drogon use libPaths as output paths. If * it is set to an empty string, drogon use libPaths as output paths. If the
* the path isn't prefixed with /, it is the relative path of the current * path isn't prefixed with /, it is relative path of the current working
* working directory. * directory.
* *
* @note * @note
* It is disabled by default. * It is disabled by default.
* This operation can be performed by an option in the configuration file. * This operation can be performed by an option in the configuration file.
*/ */
#ifndef _WIN32
virtual HttpAppFramework &enableDynamicViewsLoading( virtual HttpAppFramework &enableDynamicViewsLoading(
const std::vector<std::string> &libPaths, const std::vector<std::string> &libPaths,
const std::string &outputPath = "") = 0; const std::string &outputPath = "") = 0;
@ -1074,14 +997,11 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
*/ */
virtual HttpAppFramework &enableRelaunchOnError() = 0; virtual HttpAppFramework &enableRelaunchOnError() = 0;
/// Set the output path of logs.
/** /**
* @brief Set the output path of logs. * @param logPath The path to logs.
* @param logPath The path to logs - logs to console if empty. * @param logfileBaseName The base name of log files.
* @param logfileBaseName The base name of log files - defaults to "drogon"
* if empty.
* @param logSize indicates the maximum size of a log file. * @param logSize indicates the maximum size of a log file.
* @param maxFiles max count of log file - 0 = unlimited.
* @param useSpdlog Use spdlog for logging (if compiled-in).
* *
* @note * @note
* This operation can be performed by an option in the configuration file. * This operation can be performed by an option in the configuration file.
@ -1090,11 +1010,10 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
const std::string &logPath, const std::string &logPath,
const std::string &logfileBaseName = "", const std::string &logfileBaseName = "",
size_t logSize = 100000000, size_t logSize = 100000000,
size_t maxFiles = 0, size_t maxFiles = 0) = 0;
bool useSpdlog = false) = 0;
/// Set the log level
/** /**
* @brief Set the log level.
* @param level is one of TRACE, DEBUG, INFO, WARN. The Default value is * @param level is one of TRACE, DEBUG, INFO, WARN. The Default value is
* DEBUG. * DEBUG.
* *
@ -1437,7 +1356,7 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
*/ */
virtual size_t getJsonParserStackLimit() const noexcept = 0; virtual size_t getJsonParserStackLimit() const noexcept = 0;
/** /**
* @brief This method is to enable or disable the unicode escaping (\\u) in * @brief This method is to enable or disable the unicode escaping (\u) in
* the json string of HTTP responses or requests. it works (disable * the json string of HTTP responses or requests. it works (disable
* successfully) when the version of JsonCpp >= 1.9.3, the unicode escaping * successfully) when the version of JsonCpp >= 1.9.3, the unicode escaping
* is enabled by default. * is enabled by default.
@ -1469,19 +1388,19 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
* *
* @return std::pair<size_t, std::string> * @return std::pair<size_t, std::string>
*/ */
virtual const std::pair<unsigned int, std::string> & virtual const std::pair<unsigned int, std::string>
getFloatPrecisionInJson() const noexcept = 0; &getFloatPrecisionInJson() const noexcept = 0;
/// Create a database client /// Create a database client
/** /**
* @param dbType The database type is one of * @param dbType The database type is one of
* "postgresql","mysql","sqlite3". * "postgresql","mysql","sqlite3".
* @param host IP or host name. * @param host IP or host name.
* @param port The port on which the database server is listening. * @param port The port on which the database server is listening.
* @param databaseName Database name * @databaseName Database name
* @param userName User name * @param userName User name
* @param password Password for the database server * @param password Password for the database server
* @param connectionNum The number of connections to the database server. * @param connectionNum The number of connections to the database server.
* It's valid only if @p isFast is false. * It's valid only if @param isFast is false.
* @param filename The file name of sqlite3 database file. * @param filename The file name of sqlite3 database file.
* @param name The client name. * @param name The client name.
* @param isFast Indicates if the client is a fast database client. * @param isFast Indicates if the client is a fast database client.
@ -1492,22 +1411,20 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
* @note * @note
* This operation can be performed by an option in the configuration file. * This operation can be performed by an option in the configuration file.
*/ */
[[deprecated("Use addDbClient() instead")]] virtual HttpAppFramework & virtual HttpAppFramework &createDbClient(
createDbClient(const std::string &dbType, const std::string &dbType,
const std::string &host, const std::string &host,
unsigned short port, const unsigned short port,
const std::string &databaseName, const std::string &databaseName,
const std::string &userName, const std::string &userName,
const std::string &password, const std::string &password,
size_t connectionNum = 1, const size_t connectionNum = 1,
const std::string &filename = "", const std::string &filename = "",
const std::string &name = "default", const std::string &name = "default",
bool isFast = false, const bool isFast = false,
const std::string &characterSet = "", const std::string &characterSet = "",
double timeout = -1.0, double timeout = -1.0,
bool autoBatch = false) = 0; const bool autoBatch = false) = 0;
virtual HttpAppFramework &addDbClient(const orm::DbConfig &config) = 0;
/// Create a redis client /// Create a redis client
/** /**
@ -1588,7 +1505,7 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
/** /**
* @brief handler will be called upon an exception escapes a request handler * @brief handler will be called upon an exception escapes a request handler
*/ */
virtual HttpAppFramework &setExceptionHandler(ExceptionHandler handler) = 0; virtual void setExceptionHandler(ExceptionHandler handler) = 0;
/** /**
* @brief returns the excaption handler * @brief returns the excaption handler
@ -1609,46 +1526,18 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
*/ */
virtual int64_t getConnectionCount() const = 0; virtual int64_t getConnectionCount() const = 0;
/**
* @brief Set the before listen setsockopt callback.
*
* @param cb This callback will be called before the listen
*/
virtual HttpAppFramework &setBeforeListenSockOptCallback(
std::function<void(int)> cb) = 0;
/**
* @brief Set the after accept setsockopt callback.
*
* @param cb This callback will be called after accept
*/
virtual HttpAppFramework &setAfterAcceptSockOptCallback(
std::function<void(int)> cb) = 0;
/**
* @brief Set the client disconnect or connect callback.
*
* @param cb This callback will be called, when the client disconnect or
* connect
*/
virtual HttpAppFramework &setConnectionCallback(
std::function<void(const trantor::TcpConnectionPtr &)> cb) = 0;
virtual HttpAppFramework &enableRequestStream(bool enable = true) = 0;
virtual bool isRequestStreamEnabled() const = 0;
private: private:
virtual void registerHttpController( virtual void registerHttpController(
const std::string &pathPattern, const std::string &pathPattern,
const internal::HttpBinderBasePtr &binder, const internal::HttpBinderBasePtr &binder,
const std::vector<HttpMethod> &validMethods = {}, const std::vector<HttpMethod> &validMethods = std::vector<HttpMethod>(),
const std::vector<std::string> &middlewareNames = {}, const std::vector<std::string> &filters = std::vector<std::string>(),
const std::string &handlerName = "") = 0; const std::string &handlerName = "") = 0;
virtual void registerHttpControllerViaRegex( virtual void registerHttpControllerViaRegex(
const std::string &regExp, const std::string &regExp,
const internal::HttpBinderBasePtr &binder, const internal::HttpBinderBasePtr &binder,
const std::vector<HttpMethod> &validMethods, const std::vector<HttpMethod> &validMethods,
const std::vector<std::string> &middlewareNames, const std::vector<std::string> &filters,
const std::string &handlerName) = 0; const std::string &handlerName) = 0;
}; };

Some files were not shown because too many files have changed in this diff Show More