Compare commits

..

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

499 changed files with 11638 additions and 48133 deletions

41
.appveyor.yml Normal file
View File

@ -0,0 +1,41 @@
version: 1.6.0.{build}
configuration:
- Release
- Debug
environment:
matrix:
- GENERATOR: Visual Studio 15 2017
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
ARCH: Win32
- GENERATOR: Visual Studio 15 2017
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
ARCH: x64
init:
- cmake --version
install:
- cd C:\Tools\vcpkg
- git pull
- .\bootstrap-vcpkg.bat
- vcpkg install jsoncpp:x86-windows
- vcpkg install jsoncpp:x64-windows
- vcpkg install zlib:x86-windows
- vcpkg install zlib:x64-windows
- vcpkg install libpq:x86-windows
- vcpkg install libpq:x64-windows
- vcpkg integrate install
- cd %APPVEYOR_BUILD_FOLDER%
before_build:
- git submodule update --init
- md build
- cd build
- cmake --config "%CONFIGURATION%" -G "%GENERATOR%" -DCMAKE_TOOLCHAIN_FILE=c:/tools/vcpkg/scripts/buildsystems/vcpkg.cmake ..
build_script:
- cmake --build . --config "%CONFIGURATION%"
build:
project: build\ALL_BUILD.vcxproj
verbosity: minimal
cache:
- c:\tools\vcpkg\installed\

View File

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

View File

@ -1,17 +1,10 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
labels:
---
**Notice**
If you need support or clarification regarding the usage of Drogon in your project, visit the official Drogon support channel at [gitter](https://gitter.im/drogon-web/community)
Please create a new issue only if you think you have found a bug or if have a feature request/enhancement.
**Describe the bug**
A clear and concise description of what the bug is.

View File

@ -1,17 +1,10 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
labels:
---
**Notice**
If you need support or clarification regarding the usage of Drogon in your project, visit the official Drogon support channel at [gitter](https://gitter.im/drogon-web/community)
Please create a new issue only if you think you have found a bug or if have a feature request/enhancement.
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

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:
push:
@ -6,232 +6,204 @@ on:
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: Release
jobs:
windows:
name: windows/msvc - ${{ matrix.link }}
runs-on: windows-2022
name: 'windows/msvc - ${{matrix.link}}'
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
link: ["STATIC", "SHARED"]
link: [ 'STATIC', 'SHARED' ]
steps:
- name: Checkout Drogon source code
uses: actions/checkout@v4
uses: actions/checkout@v2
with:
submodules: true
fetch-depth: 0
- name: Install dependencies
run: pip install conan
run: |
pip install conan
- name: Create build directory
run: mkdir build
run: |
mkdir build
- name: Install conan packages
shell: pwsh
working-directory: ./build
run: |
conan profile detect
conan install .. -s compiler="msvc" -sbuild_type=Debug --build=missing -s compiler.cppstd=17
conan install .. -s compiler="Visual Studio" -s compiler.version=16 -sbuild_type=Debug -g cmake_paths
- name: Create Build Environment & Configure Cmake
shell: bash
working-directory: ./build
run: |
[[ ${{ matrix.link }} == "SHARED" ]] && shared="ON" || shared="OFF"
cmake .. \
-DCMAKE_BUILD_TYPE=Debug \
-DBUILD_TESTING=on \
-DBUILD_SHARED_LIBS=$shared \
-DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" \
-DBUILD_CTL=ON \
-DBUILD_EXAMPLES=ON \
-DUSE_SPDLOG=ON \
-DCMAKE_INSTALL_PREFIX=../install \
-DCMAKE_POLICY_DEFAULT_CMP0091=NEW \
-DCMAKE_CXX_STANDARD=17
cmake .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=on -DBUILD_DROGON_SHARED=$shared -DCMAKE_TOOLCHAIN_FILE="conan_paths.cmake" -DBUILD_CTL=ON -DBUILD_EXAMPLES=ON -DCMAKE_INSTALL_PREFIX=../install
- name: Build
run: cmake --build build --target install --parallel
working-directory: ${{env.GITHUB_WORKSPACE}}
shell: bash
run: |
cd build
cmake --build . --target install --parallel
- name: Test
working-directory: ${{env.GITHUB_WORKSPACE}}
shell: bash
run: ./test.sh -w
macos:
runs-on: macos-${{ matrix.osver }}
unix:
name: ${{matrix.buildname}}
runs-on: ${{matrix.os}}
strategy:
fail-fast: false
matrix:
osver: [13, 14, 15]
steps:
- name: Checkout Drogon source code
uses: actions/checkout@v4
with:
submodules: true
fetch-depth: 0
- name: Install dependencies
# Already installed: brotli, zlib, lz4, sqlite3
run: brew install ninja jsoncpp mariadb hiredis redis spdlog postgresql@14
- 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
run: |
cmake -B build -G Ninja \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DBUILD_TESTING=on \
-DUSE_SPDLOG=ON \
-DBUILD_SHARED_LIBS=OFF
- 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: |
brew services restart postgresql@14
brew services start mariadb
brew services start redis
sleep 4
mariadb -e "SET PASSWORD FOR 'root'@'localhost' = PASSWORD('')"
mariadb -e "GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost'"
mariadb -e "FLUSH PRIVILEGES"
brew services restart mariadb
sleep 4
psql -c 'create user postgres superuser;' postgres
- name: Test
# Execute tests defined by the CMake configuration.
# 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 }}
- 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:
- name: Checkout Drogon source code
uses: actions/checkout@v4
with:
submodules: true
fetch-depth: 0
- name: Checkout Drogon source code
uses: actions/checkout@v2
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: (macOS) Install dependencies
if: runner.os == 'macOS'
run: |
brew install jsoncpp ossp-uuid brotli zlib
brew install openssl lz4 mariadb sqlite3 postgresql hiredis
brew install redis
- 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: (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 install libjsoncpp-dev uuid-dev openssl libssl-dev zlib1g-dev libsqlite3-dev
sudo apt install libbrotli-dev
- name: (Linux) Install gcc-10
if: matrix.buildname == 'ubuntu-20.04/gcc-10'
run: |
sudo apt install gcc-10 g++-10
- name: (Linux) Install boost
if: matrix.buildname == 'ubuntu-20.04/c++14'
run: |
sudo apt update
sudo apt install libboost-all-dev
- 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: (Linux) Install postgresql
if: matrix.os == 'ubuntu-20.04'
run: |
sudo apt install postgresql-all
- name: Install Clang
if: startsWith(matrix.compiler.cxx, 'clang') && matrix.compiler.ver < 13
run: sudo apt-get install clang-${{ matrix.compiler.ver }}
- name: Create build directory
run: |
mkdir build
- 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: 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
shell: bash
working-directory: ./build
if: matrix.buildname != 'ubuntu-20.04/gcc-10' && matrix.buildname != 'ubuntu-20.04/c++14'
run: |
[[ ${{ matrix.link }} == "SHARED" ]] && shared="ON" || shared="OFF"
cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_TESTING=on -DBUILD_DROGON_SHARED=$shared
- name: Export `shared`
run: |
[[ ${{ matrix.link }} == "SHARED" ]] && shared="ON" || shared="OFF"
echo "shared=$shared" >> $GITHUB_ENV
- 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
shell: bash
working-directory: ./build
if: matrix.buildname == 'ubuntu-20.04/gcc-10'
run: |
[[ ${{ matrix.link }} == "SHARED" ]] && shared="ON" || shared="OFF"
cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_TESTING=on -DCMAKE_CXX_FLAGS="-fcoroutines" -DBUILD_DROGON_SHARED=$shared
env:
CC: gcc-10
CXX: g++-10
- 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: 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
shell: bash
working-directory: ./build
if: matrix.buildname == 'ubuntu-20.04/C++14'
run: |
[[ ${{ matrix.link }} == "SHARED" ]] && shared="ON" || shared="OFF"
cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_TESTING=on -DCMAKE_CXX_VERSION=14 -DBUILD_DROGON_SHARED=$shared
- name: Build
working-directory: ./build
# Execute the build. You can specify a specific target with "--target <NAME>"
run: ninja && sudo ninja install
- name: (Linux) Build
working-directory: ./build
shell: bash
# Execute the build. You can specify a specific target with "--target <NAME>"
run: |
sudo make -j $(nproc) && sudo make install
- name: Prepare for testing
run: |
sudo systemctl start postgresql
sleep 1
sudo -u postgres psql -c "ALTER USER postgres WITH PASSWORD '12345'" postgres
- name: Prepare for testing (macOS)
if: runner.os == 'macOS'
run: |
cd $(brew --prefix)/Homebrew/Library/Taps/homebrew/homebrew-services
git reset --hard 5f2fe01
cd ~
brew tap homebrew/services;
brew services restart postgresql;
brew services start mariadb;
brew services start redis;
sleep 4;
mariadb -e "SET PASSWORD FOR 'root'@'localhost' = PASSWORD('')";
mariadb -e "GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost'";
mariadb -e "FLUSH PRIVILEGES";
brew services restart mariadb;
sleep 4;
psql -c 'create user postgres superuser;' postgres;
- name: Test
# Execute tests defined by the CMake configuration.
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
run: ./test.sh -t
- name: Prepare for testing (Linux)
if: runner.os == 'Linux'
run: |
sudo systemctl start postgresql
sleep 1
sudo -u postgres psql -c "ALTER USER postgres WITH PASSWORD '12345'" postgres
- name: Test
working-directory: ${{env.GITHUB_WORKSPACE}}
shell: bash
# Execute tests defined by the CMake configuration.
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
run: ./test.sh -t
- name: Lint
if: matrix.os == 'ubuntu-20.04'
working-directory: ${{env.GITHUB_WORKSPACE}}
shell: bash
run: ./format.sh && git diff --exit-code

View File

@ -1,80 +0,0 @@
name: "CodeQL"
on:
push:
branches: [ 'master' ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ 'master' ]
schedule:
- cron: '46 7 * * 5'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: Release
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'cpp' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Use only 'java' to analyze code written in Java, Kotlin or both
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
env:
SHARED: ON
steps:
- name: Checkout Drogon source code
uses: actions/checkout@v4
with:
submodules: true
fetch-depth: 0
- name: Install dependencies
run: |
sudo apt update
sudo apt-get install -y libjsoncpp-dev uuid-dev libssl-dev zlib1g-dev libsqlite3-dev
sudo apt-get install -y ninja-build libbrotli-dev
- name: Create Build Environment & Configure Cmake
run: |
cmake -B build -G Ninja \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DBUILD_TESTING=on \
-DBUILD_SHARED_LIBS=$SHARED
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
- name: Build
working-directory: ./build
run: ninja && sudo ninja install
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
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

2
.gitignore vendored
View File

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

2
.gitmodules vendored
View File

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

80
.travis.yml Normal file
View File

@ -0,0 +1,80 @@
matrix:
include:
- os: linux
dist: xenial
- os: osx
osx_image: xcode12.2
sudo: required
language: cpp
addons:
apt:
sources:
- xenial
- sourceline: 'deb http://archive.ubuntu.com/ubuntu xenial main'
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- gcc
- g++
- libjsoncpp-dev
- uuid-dev
- zlib1g-dev
- postgresql-server-dev-10
- openssl
- libssl-dev
- libsqlite3-dev
- mariadb-client
- mariadb-server
- build-essential
- cmake
- boost1.67
- libbrotli-dev
homebrew:
packages:
- jsoncpp
- ossp-uuid
- openssl
- cmake
- libtool
- lz4
- mariadb
- sqlite3
update: true
mariadb: '10.0'
before_install:
- wget https://github.com/google/googletest/archive/release-1.10.0.tar.gz
- tar xf release-1.10.0.tar.gz
- cd googletest-release-1.10.0
- cmake .
- make
- sudo make install
- cd -
before_script:
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then
brew tap homebrew/services;
brew services restart postgresql;
brew services start mariadb;
sleep 4;
mariadb -e "CREATE USER 'root'@'localhost' IDENTIFIED BY ''";
mariadb -e "SET PASSWORD FOR 'root'@'localhost' = PASSWORD('')";
mariadb -e "GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost'";
mariadb -e "FLUSH PRIVILEGES";
brew services restart mariadb;
sleep 4;
psql -c 'create user postgres superuser;' postgres;
fi
services:
- postgresql
- mysql
script:
- ./build.sh -t
- ./test.sh -t
- sudo rm /usr/local/lib/libtrantor.a && sudo rm /usr/local/lib/libdrogon.a
- ./build.sh -tshared
- ./test.sh -t

380
CMakeLists.txt Normal file → Executable file
View File

@ -1,19 +1,24 @@
cmake_minimum_required(VERSION 3.5...3.31)
cmake_minimum_required(VERSION 3.5)
project(drogon)
message(STATUS "compiler: " ${CMAKE_CXX_COMPILER_ID})
option(BUILD_CTL "Build drogon_ctl" ON)
option(BUILD_EXAMPLES "Build examples" ON)
# If your cross compile is failing, you should set
# CMAKE_SYSTEM_NAME in your toolchain file
if (NOT CMAKE_CROSSCOMPILING)
set(BUILD_PROGRAMS ON)
else ()
set(BUILD_PROGRAMS OFF)
endif ()
option(BUILD_CTL "Build drogon_ctl" ${BUILD_PROGRAMS})
option(BUILD_EXAMPLES "Build examples" ${BUILD_PROGRAMS})
option(BUILD_ORM "Build orm" ON)
option(COZ_PROFILING "Use coz for profiling" OFF)
option(BUILD_SHARED_LIBS "Build drogon as a shared lib" OFF)
option(BUILD_DROGON_SHARED "Build drogon as a shared lib" OFF)
option(BUILD_DOC "Build Doxygen documentation" OFF)
option(BUILD_BROTLI "Build Brotli" ON)
option(BUILD_YAML_CONFIG "Build yaml config" ON)
option(USE_SUBMODULE "Use trantor as a submodule" ON)
option(USE_STATIC_LIBS_ONLY "Use only static libraries as dependencies" OFF)
include(CMakeDependentOption)
CMAKE_DEPENDENT_OPTION(BUILD_POSTGRESQL "Build with postgresql support" ON "BUILD_ORM" OFF)
@ -21,11 +26,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_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(USE_SPDLOG "Allow using the spdlog logging library" OFF "USE_SUBMODULE" OFF)
set(DROGON_MAJOR_VERSION 1)
set(DROGON_MINOR_VERSION 9)
set(DROGON_PATCH_VERSION 11)
set(DROGON_MINOR_VERSION 7)
set(DROGON_PATCH_VERSION 3)
set(DROGON_VERSION
${DROGON_MAJOR_VERSION}.${DROGON_MINOR_VERSION}.${DROGON_PATCH_VERSION})
set(DROGON_VERSION_STRING "${DROGON_VERSION}")
@ -40,21 +44,16 @@ set(INSTALL_DROGON_CMAKE_DIR ${DEF_INSTALL_DROGON_CMAKE_DIR}
CACHE PATH "Installation directory for cmake files")
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
# 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.
message(STATUS "You are using MSVC. Forcing to use UTF-8")
add_compile_options("$<$<C_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()
# 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.
message(STATUS "You are using MSVC. Forceing to use UTF-8")
add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
endif ()
add_library(${PROJECT_NAME})
if (BUILD_SHARED_LIBS)
if (BUILD_DROGON_SHARED)
set(BUILD_TRANTOR_SHARED TRUE)
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
find_package(Threads)
# set(BUILD_EXAMPLES FALSE)
list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES
@ -62,12 +61,13 @@ if (BUILD_SHARED_LIBS)
if ("${isSystemDir}" STREQUAL "-1")
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR}")
endif ("${isSystemDir}" STREQUAL "-1")
add_library(${PROJECT_NAME} SHARED)
set_target_properties(${PROJECT_NAME} PROPERTIES
VERSION ${DROGON_VERSION}
SOVERSION ${DROGON_MAJOR_VERSION})
target_link_libraries(${PROJECT_NAME} PUBLIC Threads::Threads)
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)
# Ignore MSVC C4251 and C4275 warning of exporting std objects with no dll export
# We export class to facilitate maintenance, thus if you compile
@ -76,20 +76,9 @@ if (BUILD_SHARED_LIBS)
target_compile_options(${PROJECT_NAME} PUBLIC /wd4251 /wd4275)
endif ()
endif ()
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)
else (BUILD_DROGON_SHARED)
add_library(${PROJECT_NAME} STATIC)
endif (BUILD_DROGON_SHARED)
if (NOT ${CMAKE_PLATFORM_NAME} STREQUAL "Windows" AND CMAKE_CXX_COMPILER_ID MATCHES GNU)
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Werror)
@ -109,22 +98,19 @@ if (NOT "${CMAKE_CXX_STANDARD}" STREQUAL "")
set(DROGON_CXX_STANDARD ${CMAKE_CXX_STANDARD})
elseif (HAS_ANY AND HAS_STRING_VIEW AND HAS_COROUTINE)
set(DROGON_CXX_STANDARD 20)
else ()
elseif (HAS_ANY AND HAS_STRING_VIEW)
set(DROGON_CXX_STANDARD 17)
else ()
set(DROGON_CXX_STANDARD 14)
endif ()
if(USE_SUBMODULE)
target_include_directories(
${PROJECT_NAME}
PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/trantor>)
endif()
target_include_directories(
${PROJECT_NAME}
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}/nosql_lib/redis/inc>
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/trantor>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/exports>
$<INSTALL_INTERFACE:${INSTALL_INCLUDE_DIR}>)
@ -134,20 +120,16 @@ if (WIN32)
PRIVATE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/third_party/mman-win32>)
endif (WIN32)
if(USE_SUBMODULE)
add_subdirectory(trantor)
target_link_libraries(${PROJECT_NAME} PUBLIC trantor)
else()
find_package(Trantor CONFIG REQUIRED)
target_link_libraries(${PROJECT_NAME} PUBLIC Trantor::Trantor)
endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "Haiku")
target_link_libraries(${PROJECT_NAME} PRIVATE network)
elseif (NOT WIN32 AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
target_link_libraries(${PROJECT_NAME} PRIVATE dl)
elseif (WIN32)
target_link_libraries(${PROJECT_NAME} PRIVATE shlwapi ws2_32 iphlpapi)
target_link_libraries(${PROJECT_NAME} PRIVATE shlwapi)
endif ()
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules/)
@ -155,67 +137,68 @@ list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules/)
find_package(Filesystem COMPONENTS Final)
if(CXX_FILESYSTEM_HAVE_FS)
message(STATUS "Found std::filesystem")
else ()
message(FATAL_ERROR "Not found std::filesystem, please use a newer compiler")
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")
# Check for partial implementation of c++17 (Windows/OSX only?)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
try_compile(check_filesystem_path ${CMAKE_BINARY_DIR}/cmaketest
${PROJECT_SOURCE_DIR}/cmake/tests/check_has_std_filesystem_path.cc
CXX_STANDARD 17)
set(CMAKE_TRY_COMPILE_TARGET_TYPE)
if (NOT check_filesystem_path)
message(FATAL_ERROR "The std::filesystem seems to be a partial implementation,"
" please use a newer compiler")
if (CXX_FILESYSTEM_HAVE_FS)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
try_compile(check_filesystem_path ${CMAKE_BINARY_DIR}/cmaketest
${PROJECT_SOURCE_DIR}/cmake/tests/check_has_std_filesystem_path.cc
CXX_STANDARD 17)
set(CMAKE_TRY_COMPILE_TARGET_TYPE)
if (NOT check_filesystem_path)
message(STATUS "The std::filesystem seems to be a partial implementation"
" Falling back to boost::filesystem")
set(NEED_BOOST_FS 1)
endif()
else()
set(NEED_BOOST_FS 1)
endif()
else ()
message(STATUS "use c++20")
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 ()
option(HAS_STD_FILESYSTEM_PATH "use std::filesystem" ON)
# HACK: Needed to be compiled on Yocto Linux
if(TARGET std::filesystem)
get_property(CAN_LINK_FS TARGET std::filesystem PROPERTY INTERFACE_LINK_LIBRARIES SET)
if ( CAN_LINK_FS )
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)
target_link_libraries(${PROJECT_NAME} PUBLIC std::filesystem)
endif()
endif()
# jsoncpp
find_package(Jsoncpp REQUIRED)
target_link_libraries(${PROJECT_NAME} PUBLIC Jsoncpp_lib)
list(APPEND INCLUDE_DIRS_FOR_DYNAMIC_VIEW ${JSONCPP_INCLUDE_DIRS})
# yamlcpp
if(BUILD_YAML_CONFIG)
find_package(yaml-cpp QUIET)
if(yaml-cpp_FOUND)
if (NOT YAML_CPP_LIBRARIES)
find_library(YAML_CPP_LINK_LIBRARY "yaml-cpp")
if(NOT YAML_CPP_LINK_LIBRARY)
message(STATUS "yaml-cpp not used")
else()
message(STATUS "yaml-cpp found ")
target_link_libraries(${PROJECT_NAME} PUBLIC ${YAML_CPP_LINK_LIBRARY})
target_compile_definitions(${PROJECT_NAME} PUBLIC HAS_YAML_CPP)
endif()
else()
message(STATUS "yaml-cpp found ")
target_link_libraries(${PROJECT_NAME} PUBLIC ${YAML_CPP_LIBRARIES})
target_compile_definitions(${PROJECT_NAME} PUBLIC HAS_YAML_CPP)
endif()
else()
message(STATUS "yaml-cpp not used")
endif()
else()
message(STATUS "yaml-cpp not used")
endif(BUILD_YAML_CONFIG)
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD"
AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD"
AND NOT WIN32)
@ -224,18 +207,16 @@ if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD"
try_compile(normal_uuid ${CMAKE_BINARY_DIR}/cmaketest
${PROJECT_SOURCE_DIR}/cmake/tests/normal_uuid_lib_test.cc
LINK_LIBRARIES UUID_lib
OUTPUT_VARIABLE NORMAL_UUID_COMPILE_OUTPUT)
LINK_LIBRARIES UUID_lib)
try_compile(ossp_uuid ${CMAKE_BINARY_DIR}/cmaketest
${PROJECT_SOURCE_DIR}/cmake/tests/ossp_uuid_lib_test.cc
LINK_LIBRARIES UUID_lib
OUTPUT_VARIABLE OSSP_UUID_COMPILE_OUTPUT)
LINK_LIBRARIES UUID_lib)
if (normal_uuid)
add_definitions(-DUSE_OSSP_UUID=0)
elseif (ossp_uuid)
add_definitions(-DUSE_OSSP_UUID=1)
else ()
message(FATAL_ERROR "uuid lib error:\n${NORMAL_UUID_COMPILE_OUTPUT}\n${OSSP_UUID_COMPILE_OUTPUT}")
message(FATAL_ERROR "uuid lib error")
endif ()
endif (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD"
AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD"
@ -252,69 +233,49 @@ endif (BUILD_BROTLI)
set(DROGON_SOURCES
lib/src/AOPAdvice.cc
lib/src/AccessLogger.cc
lib/src/CacheFile.cc
lib/src/ConfigAdapterManager.cc
lib/src/ConfigLoader.cc
lib/src/Cookie.cc
lib/src/DrClassMap.cc
lib/src/DrTemplateBase.cc
lib/src/MiddlewaresFunction.cc
lib/src/FixedWindowRateLimiter.cc
lib/src/GlobalFilters.cc
lib/src/Histogram.cc
lib/src/Hodor.cc
lib/src/FiltersFunction.cc
lib/src/HttpAppFrameworkImpl.cc
lib/src/HttpBinder.cc
lib/src/HttpClientImpl.cc
lib/src/HttpConnectionLimit.cc
lib/src/HttpControllerBinder.cc
lib/src/HttpControllersRouter.cc
lib/src/HttpFileImpl.cc
lib/src/HttpFileUploadRequest.cc
lib/src/HttpRequestImpl.cc
lib/src/HttpRequestParser.cc
lib/src/RequestStream.cc
lib/src/HttpResponseImpl.cc
lib/src/HttpResponseParser.cc
lib/src/HttpServer.cc
lib/src/HttpSimpleControllersRouter.cc
lib/src/HttpUtils.cc
lib/src/HttpViewData.cc
lib/src/IntranetIpFilter.cc
lib/src/JsonConfigAdapter.cc
lib/src/ListenerManager.cc
lib/src/LocalHostFilter.cc
lib/src/MultiPart.cc
lib/src/MultipartStreamParser.cc
lib/src/NotFound.cc
lib/src/PluginsManager.cc
lib/src/PromExporter.cc
lib/src/RangeParser.cc
lib/src/RateLimiter.cc
lib/src/RealIpResolver.cc
lib/src/SecureSSLRedirector.cc
lib/src/Redirector.cc
lib/src/AccessLogger.cc
lib/src/SessionManager.cc
lib/src/SlashRemover.cc
lib/src/SlidingWindowRateLimiter.cc
lib/src/StaticFileRouter.cc
lib/src/TaskTimeoutFlag.cc
lib/src/TokenBucketRateLimiter.cc
lib/src/Utilities.cc
lib/src/WebSocketClientImpl.cc
lib/src/WebSocketConnectionImpl.cc
lib/src/YamlConfigAdapter.cc
lib/src/drogon_test.cc)
lib/src/WebsocketControllersRouter.cc)
set(private_headers
lib/src/AOPAdvice.h
lib/src/CacheFile.h
lib/src/ConfigLoader.h
lib/src/ControllerBinderBase.h
lib/src/MiddlewaresFunction.h
lib/src/filesystem.h
lib/src/FiltersFunction.h
lib/src/HttpAppFrameworkImpl.h
lib/src/HttpClientImpl.h
lib/src/HttpConnectionLimit.h
lib/src/HttpControllerBinder.h
lib/src/HttpControllersRouter.h
lib/src/HttpFileImpl.h
lib/src/HttpFileUploadRequest.h
@ -324,6 +285,7 @@ set(private_headers
lib/src/HttpResponseImpl.h
lib/src/HttpResponseParser.h
lib/src/HttpServer.h
lib/src/HttpSimpleControllersRouter.h
lib/src/HttpUtils.h
lib/src/impl_forwards.h
lib/src/ListenerManager.h
@ -334,30 +296,23 @@ set(private_headers
lib/src/TaskTimeoutFlag.h
lib/src/WebSocketClientImpl.h
lib/src/WebSocketConnectionImpl.h
lib/src/FixedWindowRateLimiter.h
lib/src/SlidingWindowRateLimiter.h
lib/src/TokenBucketRateLimiter.h
lib/src/ConfigAdapterManager.h
lib/src/JsonConfigAdapter.h
lib/src/YamlConfigAdapter.h
lib/src/ConfigAdapter.h
lib/src/MultipartStreamParser.h)
lib/src/WebsocketControllersRouter.h)
if (NOT WIN32 AND NOT CMAKE_SYSTEM_NAME STREQUAL "iOS")
if (NOT WIN32)
set(DROGON_SOURCES
${DROGON_SOURCES}
lib/src/SharedLibManager.cc)
set(private_headers
${private_headers}
lib/src/SharedLibManager.h)
elseif(WIN32)
else (NOT WIN32)
set(DROGON_SOURCES
${DROGON_SOURCES}
third_party/mman-win32/mman.c)
set(private_headers
${private_headers}
third_party/mman-win32/mman.h)
endif()
endif (NOT WIN32)
if (BUILD_POSTGRESQL)
# find postgres
@ -368,12 +323,10 @@ if (BUILD_POSTGRESQL)
target_link_libraries(${PROJECT_NAME} PRIVATE pg_lib)
set(DROGON_SOURCES
${DROGON_SOURCES}
orm_lib/src/postgresql_impl/PostgreSQLResultImpl.cc
orm_lib/src/postgresql_impl/PgListener.cc)
orm_lib/src/postgresql_impl/PostgreSQLResultImpl.cc)
set(private_headers
${private_headers}
orm_lib/src/postgresql_impl/PostgreSQLResultImpl.h
orm_lib/src/postgresql_impl/PgListener.h)
orm_lib/src/postgresql_impl/PostgreSQLResultImpl.h)
if (LIBPQ_BATCH_MODE)
try_compile(libpq_supports_batch ${CMAKE_BINARY_DIR}/cmaketest
${PROJECT_SOURCE_DIR}/cmake/tests/test_libpq_batch_mode.cc
@ -399,28 +352,11 @@ if (BUILD_POSTGRESQL)
endif (BUILD_POSTGRESQL)
if (BUILD_MYSQL)
# Find mysql, only mariadb client library is supported
find_package(MySQL QUIET)
find_package(unofficial-libmariadb QUIET)
# Find mysql, only mariadb client liberary is supported
find_package(MySQL)
if (MySQL_FOUND)
message(STATUS "Ok! We find the mariadb!")
target_link_libraries(${PROJECT_NAME} PRIVATE MySQL_lib)
set(DROGON_FOUND_MYSQL TRUE)
set(MYSQL_LIB_NAME MySQL_lib)
elseif (unofficial-libmariadb_FOUND)
target_link_libraries(${PROJECT_NAME} PRIVATE unofficial::libmariadb)
set(DROGON_FOUND_MYSQL TRUE)
set(MYSQL_LIB_NAME unofficial::libmariadb)
endif ()
if (DROGON_FOUND_MYSQL)
message(STATUS "Ok! We find mariadb!")
include(CheckLibraryExists)
check_library_exists(${MYSQL_LIB_NAME} mysql_optionsv "" HAS_MYSQL_OPTIONSV)
if (HAS_MYSQL_OPTIONSV)
message(STATUS "Mariadb support mysql_optionsv")
add_definitions(-DHAS_MYSQL_OPTIONSV)
endif(HAS_MYSQL_OPTIONSV)
set(DROGON_SOURCES
${DROGON_SOURCES}
orm_lib/src/mysql_impl/MysqlConnection.cc
@ -429,25 +365,14 @@ if (BUILD_MYSQL)
${private_headers}
orm_lib/src/mysql_impl/MysqlConnection.h
orm_lib/src/mysql_impl/MysqlResultImpl.h)
else (DROGON_FOUND_MYSQL)
message(STATUS "MySql was not found.")
endif (DROGON_FOUND_MYSQL)
endif (MySQL_FOUND)
endif (BUILD_MYSQL)
if (BUILD_SQLITE)
# Find sqlite3.
find_package(SQLite3 QUIET)
find_package(unofficial-sqlite3 QUIET)
find_package(SQLite3)
if (SQLite3_FOUND)
target_link_libraries(${PROJECT_NAME} PRIVATE SQLite3_lib)
set(DROGON_FOUND_SQLite3 TRUE)
elseif (unofficial-sqlite3_FOUND)
target_link_libraries(${PROJECT_NAME} PRIVATE unofficial::sqlite3::sqlite3)
set(DROGON_FOUND_SQLite3 TRUE)
endif ()
if (DROGON_FOUND_SQLite3)
message(STATUS "Ok! We find sqlite3!")
set(DROGON_SOURCES
${DROGON_SOURCES}
orm_lib/src/sqlite3_impl/Sqlite3Connection.cc
@ -456,9 +381,7 @@ if (BUILD_SQLITE)
${private_headers}
orm_lib/src/sqlite3_impl/Sqlite3Connection.h
orm_lib/src/sqlite3_impl/Sqlite3ResultImpl.h)
else (DROGON_FOUND_SQLite3)
message(STATUS "sqlite3 was not found.")
endif (DROGON_FOUND_SQLite3)
endif (SQLite3_FOUND)
endif (BUILD_SQLITE)
if (BUILD_REDIS)
@ -473,17 +396,13 @@ if (BUILD_REDIS)
nosql_lib/redis/src/RedisClientManager.cc
nosql_lib/redis/src/RedisConnection.cc
nosql_lib/redis/src/RedisResult.cc
nosql_lib/redis/src/RedisTransactionImpl.cc
nosql_lib/redis/src/SubscribeContext.cc
nosql_lib/redis/src/RedisSubscriberImpl.cc)
nosql_lib/redis/src/RedisTransactionImpl.cc)
set(private_headers
${private_headers}
nosql_lib/redis/src/RedisClientImpl.h
nosql_lib/redis/src/RedisClientLockFree.h
nosql_lib/redis/src/RedisConnection.h
nosql_lib/redis/src/RedisTransactionImpl.h
nosql_lib/redis/src/SubscribeContext.h
nosql_lib/redis/src/RedisSubscriberImpl.h)
nosql_lib/redis/src/RedisTransactionImpl.h)
endif (Hiredis_FOUND)
endif (BUILD_REDIS)
@ -506,12 +425,26 @@ endif (BUILD_TESTING)
find_package(ZLIB REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE ZLIB::ZLIB)
find_package(OpenSSL)
if (OpenSSL_FOUND)
target_link_libraries(${PROJECT_NAME} PRIVATE OpenSSL::SSL OpenSSL::Crypto)
else (OpenSSL_FOUND)
set(DROGON_SOURCES
${DROGON_SOURCES}
lib/src/ssl_funcs/Md5.cc
lib/src/ssl_funcs/Sha1.cc)
set(private_headers
${private_headers}
lib/src/ssl_funcs/Md5.h
lib/src/ssl_funcs/Sha1.h)
endif (OpenSSL_FOUND)
execute_process(COMMAND "git" rev-parse HEAD
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE GIT_SHA1
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
configure_file("${PROJECT_SOURCE_DIR}/cmake/templates/version.h.in"
"${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)
option(USE_COROUTINE "Enable C++20 coroutine support" ON)
@ -545,7 +478,6 @@ set(DROGON_SOURCES
orm_lib/src/DbClientImpl.cc
orm_lib/src/DbClientLockFree.cc
orm_lib/src/DbConnection.cc
orm_lib/src/DbListener.cc
orm_lib/src/Exception.cc
orm_lib/src/Field.cc
orm_lib/src/Result.cc
@ -566,9 +498,7 @@ set(DROGON_HEADERS
lib/inc/drogon/HttpClient.h
lib/inc/drogon/HttpController.h
lib/inc/drogon/HttpFilter.h
lib/inc/drogon/HttpMiddleware.h
lib/inc/drogon/HttpRequest.h
lib/inc/drogon/RequestStream.h
lib/inc/drogon/HttpResponse.h
lib/inc/drogon/HttpSimpleController.h
lib/inc/drogon/HttpTypes.h
@ -584,11 +514,10 @@ set(DROGON_HEADERS
lib/inc/drogon/WebSocketConnection.h
lib/inc/drogon/WebSocketController.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/PubSubService.h
lib/inc/drogon/drogon_test.h
lib/inc/drogon/RateLimiter.h
${CMAKE_CURRENT_BINARY_DIR}/exports/drogon/exports.h)
set(private_headers
${private_headers}
@ -597,15 +526,15 @@ set(private_headers
orm_lib/src/DbConnection.h
orm_lib/src/ResultImpl.h
orm_lib/src/TransactionImpl.h)
if (pg_FOUND OR DROGON_FOUND_MYSQL OR DROGON_FOUND_SQLite3)
if (pg_FOUND OR MySQL_FOUND OR SQLite3_FOUND)
set(DROGON_SOURCES
${DROGON_SOURCES}
orm_lib/src/DbClientManager.cc)
else (pg_FOUND OR DROGON_FOUND_MYSQL OR DROGON_FOUND_SQLite3)
else (pg_FOUND OR MySQL_FOUND OR SQLite3_FOUND)
set(DROGON_SOURCES
${DROGON_SOURCES}
lib/src/DbClientManagerSkipped.cc)
endif (pg_FOUND OR DROGON_FOUND_MYSQL OR DROGON_FOUND_SQLite3)
endif (pg_FOUND OR MySQL_FOUND OR SQLite3_FOUND)
set_target_properties(${PROJECT_NAME}
PROPERTIES CXX_STANDARD ${DROGON_CXX_STANDARD})
@ -613,27 +542,27 @@ set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD_REQUIRED ON)
set_target_properties(${PROJECT_NAME} PROPERTIES CXX_EXTENSIONS OFF)
set_target_properties(${PROJECT_NAME} PROPERTIES EXPORT_NAME Drogon)
if (pg_FOUND OR DROGON_FOUND_MYSQL OR DROGON_FOUND_SQLite3)
if (pg_FOUND OR MySQL_FOUND OR SQLite3_FOUND)
if (pg_FOUND)
option(USE_POSTGRESQL "Enable PostgreSQL" ON)
else (pg_FOUND)
option(USE_POSTGRESQL "Disable PostgreSQL" OFF)
endif (pg_FOUND)
if (DROGON_FOUND_MYSQL)
if (MySQL_FOUND)
option(USE_MYSQL "Enable Mysql" ON)
else (DROGON_FOUND_MYSQL)
else (MySQL_FOUND)
option(USE_MYSQL "Disable Mysql" OFF)
endif (DROGON_FOUND_MYSQL)
endif (MySQL_FOUND)
if (DROGON_FOUND_SQLite3)
if (SQLite3_FOUND)
option(USE_SQLITE3 "Enable Sqlite3" ON)
else (DROGON_FOUND_SQLite3)
else (SQLite3_FOUND)
option(USE_SQLITE3 "Disable Sqlite3" OFF)
endif (DROGON_FOUND_SQLite3)
endif (pg_FOUND OR DROGON_FOUND_MYSQL OR DROGON_FOUND_SQLite3)
endif (SQLite3_FOUND)
endif (pg_FOUND OR MySQL_FOUND OR SQLite3_FOUND)
get_filename_component(COMPILER_COMMAND ${CMAKE_CXX_COMPILER} NAME)
set(COMPILER_COMMAND ${CMAKE_CXX_COMPILER})
set(COMPILER_ID ${CMAKE_CXX_COMPILER_ID})
if (CMAKE_BUILD_TYPE)
@ -669,12 +598,12 @@ if (BUILD_TESTING)
if (pg_FOUND)
add_subdirectory(${PROJECT_SOURCE_DIR}/orm_lib/src/postgresql_impl/test)
endif (pg_FOUND)
if (DROGON_FOUND_MYSQL)
if (MySQL_FOUND)
add_subdirectory(${PROJECT_SOURCE_DIR}/orm_lib/src/mysql_impl/test)
endif (DROGON_FOUND_MYSQL)
if (DROGON_FOUND_SQLite3)
endif (MySQL_FOUND)
if (SQLite3_FOUND)
add_subdirectory(${PROJECT_SOURCE_DIR}/orm_lib/src/sqlite3_impl/test)
endif (DROGON_FOUND_SQLite3)
endif (SQLite3_FOUND)
add_subdirectory(${PROJECT_SOURCE_DIR}/orm_lib/tests)
endif (BUILD_TESTING)
@ -690,11 +619,8 @@ install(FILES ${DROGON_HEADERS} DESTINATION ${INSTALL_INCLUDE_DIR}/drogon)
set(ORM_HEADERS
orm_lib/inc/drogon/orm/ArrayParser.h
orm_lib/inc/drogon/orm/BaseBuilder.h
orm_lib/inc/drogon/orm/Criteria.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/DbTypes.h
orm_lib/inc/drogon/orm/Exception.h
orm_lib/inc/drogon/orm/Field.h
@ -712,43 +638,25 @@ install(FILES ${ORM_HEADERS} DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/orm)
set(NOSQL_HEADERS
nosql_lib/redis/inc/drogon/nosql/RedisClient.h
nosql_lib/redis/inc/drogon/nosql/RedisResult.h
nosql_lib/redis/inc/drogon/nosql/RedisSubscriber.h
nosql_lib/redis/inc/drogon/nosql/RedisException.h)
install(FILES ${NOSQL_HEADERS} DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/nosql)
set(DROGON_UTIL_HEADERS
lib/inc/drogon/utils/coroutine.h
lib/inc/drogon/utils/FunctionTraits.h
lib/inc/drogon/utils/HttpConstraint.h
lib/inc/drogon/utils/OStringStream.h
lib/inc/drogon/utils/Utilities.h
lib/inc/drogon/utils/monitoring.h)
lib/inc/drogon/utils/any.h
lib/inc/drogon/utils/string_view.h
lib/inc/drogon/utils/optional.h
lib/inc/drogon/utils/coroutine.h
lib/inc/drogon/utils/HttpConstraint.h
lib/inc/drogon/utils/OStringStream.h)
install(FILES ${DROGON_UTIL_HEADERS}
DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/utils)
set(DROGON_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
lib/inc/drogon/plugins/Plugin.h
lib/inc/drogon/plugins/Redirector.h
lib/inc/drogon/plugins/SecureSSLRedirector.h
lib/inc/drogon/plugins/AccessLogger.h
lib/inc/drogon/plugins/RealIpResolver.h
lib/inc/drogon/plugins/Hodor.h
lib/inc/drogon/plugins/SlashRemover.h
lib/inc/drogon/plugins/GlobalFilters.h
lib/inc/drogon/plugins/PromExporter.h)
lib/inc/drogon/plugins/AccessLogger.h)
install(FILES ${DROGON_PLUGIN_HEADERS}
DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/plugins)
@ -759,8 +667,7 @@ target_sources(${PROJECT_NAME} PRIVATE
${ORM_HEADERS}
${DROGON_UTIL_HEADERS}
${DROGON_PLUGIN_HEADERS}
${NOSQL_HEADERS}
${DROGON_MONITORING_HEADERS})
${NOSQL_HEADERS})
source_group("Public API"
FILES
@ -768,8 +675,7 @@ source_group("Public API"
${ORM_HEADERS}
${DROGON_UTIL_HEADERS}
${DROGON_PLUGIN_HEADERS}
${NOSQL_HEADERS}
${DROGON_MONITORING_HEADERS})
${NOSQL_HEADERS})
source_group("Private Headers"
FILES
${private_headers})

View File

@ -9,7 +9,7 @@ contribute in an overall positive way.
1. Fork, then clone the repository: `git clone
git@github.com:your-username/drogon.git`
1. Follow the [official installation steps on
Github](https://drogonframework.github.io/drogon-docs/#/ENG-02-Installation). Its best to
DocsForge](https://drogon.docsforge.com/master/installation/). Its best to
make sure to have the `drogon_ctl` executable in your shells `PATH`
environment variable in case you use a terminal.

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

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2019-2023 An Tao
Copyright (c) 2019-2021 An Tao
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,14 +1,15 @@
![](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)
[![Conan Center](https://img.shields.io/conan/v/drogon)](https://conan.io/center/recipes/drogon)
[![Build Status](https://travis-ci.com/an-tao/drogon.svg?branch=master)](https://travis-ci.com/an-tao/drogon)
![Build Status](https://github.com/an-tao/drogon/workflows/Build%20Drogon/badge.svg?branch=master)
[![Total alerts](https://img.shields.io/lgtm/alerts/g/an-tao/drogon.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/an-tao/drogon/alerts/)
[![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 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)
English | [简体中文](./README.zh-CN.md) | [繁體中文](./README.zh-TW.md)
### 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:
@ -34,7 +35,7 @@ Drogon is a cross-platform framework, It supports Linux, macOS, FreeBSD, OpenBSD
* Support ARM Architecture;
* 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 AOP with built-in joinpoints.
* Support AOP with build-in joinpoints.
* Support C++ coroutines
## A very simple example
@ -95,7 +96,7 @@ using namespace drogon;
class TestCtrl:public drogon::HttpSimpleController<TestCtrl>
{
public:
void asyncHandleHttpRequest(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback) override;
virtual void asyncHandleHttpRequest(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback) override;
PATH_LIST_BEGIN
PATH_ADD("/test",Get);
PATH_LIST_END
@ -126,7 +127,7 @@ using namespace drogon;
class JsonCtrl : public drogon::HttpSimpleController<JsonCtrl>
{
public:
void asyncHandleHttpRequest(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback) override;
virtual void asyncHandleHttpRequest(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback) override;
PATH_LIST_BEGIN
//list path definitions here;
PATH_ADD("/json", Get);
@ -183,40 +184,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.
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**.
## Cross-compilation
Drogon supports cross-compilation, you should define the `CMAKE_SYSTEM_NAME` in toolchain file, for example:
```cmake
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
```
You can disable building options for examples and drogon_ctl by settings `BUILD_EXAMPLES` and `BUILD_CTL` to `OFF` in the toolchain file.
## Building options
Drogon provides some building options, you can enable or disable them by setting the corresponding variables to `ON` or `OFF` in the cmake command line, cmake file etc...
| Option name | Description | Default value |
| :--- | :--- | :--- |
| BUILD_CTL | Build drogon_ctl | ON |
| BUILD_EXAMPLES | Build examples | ON |
| BUILD_ORM | Build orm | ON |
| COZ_PROFILING | Use coz for profiling | OFF |
| BUILD_SHARED_LIBS | Build drogon as a shared lib | OFF |
| BUILD_DOC | Build Doxygen documentation | OFF |
| BUILD_BROTLI | Build Brotli | ON |
| BUILD_YAML_CONFIG | Build yaml config | ON |
| USE_SUBMODULE | Use trantor as a submodule | ON |
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) or [DocsForge](https://drogon.docsforge.com/master/overview/)**
## Contributions
This project exists thanks to all the people who contribute code.
<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>
Every contribution is welcome. Please refer to the [contribution guidelines](CONTRIBUTING.md) for more information.

View File

@ -1,14 +1,15 @@
![](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)
[![Conan Center](https://img.shields.io/conan/v/drogon)](https://conan.io/center/recipes/drogon)
[![Build Status](https://travis-ci.com/an-tao/drogon.svg?branch=master)](https://travis-ci.com/an-tao/drogon)
![Build Status](https://github.com/an-tao/drogon/workflows/Build%20Drogon/badge.svg?branch=master)
[![Total alerts](https://img.shields.io/lgtm/alerts/g/an-tao/drogon.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/an-tao/drogon/alerts/)
[![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 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)
[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的误写为了不至于引起不必要的误会这里说明一下。
Drogon是一个跨平台框架它支持Linux也支持macOS、FreeBSDOpenBSDHaikuOS和Windows。它的主要特点如下
@ -98,7 +99,7 @@ using namespace drogon;
class TestCtrl:public drogon::HttpSimpleController<TestCtrl>
{
public:
void asyncHandleHttpRequest(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback) override;
virtual void asyncHandleHttpRequest(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback) override;
PATH_LIST_BEGIN
PATH_ADD("/test",Get);
PATH_LIST_END
@ -129,7 +130,7 @@ using namespace drogon;
class JsonCtrl : public drogon::HttpSimpleController<JsonCtrl>
{
public:
void asyncHandleHttpRequest(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback) override;
virtual void asyncHandleHttpRequest(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback) override;
PATH_LIST_BEGIN
//list path definitions here;
PATH_ADD("/json", Get);
@ -186,20 +187,12 @@ 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-概述)或者[doxiz](https://doxiz.com/drogon/master/overview/)以获取更多的信息**
## 贡献方式
欢迎您的贡献。 请阅读[贡献指南](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>
## QQ交流群1137909452
欢迎交流探讨。
## 微信公众号:
![](https://github.com/an-tao/drogon/wiki/images/qrcode_wechat.jpg)
会不定期推送一些Drogon的使用技巧和更新信息欢迎关注。

View File

@ -1,49 +1,49 @@
![](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)
[![Conan Center](https://img.shields.io/conan/v/drogon)](https://conan.io/center/recipes/drogon)
[![Build Status](https://travis-ci.com/an-tao/drogon.svg?branch=master)](https://travis-ci.com/an-tao/drogon)
![Build Status](https://github.com/an-tao/drogon/workflows/Build%20Drogon/badge.svg?branch=master)
[![Total alerts](https://img.shields.io/lgtm/alerts/g/an-tao/drogon.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/an-tao/drogon/alerts/)
[![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 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)
[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。主要特點如下
* 網路層使用基於 epollmacOS/FreeBSD 下是 kqueue的非阻塞 IO 框架,提供高並行、高效能的網路 IO。詳細請見 [TFB Tests Results](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=composite)
* 完全非同步的程式撰寫邏輯;
* 支援 HTTP 1.0/1.1(伺服器端和用戶端);
* 基於樣板template實作的簡單反射機制使主程式框架、控制器controller和視圖view完全解耦
* 支援 cookies 和內建的 session
* 支援後端算繪,將控制器產生的資料交給視圖產生 HTML 頁面,視圖由 CSP 樣板檔案描述,透過 CSP 標籤將 C++ 程式碼嵌入 HTML 頁面,由 drogon 的命令列工具在編譯階段自動產生 C++ 程式碼並編譯;
* 支援執行期的視圖頁面動態載入(動態編譯和載入 so 檔案);
* 非常方便靈活的路徑path到控制器處理函式handler的對應方案
* 支援過濾器filter方便在控制器之前執行統一的邏輯如登入驗證、HTTP Method 限制驗證等);
* 支援 HTTPS基於 OpenSSL
* 支援 WebSocket伺服器端和用戶端
* 支援 JSON 格式的請求和回應,方便開發 RESTful API
* 支援檔案下載和上傳,支援 `sendfile` 系統呼叫;
* 支援 Gzip/Brotli 壓縮傳輸;
* 支援 pipelining
* 提供輕量的命令列工具 `drogon_ctl`,幫助簡化各種類別的建立和視圖程式碼的產生過程;
* 非同步的讀寫資料庫,目前支援 PostgreSQL 和 MySQLMariaDB資料庫
* 支援非同步讀寫 Redis
* 基於執行緒池實作 sqlite3 資料庫的非同步讀寫,提供與上述資料庫相同的介面;
* 支援 ARM 架構;
* 方便的輕量級 ORM 實現,一般物件到資料庫的雙向對應;
* 支援外掛,可透過設定檔案在載入時動態載入;
* 支援內建插入點的 AOP
* 支援 C++ coroutine。
* 網路層使用基於epoll(macOS/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)完全去耦;
* 支援cookies和內建的session
* 支援後端渲染把控制器生成的數據交給視圖生成Html頁面視圖由CSP模板文件描述通過CSP標籤把C++程式碼嵌入到Html頁面由drogon的指令列工具在編譯階段自動生成C++程式碼並編譯;
* 支援運行期的視圖頁面動態加載(動態編譯和載入so文件)
* 非常方便靈活的路徑(path)到控制器處理函數(handler)的映射方案;
* 支援過濾器(filter)鏈,方便在控制器之前執行統一的邏輯(如登錄驗證、Http Method約束驗證等)
* 支援https(基於OpenSSL);
* 支援websocket(server端和client端);
* 支援Json格式的請求和回應, 方便開發Restful API;
* 支援文件下載和上傳,支援sendfile系統呼叫
* 支援gzip/brotli壓縮傳輸
* 支援pipelining
* 提供一個輕量的指令列工具drogon_ctl幫助簡化各種類的創造和視圖程式碼的生成過程
* 非同步的讀寫資料庫目前支援PostgreSQL和MySQL(MariaDB)資料庫;
* 支援異步讀寫Redis;
* 基於執行序池實現sqlite3資料庫的異步讀寫提供與上文資料庫相同的接口
* 支援ARM架構
* 方便的輕量級ORM實現一般物件到資料庫的雙向映射
* 支援外掛,可通過設定文件在載入時動態載入;
* 支援內建插入點的AOP
* 支援C++ coroutine
## 一個非常簡單的例子
不像大多數 C++ 框架drogon 的主程式可以非常簡單。Drogon 使用了一些小技巧使主程式和控制器解耦。控制器的路由設定可以在控制器類別中定義或在設定檔案中完成。
不像大多數C++框架那樣drogon的主程式可以非常簡單。 Drogon使用了一些小技巧使主程式和控制器去耦. 控制器的路由設定可以在控制器類別中定義或者設定文件中完成.
下面是一個典型主程式的樣子:
下面是一個典型的主程式的樣子:
```c++
#include <drogon/drogon.h>
@ -59,7 +59,7 @@ int main()
}
```
如果使用設定檔案,可以進一步簡化成:
如果使用設定文件,可以進一步簡化成這樣:
```c++
#include <drogon/drogon.h>
@ -70,7 +70,7 @@ int main()
}
```
當然Drogon 也提供了一些函式,讓使用者可以在 `main()` 函式中直接加入控制器邏輯,例如,使用者可以註冊一個 lambda 處理常式到 drogon 框架中,如下所示:
當然Drogon也提供了一些函數使使用者可以在main()函數中直接添加控制器邏輯比如使用者可以註冊一個lambda處理器到drogon框架中,如下所示:
```c++
app().registerHandler("/test?username={name}",
@ -87,7 +87,9 @@ app().registerHandler("/test?username={name}",
{Get,"LoginFilter"});
```
這看起來很方便,但不適用於複雜的場景,試想如果有數十個或數百個處理函式要註冊進框架,`main()` 函式將變得難以閱讀。顯然,讓每個包含處理函式的類別在自己的定義中完成註冊是更好的選擇。所以,除非你的應用邏輯非常簡單,我們不建議使用上述介面,更好的做法是建立一個 HttpSimpleController 類別,如下:
這看起來是很方便但是這並不適用於復雜的場景試想假如有數十個或者數百個處理函數要註冊進框架main()函數將膨脹到不可讀的程度。顯然讓每個包含處理函數的類在自己的定義中完成註冊是更好的選擇。所以除非你的應用邏輯非常簡單我們不推薦使用上述接口更好的實踐是我們可以創造一個HttpSimpleController類別如下
```c++
/// The TestCtrl.h file
@ -97,7 +99,7 @@ using namespace drogon;
class TestCtrl:public drogon::HttpSimpleController<TestCtrl>
{
public:
void asyncHandleHttpRequest(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback) override;
virtual void asyncHandleHttpRequest(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback) override;
PATH_LIST_BEGIN
PATH_ADD("/test",Get);
PATH_LIST_END
@ -116,9 +118,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++
/// The header file
@ -128,7 +130,7 @@ using namespace drogon;
class JsonCtrl : public drogon::HttpSimpleController<JsonCtrl>
{
public:
void asyncHandleHttpRequest(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback) override;
virtual void asyncHandleHttpRequest(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback) override;
PATH_LIST_BEGIN
//list path definitions here;
PATH_ADD("/json", Get);
@ -147,7 +149,7 @@ void JsonCtrl::asyncHandleHttpRequest(const HttpRequestPtr &req,
}
```
讓我們更進一步,透過 HttpController 類別建立一個 RESTful API 的範例,如下所示(省略實作檔案
讓我們更進一步,通過HttpController類別創造一個RESTful API的例子如下所示忽略了實做文件
```c++
/// The header file
@ -181,18 +183,16 @@ class User : public drogon::HttpController<User>
} // 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-概述)或者[doxiz](https://doxiz.com/drogon/master/overview/)以獲取更多的信息**
## 貢獻方式
歡迎您的貢獻。請閱讀[貢獻指南](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>
## QQ交流群1137909452
## QQ 交流群1137909452
歡迎交流討論。
歡迎交流探討。

1
_config.yml Normal file
View File

@ -0,0 +1 @@
theme: jekyll-theme-cayman

View File

@ -30,7 +30,7 @@ function build_drogon() {
if [ $1 -eq 1 ]; then
cmake .. -DBUILD_TESTING=YES $cmake_gen
elif [ $1 -eq 2 ]; then
cmake .. -DBUILD_TESTING=YES -DBUILD_SHARED_LIBS=ON -DCMAKE_CXX_VISIBILITY_PRESET=hidden -DCMAKE_VISIBILITY_INLINES_HIDDEN=1 $cmake_gen
cmake .. -DBUILD_TESTING=YES -DBUILD_DROGON_SHARED=ON -DCMAKE_CXX_VISIBILITY_PRESET=hidden -DCMAKE_VISIBILITY_INLINES_HIDDEN=1 $cmake_gen
else
cmake .. -DCMAKE_BUILD_TYPE=release $cmake_gen
fi
@ -48,7 +48,7 @@ function build_drogon() {
fi
echo "Installing ..."
$make_program install
sudo $make_program install
#Go back to the current directory
cd $current_dir
@ -92,10 +92,10 @@ else
make_flags="$make_flags -j$parallel"
fi
if [ "X$1" = "X-t" ]; then
if [ "$1" = "-t" ]; then
build_drogon 1
elif [ "X$1" = "X-tshared" ]; then
elif [ "$1" = "-tshared" ]; then
build_drogon 2
else
build_drogon 0
fi
fi

View File

@ -1,6 +1,6 @@
# ##############################################################################
# function drogon_create_views(target source_path output_path
# [TRUE to use_path_as_namespace] [prefixed namespace])
# [use_path_as_namespace])
# ##############################################################################
function(drogon_create_views arg)
if(ARGC LESS 3)
@ -27,27 +27,15 @@ function(drogon_create_views arg)
""
outputFile
${f2})
set(p2ns "")
if("${ARGV3}" STREQUAL "TRUE")
set(p2ns "--path-to-namespace")
endif()
if ( (ARGC EQUAL 5) AND ( NOT "${ARGV4}" STREQUAL "") )
string(REPLACE "::" "_" nSpace ${ARGV4})
set(outputFile "${nSpace}_${outputFile}")
set(ns -n ${ARGV4})
else()
set(ns "")
endif()
add_custom_command(OUTPUT ${ARGV2}/${outputFile}.h ${ARGV2}/${outputFile}.cc
COMMAND drogon_ctl
ARGS
create
view
${inFile}
${p2ns}
--path-to-namespace
-o
${ARGV2}
${ns}
DEPENDS ${cspFile}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
VERBATIM)

View File

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

View File

@ -27,6 +27,10 @@ endif()
if(@MySQL_FOUND@)
find_dependency(MySQL)
endif()
if(@Boost_FOUND@)
find_dependency(Boost)
find_package(Boost COMPONENTS filesystem system)
endif()
if(@Brotli_FOUND@)
find_dependency(Brotli)
endif()
@ -36,10 +40,7 @@ endif()
if(@Hiredis_FOUND@)
find_dependency(Hiredis)
endif()
if(@yaml-cpp_FOUND@)
find_dependency(yaml-cpp)
endif()
if(@BUILD_SHARED_LIBS@)
if(@BUILD_DROGON_SHARED@)
find_dependency(Threads)
endif()
if(@HAS_STD_FILESYSTEM_PATH@)
@ -47,7 +48,6 @@ find_dependency(Filesystem)
find_package(Filesystem COMPONENTS Final REQUIRED)
endif()
# Our library dependencies (contains definitions for IMPORTED targets)
get_filename_component(DROGON_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)

View File

@ -1,7 +1,5 @@
#pragma once
#define MAJOR @DROGON_MAJOR_VERSION@
#define MINOR @DROGON_MINOR_VERSION@
#define PATCH @DROGON_PATCH_VERSION@
#define DROGON_VERSION "@DROGON_VERSION_STRING@"
#define DROGON_VERSION_SHA1 "@GIT_SHA1@"
#define DROGON_VERSION_SHA1 "@GIT_SHA1@"

View File

@ -2,8 +2,11 @@
int main()
{
PQenterPipelineMode(NULL);
PQexitPipelineMode(NULL);
PQpipelineSync(NULL);
PQpipelineStatus(NULL);
PQisInBatchMode(NULL);
PQbatchIsAborted(NULL);
PQqueriesInBatch(NULL);
PQbeginBatchMode(NULL);
PQendBatchMode(NULL);
PQsendEndBatch(NULL);
PQgetNextQuery(NULL);
}

View File

@ -23,9 +23,9 @@ include(FindPackageHandleStandardArgs)
find_path(BROTLI_INCLUDE_DIR "brotli/decode.h")
find_library(BROTLICOMMON_LIBRARY NAMES brotlicommon brotlicommon-static)
find_library(BROTLIDEC_LIBRARY NAMES brotlidec brotlidec-static)
find_library(BROTLIENC_LIBRARY NAMES brotlienc brotlienc-static)
find_library(BROTLICOMMON_LIBRARY NAMES brotlicommon)
find_library(BROTLIDEC_LIBRARY NAMES brotlidec)
find_library(BROTLIENC_LIBRARY NAMES brotlienc)
find_package_handle_standard_args(Brotli
REQUIRED_VARS

View File

@ -102,14 +102,8 @@ if(TARGET std::filesystem)
return()
endif()
# Ignore filesystem check if version too low
if(CMAKE_VERSION VERSION_LESS 3.10)
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)
return()
endif()
cmake_minimum_required(VERSION 3.10)
include(CMakePushCheckState)
include(CheckIncludeFileCXX)
@ -132,7 +126,6 @@ cmake_push_check_state()
set(CMAKE_REQUIRED_QUIET ${Filesystem_FIND_QUIETLY})
# All of our tests required C++17 or later
set(BACKUP_CXX_STANDARD "${CMAKE_CXX_STANDARD}")
set(CMAKE_CXX_STANDARD 17)
# Normalize and check the component list we were given
@ -201,7 +194,7 @@ set(_found FALSE)
if(CXX_FILESYSTEM_HAVE_FS)
# We have some filesystem library available. Do link checks
string(CONFIGURE [[
#include <cstdio>
#include <cstdlib>
#include <@CXX_FILESYSTEM_HEADER@>
int main() {
@ -211,11 +204,6 @@ if(CXX_FILESYSTEM_HAVE_FS)
}
]] code @ONLY)
# HACK: Needed to compile correctly on Yocto Linux
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang"
OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
set(CMAKE_REQUIRED_FLAGS ${prev_req_flags} -std=c++17)
endif ()
# Check a simple filesystem program without any linker flags
_cmcm_check_cxx_source("${code}" CXX_FILESYSTEM_NO_LINK_NEEDED)
@ -258,4 +246,3 @@ if(Filesystem_FIND_REQUIRED AND NOT Filesystem_FOUND)
message(FATAL_ERROR "Cannot run simple program using std::filesystem")
endif()
set(CMAKE_CXX_STANDARD "${BACKUP_CXX_STANDARD}")

View File

@ -7,7 +7,6 @@
if (HIREDIS_INCLUDE_DIRS AND HIREDIS_LIBRARIES)
set(HIREDIS_FIND_QUIETLY TRUE)
set(Hiredis_FOUND TRUE)
else ()
find_path(
HIREDIS_INCLUDE_DIR

View File

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

View File

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

View File

@ -14,7 +14,7 @@
if(UUID_LIBRARIES AND UUID_INCLUDE_DIRS)
# in cache already
set(UUID_FOUND TRUE)
else()
else(UUID_LIBRARIES AND UUID_INCLUDE_DIRS)
find_path(
UUID_INCLUDE_DIR
NAMES uuid.h
@ -76,34 +76,34 @@ else()
/opt/lib
/usr/freeware/lib64)
if(NOT UUID_LIBRARY AND (BSD OR APPLE))
if(NOT UUID_LIBRARY AND BSD)
set(UUID_LIBRARY "")
endif()
endif(NOT UUID_LIBRARY AND BSD)
set(UUID_INCLUDE_DIRS ${UUID_INCLUDE_DIR})
set(UUID_LIBRARIES ${UUID_LIBRARY})
if(UUID_INCLUDE_DIRS)
if((BSD OR APPLE) OR UUID_LIBRARIES)
if(BSD OR UUID_LIBRARIES)
set(UUID_FOUND TRUE)
endif()
endif()
endif(BSD OR UUID_LIBRARIES)
endif(UUID_INCLUDE_DIRS)
if(UUID_FOUND)
if(NOT UUID_FIND_QUIETLY)
message(STATUS "Found UUID: ${UUID_LIBRARIES}")
endif()
else()
endif(NOT UUID_FIND_QUIETLY)
else(UUID_FOUND)
if(UUID_FIND_REQUIRED)
message(FATAL_ERROR "Could not find UUID")
endif()
endif()
endif(UUID_FIND_REQUIRED)
endif(UUID_FOUND)
# show the UUID_INCLUDE_DIRS and UUID_LIBRARIES variables only in the advanced
# view
mark_as_advanced(UUID_INCLUDE_DIRS UUID_LIBRARIES)
endif()
endif(UUID_LIBRARIES AND UUID_INCLUDE_DIRS)
if(UUID_FOUND)
add_library(UUID_lib INTERFACE IMPORTED)
@ -112,7 +112,7 @@ if(UUID_FOUND)
"${UUID_INCLUDE_DIRS}"
INTERFACE_LINK_LIBRARIES
"${UUID_LIBRARIES}")
else()
else(UUID_FOUND)
set(UUID_LIBRARIES)
set(UUID_INCLUDE_DIRS)
endif()
endif(UUID_FOUND)

View File

@ -2,14 +2,14 @@
jsoncpp/1.9.4
zlib/1.2.11
gtest/1.10.0
sqlite3/3.40.1
sqlite3/3.34.1
#libpq/13.2
openssl/1.1.1t
openssl/1.1.1j
hiredis/1.0.0
brotli/1.0.9
[generators]
CMakeToolchain
cmake_paths
[options]

View File

@ -5,8 +5,8 @@
//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`.
"ssl": {
"cert": "../../trantor/trantor/tests/server.crt",
"key": "../../trantor/trantor/tests/server.key",
"cert": "../../trantor/trantor/tests/server.pem",
"key": "../../trantor/trantor/tests/server.pem",
"conf": [
//["Options", "-SessionTicket"],
//["Options", "Compression"]
@ -39,7 +39,7 @@
"db_clients": [
{
//name: Name of the client,'default' by default
"name": "default",
//"name":"",
//rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default
"rdbms": "postgresql",
//filename: Sqlite3 db file name
@ -65,25 +65,17 @@
"number_of_connections": 1,
//timeout: -1.0 by default, in seconds, the timeout for executing a SQL query.
//zero or negative value means no timeout.
"timeout": -1.0,
//auto_batch: this feature is only available for the PostgreSQL driver(version >= 14.0), see
//the wiki for more details.
"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" }
"timeout": -1.0
}
],
"redis_clients": [
{
//name: Name of the client,'default' by default
"name": "default",
//"name":"",
//host: Server IP, 127.0.0.1 by default
"host": "127.0.0.1",
//port: Server port, 6379 by default
"port": 6379,
//username: '' by default which means 'default' in redis ACL
"username": "",
//passwd: '' by default
"passwd": "",
//db index: 0 by default
@ -106,14 +98,7 @@
//enable_session: False by default
"enable_session": true,
"session_timeout": 0,
//string value of SameSite attribute of the Set-Cookie HTTP response header
//valid value is either 'Null' (default), 'Lax', 'Strict' or 'None'
"session_same_site": "Null",
//session_cookie_key: The cookie key of the session, "JSESSIONID" by default
"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: Root path of HTTP document, defaut path is ./
"document_root": "./",
//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
@ -126,10 +111,10 @@
"implicit_page": "index.html",
//static_file_headers: Headers for static files
/*"static_file_headers": [
{
{
"name": "field-name",
"value": "field-value"
}
}
],*/
//upload_path: The path to save the uploaded file. "uploads" by default.
//If the path isn't prefixed with /, ./ or ../,
@ -152,9 +137,7 @@
"xap",
"apk",
"cur",
"xml",
"webp",
"svg"
"xml"
],
// 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.
@ -187,7 +170,7 @@
],
//max_connections: maximum number of connections, 100000 by default
"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,
//Load_dynamic_views: False by default, when set to true, drogon
//compiles and loads dynamically "CSP View Files" in directories defined
@ -202,8 +185,6 @@
//files is the path where the csp files locate. If the path isn't prefixed with /, it is relative
//path of the current working directory.
"dynamic_views_output_path": "",
//json_parser_stack_limit: 1000 by default, the maximum number of stack depth when reading a json string by the jsoncpp library.
"json_parser_stack_limit": 1000,
//enable_unicode_escaping_in_json: true by default, enable unicode escaping in json.
"enable_unicode_escaping_in_json": true,
//float_precision_in_json: set precision of float number in json.
@ -217,8 +198,6 @@
},
//log: Set log output, drogon output logs to stdout by default
"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": "./",
//logfile_base_name: Log file base name,empty by default which means drogon names logfile as
@ -227,14 +206,9 @@
//log_size_limit: 100000000 bytes by default,
//When the log file size reaches "log_size_limit", the log file is switched.
"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"
//The TRACE level is only valid when built in DEBUG mode.
"log_level": "DEBUG",
//display_local_time: false by default, if true, the log time is displayed in local time
"display_local_time": false
"log_level": "DEBUG"
},
//run_as_daemon: False by default
"run_as_daemon": false,
@ -253,19 +227,19 @@
//0 means cache forever, the negative value means no cache
"static_files_cache_time": 5,
//simple_controllers_map: Used to configure mapping from path to simple controller
//"simple_controllers_map": [
// {
// "path": "/path/name",
// "controller": "controllerClassName",
// "http_methods": [
// "get",
// "post"
// ],
// "filters": [
// "FilterClassName"
// ]
// }
//],
"simple_controllers_map": [
{
"path": "/path/name",
"controller": "controllerClassName",
"http_methods": [
"get",
"post"
],
"filters": [
"FilterClassName"
]
}
],
//idle_connection_timeout: Defaults to 60 seconds, the lifetime
//of the connection without read or write
"idle_connection_timeout": 60,
@ -305,43 +279,22 @@
//One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
"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": false,
// enabled_compressed_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.
// Once when receiving and once when decompressing. i.e. if the decompressed body is larger than max_body_size, the request
// will be rejected.
"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,
"reuse_port": false
},
//plugins: Define all plugins running in the application
"plugins": [
{
//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": [],
//config: The configuration of the plugin. This json object is the parameter to initialize the plugin.
//It can be commented out
"config": {
"path": "/metrics"
}
},
{
"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
"ssl_redirect_exempt": [
".*\\.jpg"
],
"secure_ssl_host": "localhost:8849"
}
}
],

View File

@ -1,318 +0,0 @@
# This is a YAML format configuration file
# 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`.
# ssl:
# cert: ../../trantor/trantor/tests/server.crt
# key: ../../trantor/trantor/tests/server.key
# conf: [
# # [Options, -SessionTicket],
# # [Options, Compression]
# ]
# listeners:
# # address: Ip address,0.0.0.0 by default
# - address: 0.0.0.0
# # port: Port number
# port: 80
# # https: If true, use https for security,false by default
# https: false
# - address: 0.0.0.0
# port: 443
# https: true
# # cert,key: Cert file path and key file path, empty by default,
# # if empty, use the global setting
# cert: ''
# key: ''
# # use_old_tls: enable the TLS1.0/1.1, false by default
# use_old_tls: false
# ssl_conf: [
# # [MinProtocol, TLSv1.3]
# ]
# db_clients:
# # name: Name of the client,'default' by default
# - name: default
# # rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default
# rdbms: postgresql
# # filename: Sqlite3 db file name
# # filename: ''
# # host: Server address,localhost by default
# host: 127.0.0.1
# # port: Server port, 5432 by default
# port: 5432
# # dbname: Database name
# dbname: test
# # user: 'postgres' by default
# user: ''
# # passwd: '' by default
# passwd: ''
# # is_fast: false by default, if it is true, the client is faster but user can't call
# # any synchronous interface of it.
# is_fast: false
# # client_encoding: The character set used by the client. it is empty string by default which
# # means use the default character set.
# # client_encoding: ''
# # number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
# # connections per IO thread, otherwise it is the total number of all connections.
# number_of_connections: 1
# # timeout: -1 by default, in seconds, the timeout for executing a SQL query.
# # zero or negative value means no timeout.
# timeout: -1
# # auto_batch: this feature is only available for the PostgreSQL driver(version >= 14.0), see
# # the wiki for more details.
# 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:
# # name: Name of the client,'default' by default
# - name: default
# # host: Server IP, 127.0.0.1 by default
# host: 127.0.0.1
# # port: Server port, 6379 by default
# port: 6379
# # username: '' by default which means 'default' in redis ACL
# username: ''
# # passwd: '' by default
# passwd: ''
# # db index: 0 by default
# db: 0
# # is_fast: false by default, if it is true, the client is faster but user can't call
# # any synchronous interface of it.
# is_fast: false
# # number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
# # connections per IO thread, otherwise it is the total number of all connections.
# number_of_connections: 1
# # timeout: -1.0 by default, in seconds, the timeout for executing a command.
# # zero or negative value means no timeout.
# timeout: -1
app:
# number_of_threads: The number of IO threads, 1 by default, if the value is set to 0, the number of threads
# is the number of CPU cores
number_of_threads: 1
# enable_session: False by default
enable_session: true
session_timeout: 0
# string value of SameSite attribute of the Set-Cookie HTTP response header
# valid value is either 'Null' (default), 'Lax', 'Strict' or 'None'
session_same_site: 'Null'
# session_cookie_key: The cookie key of the session, "JSESSIONID" by default
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: ./
# 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
# to the request for "/".
home_page: index.html
# use_implicit_page: enable implicit pages if true, true by default
use_implicit_page: true
# implicit_page: Set the file which would the server access in a directory that a user accessed.
# For example, by default, http://localhost/a-directory resolves to http://localhost/a-directory/index.html.
implicit_page: index.html
# static_file_headers: Headers for static files
# static_file_headers:
# - name: field-name
# value: field-value
# upload_path: The path to save the uploaded file. "uploads" by default.
# If the path isn't prefixed with /, ./ or ../,
# it is relative path of document_root path
upload_path: uploads
# file_types:
# HTTP download file types,The file types supported by drogon
# by default are "html", "js", "css", "xml", "xsl", "txt", "svg",
# "ttf", "otf", "woff2", "woff" , "eot", "png", "jpg", "jpeg",
# "gif", "bmp", "ico", "icns", etc.
file_types:
- gif
- png
- jpg
- js
- css
- html
- ico
- swf
- xap
- apk
- cur
- xml
# 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.
mime: {
# text/markdown: md
# text/gemini:
# - gmi
# - gemini
}
# locations: An array of locations of static files for GET requests.
locations:
# uri_prefix: The URI prefix of the location prefixed with "/", the default value is "" that disables the location.
- uri_prefix: '' # /.well-known/acme-challenge/
# default_content_type: The default content type of the static files without
# an extension. empty string by default.
default_content_type: text/plain
# alias: The location in file system, if it is prefixed with "/", it
# presents an absolute path, otherwise it presents a relative path to
# the document_root path.
# The default value is "" which means use the document root path as the location base path.
alias: ''
# is_case_sensitive: indicates whether the URI prefix is case sensitive.
is_case_sensitive: false
# allow_all: true by default. If it is set to false, only static files with a valid extension can be accessed.
allow_all: true
# is_recursive: true by default. If it is set to false, files in sub directories can't be accessed.
is_recursive: true
# filters: string array, the filters applied to the location.
filters: []
# max_connections: maximum number of connections, 100000 by default
max_connections: 100000
# max_connections_per_ip: maximum number of connections per client, 0 by default which means no limit
max_connections_per_ip: 0
# Load_dynamic_views: False by default, when set to true, drogon
# compiles and loads dynamically "CSP View Files" in directories defined
# by "dynamic_views_path"
load_dynamic_views: false
# dynamic_views_path: If the path isn't prefixed with /, ./ or ../,
# it is relative path of document_root path
dynamic_views_path:
- ./views
# dynamic_views_output_path: Default by an empty string which means the output path of source
# files is the path where the csp files locate. If the path isn't prefixed with /, it is relative
# path of the current working directory.
dynamic_views_output_path: ''
# json_parser_stack_limit: 1000 by default, the maximum number of stack depth when reading a json string by the jsoncpp library.
json_parser_stack_limit: 1000
# enable_unicode_escaping_in_json: true by default, enable unicode escaping in json.
enable_unicode_escaping_in_json: true
# float_precision_in_json: set precision of float number in json.
float_precision_in_json:
# precision: 0 by default, 0 means use the default precision of the jsoncpp lib.
precision: 0
# precision_type: must be "significant" or "decimal", defaults to "significant" that means
# setting max number of significant digits in string, "decimal" means setting max number of
# digits after "." in string
precision_type: significant
# log: Set log output, drogon output logs to stdout by default
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: ./
# logfile_base_name: Log file base name,empty by default which means drogon names logfile as
# drogon.log ...
logfile_base_name: ''
# log_size_limit: 100000000 bytes by default,
# When the log file size reaches "log_size_limit", the log file is switched.
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"
# The TRACE level is only valid when built in DEBUG mode.
log_level: DEBUG
# display_local_time: false by default, if true, the log time is displayed in local time
display_local_time: false
# run_as_daemon: False by default
run_as_daemon: false
# handle_sig_term: True by default
handle_sig_term: true
# relaunch_on_error: False by default, if true, the program will be restart by the parent after exiting;
relaunch_on_error: false
# use_sendfile: True by default, if true, the program
# uses sendfile() system-call to send static files to clients;
use_sendfile: true
# use_gzip: True by default, use gzip to compress the response body's content;
use_gzip: true
# use_brotli: False by default, use brotli to compress the response body's content;
use_brotli: false
# static_files_cache_time: 5 (seconds) by default, the time in which the static file response is cached,
# 0 means cache forever, the negative value means no cache
static_files_cache_time: 5
# simple_controllers_map: Used to configure mapping from path to simple controller
# simple_controllers_map:
# - path: /path/name
# controller: controllerClassName
# http_methods:
# - get
# - post
# filters:
# - FilterClassName
# idle_connection_timeout: Defaults to 60 seconds, the lifetime
# of the connection without read or write
idle_connection_timeout: 60
# server_header_field: Set the 'Server' header field in each response sent by drogon,
# empty string by default with which the 'Server' header field is set to "Server: drogon/version string\r\n"
server_header_field: ''
# enable_server_header: Set true to force drogon to add a 'Server' header to each HTTP response. The default
# value is true.
enable_server_header: true
# enable_date_header: Set true to force drogon to add a 'Date' header to each HTTP response. The default
# value is true.
enable_date_header: true
# keepalive_requests: Set the maximum number of requests that can be served through one keep-alive connection.
# After the maximum number of requests are made, the connection is closed.
# The default value of 0 means no limit.
keepalive_requests: 0
# pipelining_requests: Set the maximum number of unhandled requests that can be cached in pipelining buffer.
# After the maximum number of requests are made, the connection is closed.
# The default value of 0 means no limit.
pipelining_requests: 0
# gzip_static: If it is set to true, when the client requests a static file, drogon first finds the compressed
# file with the extension ".gz" in the same path and send the compressed file to the client.
# The default value of gzip_static is true.
gzip_static: true
# br_static: If it is set to true, when the client requests a static file, drogon first finds the compressed
# file with the extension ".br" in the same path and send the compressed file to the client.
# The default value of br_static is true.
br_static: true
# client_max_body_size: Set the maximum body size of HTTP requests received by drogon. The default value is "1M".
# One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
client_max_body_size: 1M
# max_memory_body_size: Set the maximum body size in memory of HTTP requests received by drogon. The default value is "64K" bytes.
# If the body size of a HTTP request exceeds this limit, the body is stored to a temporary file for processing.
# Setting it to "" means no limit.
client_max_memory_body_size: 64K
# client_max_websocket_message_size: Set the maximum size of messages sent by WebSocket client. The default value is "128K".
# One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
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: false
# enabled_compressed_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.
# Once when receiving and once when decompressing. i.e. if the decompressed body is larger than max_body_size, the request
# will be rejected.
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:
# name: The class name of the plugin
- name: drogon::plugin::PromExporter
# dependencies: Plugins that the plugin depends on. It can be commented out
dependencies: []
# config: The configuration of the plugin. This json object is the parameter to initialize the plugin.
# It can be commented out
config:
path: /metrics
- 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
# custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method.
custom_config:
realm: drogonRealm
opaque: drogonOpaque
credentials:
- user: drogon
password: dr0g0n

View File

@ -1,29 +1,29 @@
FROM ubuntu:22.04
FROM ubuntu:20.04
ENV TZ=UTC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN apt-get update -yqq \
&& apt-get install -yqq --no-install-recommends software-properties-common \
sudo curl wget cmake make pkg-config locales git gcc-11 g++-11 \
sudo curl wget cmake make pkg-config locales git gcc-10 g++-10 \
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/* \
&& locale-gen en_US.UTF-8
ENV LANG=en_US.UTF-8 \
LANGUAGE=en_US:en \
LC_ALL=en_US.UTF-8 \
CC=gcc-11 \
CXX=g++-11 \
AR=gcc-ar-11 \
RANLIB=gcc-ranlib-11 \
CC=gcc-10 \
CXX=g++-10 \
AR=gcc-ar-10 \
RANLIB=gcc-ranlib-10 \
IROOT=/install
ENV DROGON_ROOT="$IROOT/drogon"
ADD https://api.github.com/repos/drogonframework/drogon/git/refs/heads/master $IROOT/version.json
RUN git clone https://github.com/drogonframework/drogon $DROGON_ROOT
ADD https://api.github.com/repos/an-tao/drogon/git/refs/heads/master $IROOT/version.json
RUN git clone https://github.com/an-tao/drogon $DROGON_ROOT
WORKDIR $DROGON_ROOT

View File

@ -17,13 +17,9 @@ add_executable(_drogon_ctl
create.cc
create_view.cc)
target_link_libraries(_drogon_ctl ${PROJECT_NAME})
if (WIN32 AND BUILD_SHARED_LIBS)
if (WIN32 AND BUILD_DROGON_SHARED)
set(DROGON_FILE $<TARGET_FILE:drogon>)
if (USE_SUBMODULE)
set(TRANTOR_FILE $<TARGET_FILE:trantor>)
else()
set(TRANTOR_FILE $<TARGET_FILE:Trantor::Trantor>)
endif()
set(TRANTOR_FILE $<TARGET_FILE:trantor>)
add_custom_command(TARGET _drogon_ctl POST_BUILD
COMMAND ${CMAKE_COMMAND}
-DCTL_FILE=${DROGON_FILE}
@ -57,7 +53,7 @@ target_link_libraries(drogon_ctl PRIVATE ${PROJECT_NAME})
target_include_directories(drogon_ctl PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
add_dependencies(drogon_ctl _drogon_ctl)
if(WIN32)
target_link_libraries(drogon_ctl PRIVATE ws2_32 rpcrt4 iphlpapi)
target_link_libraries(drogon_ctl PRIVATE ws2_32 Rpcrt4)
endif(WIN32)
if(APPLE)
target_link_libraries(drogon_ctl PRIVATE resolv)

View File

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

View File

@ -18,15 +18,14 @@
#include <iostream>
#include <memory>
using namespace drogon_ctl;
std::string create::detail()
{
return "Use create command to create some source files of drogon webapp\n\n"
"Usage:drogon_ctl create <view|controller|filter|project|model> "
"[-options] <object name>\n\n"
"drogon_ctl create view <csp file name> [-o <output path>] [-n "
"<namespace>] [--path-to-namespace] //create HttpView source files "
"from csp files, namespace is prefixed of path-to-namespace\n\n"
"<namespace>]|[--path-to-namespace]//create HttpView source files "
"from csp files\n\n"
"drogon_ctl create controller [-s] <[namespace::]class_name> //"
"create HttpSimpleController source files\n\n"
"drogon_ctl create controller -h <[namespace::]class_name> //"
@ -42,8 +41,7 @@ std::string create::detail()
"create a plugin named class_name\n\n"
"drogon_ctl create project <project_name> //"
"create a project named project_name\n\n"
"drogon_ctl create model <model_path> [-o <output path>] "
"[--table=<table_name>] [-f]//"
"drogon_ctl create model <model_path> [--table=<table_name>] [-f]//"
"create model classes in model_path\n";
}

View File

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

View File

@ -95,11 +95,8 @@ void create_controller::newSimpleControllerHeaderFile(
const std::string &className)
{
file << "#pragma once\n";
file << "\n";
file << "#include <drogon/HttpSimpleController.h>\n";
file << "\n";
file << "using namespace drogon;\n";
file << "\n";
std::string class_name = className;
std::string namepace_path = "/";
auto pos = class_name.find("::");
@ -114,19 +111,19 @@ void create_controller::newSimpleControllerHeaderFile(
file << "{\n";
pos = class_name.find("::");
}
file << "class " << class_name << " : public drogon::HttpSimpleController<"
file << "class " << class_name << ":public drogon::HttpSimpleController<"
<< class_name << ">\n";
file << "{\n";
file << " public:\n";
file << " void asyncHandleHttpRequest(const HttpRequestPtr& "
file << " virtual void asyncHandleHttpRequest(const HttpRequestPtr& "
"req, std::function<void (const HttpResponsePtr &)> &&callback) "
"override;\n";
file << " PATH_LIST_BEGIN\n";
file << " // list path definitions here;\n";
file << " //list path definitions here;\n";
file << " "
"// PATH_ADD(\"/"
"path\", \"filter1\", \"filter2\", HttpMethod1, HttpMethod2...);\n";
"//PATH_ADD(\"/"
"path\",\"filter1\",\"filter2\",HttpMethod1,HttpMethod2...);\n";
file << " PATH_LIST_END\n";
file << "};\n";
while (namespaceCount > 0)
@ -135,29 +132,26 @@ void create_controller::newSimpleControllerHeaderFile(
file << "}\n";
}
}
void create_controller::newSimpleControllerSourceFile(
std::ofstream &file,
const std::string &className,
const std::string &filename)
{
file << "#include \"" << filename << ".h\"\n";
file << "\n";
auto pos = className.rfind("::");
auto class_name = className;
if (pos != std::string::npos)
{
auto namespacename = className.substr(0, pos);
file << "using namespace " << namespacename << ";\n";
file << "\n";
class_name = className.substr(pos + 2);
}
file << "void " << class_name
<< "::asyncHandleHttpRequest(const HttpRequestPtr& req, "
"std::function<void (const HttpResponsePtr &)> &&callback)\n";
file << "{\n";
file << " // write your application logic here\n";
file << "}\n";
file << " //write your application logic here\n";
file << "}";
}
void create_controller::newWebsockControllerHeaderFile(
@ -165,11 +159,8 @@ void create_controller::newWebsockControllerHeaderFile(
const std::string &className)
{
file << "#pragma once\n";
file << "\n";
file << "#include <drogon/WebSocketController.h>\n";
file << "\n";
file << "using namespace drogon;\n";
file << "\n";
std::string class_name = className;
std::string namepace_path = "/";
auto pos = class_name.find("::");
@ -184,22 +175,23 @@ void create_controller::newWebsockControllerHeaderFile(
file << "{\n";
pos = class_name.find("::");
}
file << "class " << class_name << " : public drogon::WebSocketController<"
file << "class " << class_name << ":public drogon::WebSocketController<"
<< class_name << ">\n";
file << "{\n";
file << " public:\n";
file << " void handleNewMessage(const WebSocketConnectionPtr&,\n";
file
<< " virtual void handleNewMessage(const WebSocketConnectionPtr&,\n";
file << " std::string &&,\n";
file << " const WebSocketMessageType &) "
"override;\n";
file << " void handleNewConnection(const HttpRequestPtr &,\n";
file << " virtual void handleNewConnection(const HttpRequestPtr &,\n";
file << " const "
"WebSocketConnectionPtr&) override;\n";
file << " void handleConnectionClosed(const "
"WebSocketConnectionPtr&) override;\n";
"WebSocketConnectionPtr&)override;\n";
file << " virtual void handleConnectionClosed(const "
"WebSocketConnectionPtr&)override;\n";
file << " WS_PATH_LIST_BEGIN\n";
file << " // list path definitions here;\n";
file << " // WS_PATH_ADD(\"/path\", \"filter1\", \"filter2\", ...);\n";
file << " //list path definitions here;\n";
file << " //WS_PATH_ADD(\"/path\",\"filter1\",\"filter2\",...);\n";
file << " WS_PATH_LIST_END\n";
file << "};\n";
while (namespaceCount > 0)
@ -208,42 +200,37 @@ void create_controller::newWebsockControllerHeaderFile(
file << "}\n";
}
}
void create_controller::newWebsockControllerSourceFile(
std::ofstream &file,
const std::string &className,
const std::string &filename)
{
file << "#include \"" << filename << ".h\"\n";
file << "\n";
auto pos = className.rfind("::");
auto class_name = className;
if (pos != std::string::npos)
{
auto namespacename = className.substr(0, pos);
file << "using namespace " << namespacename << ";\n";
file << "\n";
class_name = className.substr(pos + 2);
}
file << "void " << class_name
<< "::handleNewMessage(const WebSocketConnectionPtr& wsConnPtr, "
"std::string &&message, const WebSocketMessageType &type)\n";
file << "{\n";
file << " // write your application logic here\n";
file << " //write your application logic here\n";
file << "}\n";
file << "\n";
file << "void " << class_name
<< "::handleNewConnection(const HttpRequestPtr &req, const "
<< "::handleNewConnection(const HttpRequestPtr &req,const "
"WebSocketConnectionPtr& wsConnPtr)\n";
file << "{\n";
file << " // write your application logic here\n";
file << " //write your application logic here\n";
file << "}\n";
file << "\n";
file << "void " << class_name
<< "::handleConnectionClosed(const WebSocketConnectionPtr& "
"wsConnPtr)\n";
file << "{\n";
file << " // write your application logic here\n";
file << " //write your application logic here\n";
file << "}\n";
}
@ -252,11 +239,8 @@ void create_controller::newHttpControllerHeaderFile(
const std::string &className)
{
file << "#pragma once\n";
file << "\n";
file << "#include <drogon/HttpController.h>\n";
file << "\n";
file << "using namespace drogon;\n";
file << "\n";
std::string class_name = className;
std::string namepace_path = "/";
auto pos = class_name.find("::");
@ -271,33 +255,33 @@ void create_controller::newHttpControllerHeaderFile(
file << "{\n";
pos = class_name.find("::");
}
file << "class " << class_name << " : public drogon::HttpController<"
file << "class " << class_name << ":public drogon::HttpController<"
<< class_name << ">\n";
file << "{\n";
file << " public:\n";
file << " METHOD_LIST_BEGIN\n";
file << " // use METHOD_ADD to add your custom processing function "
file << " //use METHOD_ADD to add your custom processing function "
"here;\n";
file << " // METHOD_ADD(" << class_name
<< "::get, \"/{2}/{1}\", Get);"
" // path is "
file << " //METHOD_ADD(" << class_name
<< "::get,\"/{2}/{1}\",Get);"
"//path is "
<< namepace_path << class_name << "/{arg2}/{arg1}\n";
file << " // METHOD_ADD(" << class_name
<< "::your_method_name, \"/{1}/{2}/list\", Get);"
" // path is "
file << " //METHOD_ADD(" << class_name
<< "::your_method_name,\"/{1}/{2}/list\",Get);"
"//path is "
<< namepace_path << class_name << "/{arg1}/{arg2}/list\n";
file << " // ADD_METHOD_TO(" << class_name
<< "::your_method_name, \"/absolute/path/{1}/{2}/list\", Get);"
" // path is /absolute/path/{arg1}/{arg2}/list\n";
file << " //ADD_METHOD_TO(" << class_name
<< "::your_method_name,\"/absolute/path/{1}/{2}/list\",Get);"
"//path is /absolute/path/{arg1}/{arg2}/list\n";
file << "\n";
file << " METHOD_LIST_END\n";
file << " // your declaration of processing function maybe like this:\n";
file << " // void get(const HttpRequestPtr& req, "
"std::function<void (const HttpResponsePtr &)> &&callback, int "
"p1, std::string p2);\n";
file << " // void your_method_name(const HttpRequestPtr& req, "
"std::function<void (const HttpResponsePtr &)> &&callback, double "
"p1, int p2) const;\n";
file << " // void get(const HttpRequestPtr& req,"
"std::function<void (const HttpResponsePtr &)> &&callback,int "
"p1,std::string p2);\n";
file << " // void your_method_name(const HttpRequestPtr& req,"
"std::function<void (const HttpResponsePtr &)> &&callback,double "
"p1,int p2) const;\n";
file << "};\n";
while (namespaceCount > 0)
{
@ -305,25 +289,22 @@ void create_controller::newHttpControllerHeaderFile(
file << "}\n";
}
}
void create_controller::newHttpControllerSourceFile(
std::ofstream &file,
const std::string &className,
const std::string &filename)
{
file << "#include \"" << filename << ".h\"\n";
file << "\n";
auto pos = className.rfind("::");
auto class_name = className;
if (pos != std::string::npos)
{
auto namespacename = className.substr(0, pos);
file << "using namespace " << namespacename << ";\n";
file << "\n";
class_name = className.substr(pos + 2);
}
file << "// Add definition of your processing function here\n";
file << "//add definition of your processing function here\n";
}
void create_controller::createController(std::vector<std::string> &httpClasses,
@ -379,21 +360,20 @@ void create_controller::createController(const std::string &className,
}
if (type == Http)
{
std::cout << "Create a http controller: " << className << std::endl;
std::cout << "create a http controller:" << className << std::endl;
newHttpControllerHeaderFile(oHeadFile, className);
newHttpControllerSourceFile(oSourceFile, className, ctlName);
}
else if (type == Simple)
{
std::cout << "Create a http simple controller: " << className
std::cout << "create a http simple controller:" << className
<< std::endl;
newSimpleControllerHeaderFile(oHeadFile, className);
newSimpleControllerSourceFile(oSourceFile, className, ctlName);
}
else if (type == WebSocket)
{
std::cout << "Create a websocket controller: " << className
<< std::endl;
std::cout << "create a websocket controller:" << className << std::endl;
newWebsockControllerHeaderFile(oHeadFile, className);
newWebsockControllerSourceFile(oSourceFile, className, ctlName);
}
@ -464,8 +444,8 @@ void create_controller::createARestfulController(const std::string &className,
std::cerr << err.what() << std::endl;
exit(1);
}
std::cout << "Create a http restful API controller: " << className
std::cout << "create a http restful API controller:" << className
<< std::endl;
std::cout << "File name: " << ctlName << ".h and " << ctlName << ".cc"
std::cout << "file name: " << ctlName << ".h and " << ctlName << ".cc"
<< std::endl;
}
}

View File

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

View File

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

View File

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

View File

@ -36,13 +36,10 @@
using namespace std::chrono_literals;
using namespace drogon_ctl;
static std::string toLower(const std::string &str)
{
auto ret = str;
std::transform(ret.begin(), ret.end(), ret.begin(), [](unsigned char c) {
return tolower(c);
});
std::transform(ret.begin(), ret.end(), ret.begin(), tolower);
return ret;
}
@ -66,17 +63,6 @@ static std::string escapeConnString(const std::string &str)
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(
const Json::Value &convertColumns)
{
@ -177,7 +163,7 @@ void create_model::createModelClassFromPG(
auto className = nameTransform(tableName, true);
HttpViewData data;
data["className"] = className;
data["tableName"] = tableName;
data["tableName"] = toLower(tableName);
data["hasPrimaryKey"] = (int)0;
data["primaryKeyName"] = "";
data["dbName"] = dbname_;
@ -189,10 +175,10 @@ void create_model::createModelClassFromPG(
data["schema"] = schema;
}
std::vector<ColumnInfo> cols;
*client << "SELECT * "
"FROM information_schema.columns "
"WHERE table_schema = $1 "
"AND table_name = $2"
*client << "SELECT * \
FROM information_schema.columns \
WHERE table_schema = $1 \
AND table_name = $2"
<< schema << tableName << Mode::Blocking >>
[&](const Result &r) {
if (r.size() == 0)
@ -295,14 +281,14 @@ void create_model::createModelClassFromPG(
exit(1);
};
size_t pkNumber = 0;
*client << "SELECT "
"pg_constraint.conname AS pk_name,"
"pg_constraint.conkey AS pk_vector "
"FROM pg_constraint "
"INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid "
"WHERE "
"pg_class.relname = $1 "
"AND pg_constraint.contype = 'p'"
*client << "SELECT \
pg_constraint.conname AS pk_name,\
pg_constraint.conkey AS pk_vector \
FROM pg_constraint \
INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid \
WHERE \
pg_class.relname = $1 \
AND pg_constraint.contype = 'p'"
<< tableName << Mode::Blocking >>
[&](bool isNull,
const std::string &pkName,
@ -319,18 +305,16 @@ void create_model::createModelClassFromPG(
data["hasPrimaryKey"] = (int)pkNumber;
if (pkNumber == 1)
{
*client << "SELECT "
"pg_attribute.attname AS colname,"
"pg_type.typname AS typename,"
"pg_constraint.contype AS contype "
"FROM pg_constraint "
"INNER JOIN pg_class ON pg_constraint.conrelid = "
"pg_class.oid "
"INNER JOIN pg_attribute ON pg_attribute.attrelid = "
"pg_class.oid "
"AND pg_attribute.attnum = pg_constraint.conkey [ 1 ] "
"INNER JOIN pg_type ON pg_type.oid = pg_attribute.atttypid "
"WHERE pg_class.relname = $1 and pg_constraint.contype='p'"
*client << "SELECT \
pg_attribute.attname AS colname,\
pg_type.typname AS typename,\
pg_constraint.contype AS contype \
FROM pg_constraint \
INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid \
INNER JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid \
AND pg_attribute.attnum = pg_constraint.conkey [ 1 ] \
INNER JOIN pg_type ON pg_type.oid = pg_attribute.atttypid \
WHERE pg_class.relname = $1 and pg_constraint.contype='p'"
<< tableName << Mode::Blocking >>
[&](bool isNull,
const std::string &colName,
@ -358,20 +342,16 @@ void create_model::createModelClassFromPG(
std::vector<std::string> pkNames, pkTypes, pkValNames;
for (size_t i = 1; i <= pkNumber; ++i)
{
*client << "SELECT "
"pg_attribute.attname AS colname,"
"pg_type.typname AS typename,"
"pg_constraint.contype AS contype "
"FROM pg_constraint "
"INNER JOIN pg_class ON pg_constraint.conrelid = "
"pg_class.oid "
"INNER JOIN pg_attribute ON pg_attribute.attrelid = "
"pg_class.oid "
"AND pg_attribute.attnum = pg_constraint.conkey [ $1 ] "
"INNER JOIN pg_type ON pg_type.oid = "
"pg_attribute.atttypid "
"WHERE pg_class.relname = $2 and "
"pg_constraint.contype='p'"
*client << "SELECT \
pg_attribute.attname AS colname,\
pg_type.typname AS typename,\
pg_constraint.contype AS contype \
FROM pg_constraint \
INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid \
INNER JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid \
AND pg_attribute.attnum = pg_constraint.conkey [ $1 ] \
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 >>
[&](bool isNull, std::string colName, const std::string &type) {
if (isNull)
@ -407,7 +387,6 @@ void create_model::createModelClassFromPG(
sourceFile << templ->genText(data);
createRestfulAPIController(data, restfulApiConfig);
}
void create_model::createModelFromPG(
const std::string &path,
const DbClientPtr &client,
@ -471,7 +450,7 @@ void create_model::createModelClassFromMysql(
data["convertMethods"] = convertMethods;
std::vector<ColumnInfo> cols;
int i = 0;
*client << "desc `" + tableName + "`" << Mode::Blocking >>
*client << "desc " + tableName << Mode::Blocking >>
[&i, &cols](bool isNull,
const std::string &field,
const std::string &type,
@ -500,11 +479,6 @@ void create_model::createModelClassFromMysql(
info.colType_ = "int16_t";
info.colLength_ = 2;
}
else if (type.find("mediumint") == 0)
{
info.colType_ = "int32_t";
info.colLength_ = 3;
}
else if (type.find("int") == 0)
{
info.colType_ = "int32_t";
@ -603,7 +577,6 @@ void create_model::createModelClassFromMysql(
sourceFile << templ->genText(data);
createRestfulAPIController(data, restfulApiConfig);
}
void create_model::createModelFromMysql(
const std::string &path,
const DbClientPtr &client,
@ -657,10 +630,7 @@ void create_model::createModelClassFromSqlite3(
bool notnull = row["notnull"].as<bool>();
bool primary = row["pk"].as<int>();
auto type = row["type"].as<std::string>();
std::transform(type.begin(),
type.end(),
type.begin(),
[](unsigned char c) { return tolower(c); });
std::transform(type.begin(), type.end(), type.begin(), tolower);
ColumnInfo info;
info.index_ = index++;
info.dbType_ = "sqlite3";
@ -687,9 +657,7 @@ void create_model::createModelClassFromSqlite3(
std::transform(sql.begin(),
sql.end(),
sql.begin(),
[](unsigned char c) {
return tolower(c);
});
tolower);
if (sql.find("autoincrement") !=
std::string::npos)
{
@ -710,7 +678,7 @@ void create_model::createModelClassFromSqlite3(
if (type.find("int") != std::string::npos)
{
info.colType_ = "int64_t";
info.colType_ = "uint64_t";
info.colLength_ = 8;
}
else if (type.find("char") != std::string::npos || type == "text" ||
@ -765,11 +733,6 @@ void create_model::createModelClassFromSqlite3(
}
else if (pkNames.size() > 1)
{
for (auto &col : cols)
{
col.isAutoVal_ = false;
}
data["primaryKeyName"] = pkNames;
data["primaryKeyType"] = pkTypes;
data["primaryKeyValNames"] = pkValNames;
@ -819,14 +782,10 @@ void create_model::createModel(const std::string &path,
const std::string &singleModelName)
{
auto dbType = config.get("rdbms", "no dbms").asString();
std::transform(dbType.begin(),
dbType.end(),
dbType.begin(),
[](unsigned char c) { return tolower(c); });
std::transform(dbType.begin(), dbType.end(), dbType.begin(), tolower);
auto restfulApiConfig = config["restful_api_controllers"];
auto relationships = getRelationships(config["relationships"]);
auto convertMethods = getConvertMethods(config["convert"]);
drogon::utils::createPath(path);
if (dbType == "postgresql")
{
#if USE_POSTGRESQL
@ -910,7 +869,7 @@ void create_model::createModel(const std::string &path,
std::transform(tableName.begin(),
tableName.end(),
tableName.begin(),
[](unsigned char c) { return tolower(c); });
tolower);
std::cout << "table name:" << tableName << std::endl;
createModelClassFromPG(path,
client,
@ -1019,7 +978,7 @@ void create_model::createModel(const std::string &path,
std::transform(tableName.begin(),
tableName.end(),
tableName.begin(),
[](unsigned char c) { return tolower(c); });
tolower);
std::cout << "table name:" << tableName << std::endl;
createModelClassFromMysql(path,
client,
@ -1094,7 +1053,7 @@ void create_model::createModel(const std::string &path,
std::transform(tableName.begin(),
tableName.end(),
tableName.begin(),
[](unsigned char c) { return tolower(c); });
tolower);
std::cout << "table name:" << tableName << std::endl;
createModelClassFromSqlite3(path,
client,
@ -1133,7 +1092,6 @@ void create_model::createModel(const std::string &path,
exit(1);
}
}
void create_model::createModel(const std::string &path,
const std::string &singleModelName)
{
@ -1174,9 +1132,7 @@ void create_model::createModel(const std::string &path,
try
{
infile >> configJsonRoot;
createModel(outputPath_.empty() ? path : outputPath_,
configJsonRoot,
singleModelName);
createModel(path, configJsonRoot, singleModelName);
}
catch (const std::exception &exception)
{
@ -1214,22 +1170,6 @@ void create_model::handleCommand(std::vector<std::string> &parameters)
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)
{
createModel(path, singleModelName);
@ -1254,10 +1194,7 @@ void create_model::createRestfulAPIController(
restfulApiConfig.get("resource_uri", "/*").asString(),
regex,
modelClassName);
std::transform(resource.begin(),
resource.end(),
resource.begin(),
[](unsigned char c) { return tolower(c); });
std::transform(resource.begin(), resource.end(), resource.begin(), tolower);
auto ctrlClassName =
std::regex_replace(restfulApiConfig.get("class_name", "/*").asString(),
regex,

View File

@ -47,9 +47,7 @@ struct ColumnInfo
inline std::string nameTransform(const std::string &origName, bool isType)
{
auto str = origName;
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) {
return tolower(c);
});
std::transform(str.begin(), str.end(), str.begin(), tolower);
std::string::size_type startPos = 0;
std::string::size_type pos;
std::string ret;
@ -77,15 +75,10 @@ inline std::string nameTransform(const std::string &origName, bool isType)
ret[0] += ('A' - 'a');
return ret;
}
std::string escapeIdentifier(const std::string &identifier,
const std::string &rdbms);
class PivotTable
{
public:
PivotTable() = default;
PivotTable(const Json::Value &json)
: tableName_(json.get("table_name", "").asString())
{
@ -104,7 +97,6 @@ class PivotTable
throw std::runtime_error("target_key can't be empty");
}
}
PivotTable reverse() const
{
PivotTable pivot;
@ -113,17 +105,14 @@ class PivotTable
pivot.targetKey_ = originalKey_;
return pivot;
}
const std::string &tableName() const
{
return tableName_;
}
const std::string &originalKey() const
{
return originalKey_;
}
const std::string &targetKey() const
{
return targetKey_;
@ -169,7 +158,6 @@ class ConvertMethod
includeFiles_.push_back(i.asString());
} // for
}
ConvertMethod() = default;
bool shouldConvert(const std::string &tableName,
@ -179,22 +167,18 @@ class ConvertMethod
{
return tableName_;
}
const std::string &colName() const
{
return colName_;
}
const std::string &methodBeforeDbWrite() const
{
return methodBeforeDbWrite_;
}
const std::string &methodAfterDbRead() const
{
return methodAfterDbRead_;
}
const std::vector<std::string> &includeFiles() const
{
return includeFiles_;
@ -217,7 +201,6 @@ class Relationship
HasMany,
ManyToMany
};
Relationship(const Json::Value &relationship)
{
auto type = relationship.get("type", "has one").asString();
@ -279,9 +262,7 @@ class Relationship
pivotTable_ = PivotTable(pivot);
}
}
Relationship() = default;
Relationship reverse() const
{
Relationship r;
@ -303,47 +284,38 @@ class Relationship
r.pivotTable_ = pivotTable_.reverse();
return r;
}
Type type() const
{
return type_;
}
bool enableReverse() const
{
return enableReverse_;
}
const std::string &originalTableName() const
{
return originalTableName_;
}
const std::string &originalTableAlias() const
{
return originalTableAlias_;
}
const std::string &originalKey() const
{
return originalKey_;
}
const std::string &targetTableName() const
{
return targetTableName_;
}
const std::string &targetTableAlias() const
{
return targetTableAlias_;
}
const std::string &targetKey() const
{
return targetKey_;
}
const PivotTable &pivotTable() const
{
return pivotTable_;
@ -360,13 +332,11 @@ class Relationship
bool enableReverse_{false};
PivotTable pivotTable_;
};
class create_model : public DrObject<create_model>, public CommandHandler
{
public:
void handleCommand(std::vector<std::string> &parameters) override;
std::string script() override
virtual void handleCommand(std::vector<std::string> &parameters) override;
virtual std::string script() override
{
return "create Model classes files";
}
@ -429,6 +399,5 @@ class create_model : public DrObject<create_model>, public CommandHandler
const Json::Value &restfulApiConfig);
std::string dbname_;
bool forceOverwrite_{false};
std::string outputPath_;
};
} // namespace drogon_ctl

View File

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

View File

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

View File

@ -38,7 +38,6 @@ void create_project::handleCommand(std::vector<std::string> &parameters)
auto pName = parameters[0];
createProject(pName);
}
static void newCmakeFile(std::ofstream &cmakeFile,
const std::string &projectName)
{
@ -47,43 +46,32 @@ static void newCmakeFile(std::ofstream &cmakeFile,
auto templ = DrTemplateBase::newTemplate("cmake.csp");
cmakeFile << templ->genText(data);
}
static void newMainFile(std::ofstream &mainFile)
{
auto templ = DrTemplateBase::newTemplate("demoMain");
mainFile << templ->genText();
}
static void newGitIgFile(std::ofstream &gitFile)
{
auto templ = DrTemplateBase::newTemplate("gitignore.csp");
gitFile << templ->genText();
}
static void newConfigJsonFile(std::ofstream &configJsonFile)
static void newConfigFile(std::ofstream &configFile)
{
auto templ = DrTemplateBase::newTemplate("config_json");
configJsonFile << templ->genText();
auto templ = DrTemplateBase::newTemplate("config");
configFile << templ->genText();
}
static void newConfigYamlFile(std::ofstream &configYamlFile)
{
auto templ = DrTemplateBase::newTemplate("config_yaml");
configYamlFile << templ->genText();
}
static void newModelConfigFile(std::ofstream &configFile)
{
auto templ = DrTemplateBase::newTemplate("model_json");
configFile << templ->genText();
}
static void newTestMainFile(std::ofstream &mainFile)
{
auto templ = DrTemplateBase::newTemplate("test_main");
mainFile << templ->genText();
}
static void newTestCmakeFile(std::ofstream &testCmakeFile,
const std::string &projectName)
{
@ -92,7 +80,6 @@ static void newTestCmakeFile(std::ofstream &testCmakeFile,
auto templ = DrTemplateBase::newTemplate("test_cmake");
testCmakeFile << templ->genText(data);
}
void create_project::createProject(const std::string &projectName)
{
#ifdef _WIN32
@ -130,10 +117,8 @@ void create_project::createProject(const std::string &projectName)
std::ofstream gitFile(".gitignore", std::ofstream::out);
newGitIgFile(gitFile);
std::ofstream configJsonFile("config.json", std::ofstream::out);
newConfigJsonFile(configJsonFile);
std::ofstream configYamlFile("config.yaml", std::ofstream::out);
newConfigYamlFile(configYamlFile);
std::ofstream configFile("config.json", std::ofstream::out);
newConfigFile(configFile);
std::ofstream modelConfigFile("models/model.json", std::ofstream::out);
newModelConfigFile(modelConfigFile);
std::ofstream testMainFile("test/test_main.cc", std::ofstream::out);

View File

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

View File

@ -53,7 +53,6 @@ static std::string &replace_all(std::string &str,
}
return str;
}
static void parseCxxLine(std::ofstream &oSrcFile,
const std::string &line,
const std::string &streamName,
@ -67,7 +66,6 @@ static void parseCxxLine(std::ofstream &oSrcFile,
oSrcFile << tmp << "\n";
}
}
static void outputVal(std::ofstream &oSrcFile,
const std::string &streamName,
const std::string &viewDataName,
@ -78,12 +76,12 @@ static void outputVal(std::ofstream &oSrcFile,
<< "\"];\n";
oSrcFile << " if(val.type()==typeid(const char *)){\n";
oSrcFile << " " << streamName
<< "<<*(std::any_cast<const char *>(&val));\n";
<< "<<*any_cast<const char *>(&val);\n";
oSrcFile << " }else "
"if(val.type()==typeid(std::string)||val.type()==typeid(const "
"std::string)){\n";
oSrcFile << " " << streamName
<< "<<*(std::any_cast<const std::string>(&val));\n";
<< "<<*any_cast<const std::string>(&val);\n";
oSrcFile << " }\n";
oSrcFile << "}\n";
}
@ -120,7 +118,7 @@ static void parseLine(std::ofstream &oSrcFile,
{
// std::cout<<"blank line!"<<std::endl;
// std::cout<<streamName<<"<<\"\\n\";\n";
if (returnFlag && !cxx_flag)
if (returnFlag)
oSrcFile << streamName << "<<\"\\n\";\n";
return;
}
@ -288,7 +286,6 @@ void create_view::handleCommand(std::vector<std::string> &parameters)
}
createViewFiles(parameters);
}
void create_view::createViewFiles(std::vector<std::string> &cspFileNames)
{
for (auto const &file : cspFileNames)
@ -298,7 +295,6 @@ void create_view::createViewFiles(std::vector<std::string> &cspFileNames)
exit(1);
}
}
int create_view::createViewFile(const std::string &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;
}
void create_view::newViewHeaderFile(std::ofstream &file,
const std::string &className)
{
@ -411,7 +406,6 @@ void create_view::newViewSourceFile(std::ofstream &file,
"automatically,don't modify it!\n";
file << "#include \"" << namespacePrefix << className << ".h\"\n";
file << "#include <drogon/utils/OStringStream.h>\n";
file << "#include <drogon/utils/Utilities.h>\n";
file << "#include <string>\n";
file << "#include <map>\n";
file << "#include <vector>\n";
@ -452,7 +446,7 @@ void create_view::newViewSourceFile(std::ofstream &file,
std::transform(lowerBuffer.begin(),
lowerBuffer.end(),
lowerBuffer.begin(),
[](unsigned char c) { return tolower(c); });
::tolower);
if ((pos = lowerBuffer.find(cxx_include)) != std::string::npos)
{
// std::cout<<"haha find it!"<<endl;

View File

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

View File

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

View File

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

View File

@ -18,17 +18,12 @@
#include <iostream>
#include <memory>
#include <iomanip>
#include <cstdlib>
#include <json/json.h>
#include <fstream>
#include <string>
#include <unordered_map>
#include <stdlib.h>
#ifndef _WIN32
#include <unistd.h>
#endif
using namespace drogon_ctl;
std::string press::detail()
{
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"
" -t num number of threads(default : 1)\n"
" -c num concurrent connections(default : 1)\n"
" -k disable SSL certificate validation(default: enable)\n"
" -f customize http request json file(default: disenable)\n"
" -q no progress indication(default: show)\n\n"
// " -k keep alive(default: no)\n"
" -q no progress indication(default: no)\n\n"
"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;
exit(1);
}
void press::handleCommand(std::vector<std::string> &parameters)
{
for (auto iter = parameters.begin(); iter != parameters.end(); iter++)
@ -156,29 +149,11 @@ void press::handleCommand(std::vector<std::string> &parameters)
continue;
}
}
else if (param.find("-f") == 0)
{
if (param == "-f")
{
++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 == "-k")
// {
// keepAlive_ = true;
// continue;
// }
else if (param == "-q")
{
processIndication_ = false;
@ -201,7 +176,7 @@ void press::handleCommand(std::vector<std::string> &parameters)
else
{
auto pos = url_.find("://");
auto posOfPath = url_.find('/', pos + 3);
auto posOfPath = url_.find("/", pos + 3);
if (posOfPath == std::string::npos)
{
host_ = url_;
@ -213,118 +188,6 @@ void press::handleCommand(std::vector<std::string> &parameters)
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 << "path=" << path_ << std::endl;
doTesting();
@ -351,10 +214,8 @@ void press::createRequestAndClients()
loopPool_->start();
for (size_t i = 0; i < numOfConnections_; ++i)
{
auto client = HttpClient::newHttpClient(host_,
loopPool_->getNextLoop(),
false,
certValidation_);
auto client =
HttpClient::newHttpClient(host_, loopPool_->getNextLoop());
client->enableCookies();
clients_.push_back(client);
}
@ -367,19 +228,9 @@ void press::sendRequest(const HttpClientPtr &client)
{
return;
}
HttpRequestPtr request;
if (createHttpRequestFunc_)
{
request = createHttpRequestFunc_();
}
else
{
request = HttpRequest::newHttpRequest();
request->setPath(path_);
request->setMethod(Get);
}
auto request = HttpRequest::newHttpRequest();
request->setPath(path_);
request->setMethod(Get);
// std::cout << "send!" << std::endl;
client->sendRequest(
request,
@ -431,6 +282,7 @@ void press::sendRequest(const HttpClientPtr &client)
void press::outputResults()
{
static std::mutex mtx;
size_t totalSent = 0;
size_t totalRecv = 0;
for (auto &client : clients_)
@ -442,7 +294,7 @@ void press::outputResults()
auto microSecs = now.microSecondsSinceEpoch() -
statistics_.startDate_.microSecondsSinceEpoch();
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 << "TOTALS: " << numOfConnections_ << " connect, "
<< numOfRequests_ << " requests, "

View File

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

View File

@ -34,7 +34,10 @@ target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)
# ##############################################################################
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)
message(STATUS "use c++17")
else ()
@ -51,10 +54,6 @@ drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views
# 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}

View File

@ -1,14 +1,77 @@
/* This is a JSON format configuration file
*/
{
/*
//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`.
"ssl": {
"cert": "../../trantor/trantor/tests/server.pem",
"key": "../../trantor/trantor/tests/server.pem",
"conf": [
//["Options", "-SessionTicket"],
//["Options", "Compression"]
]
},
// 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.
"mime": {
// "text/markdown": "md",
// "text/gemini": ["gmi", "gemini"]
},
"listeners": [
{
//address: Ip address,0.0.0.0 by default
"address": "0.0.0.0",
//port: Port number
"port": 8848,
"port": 80,
//https: If true, use https for security,false by default
"https": false
},
{
"address": "0.0.0.0",
"port": 443,
"https": true,
//cert,key: Cert file path and key file path, empty by default,
//if empty, use the global setting
"cert": "",
"key": "",
//use_old_tls: enable the TLS1.0/1.1, false by default
"use_old_tls": false,
"ssl_conf": [
//["MinProtocol", "TLSv1.3"]
]
}
],
"db_clients": [
{
//name: Name of the client,'default' by default
//"name":"",
//rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default
"rdbms": "postgresql",
//filename: Sqlite3 db file name
//"filename":"",
//host: Server address,localhost by default
"host": "127.0.0.1",
//port: Server port, 5432 by default
"port": 5432,
//dbname: Database name
"dbname": "test",
//user: 'postgres' by default
"user": "",
//passwd: '' by default
"passwd": "",
//is_fast: false by default, if it is true, the client is faster but user can't call
//any synchronous interface of it.
"is_fast": false,
//client_encoding: The character set used by the client. it is empty string by default which
//means use the default character set.
//"client_encoding": "",
//number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
//connections per IO thread, otherwise it is the total number of all connections.
"number_of_connections": 1,
//timeout: -1.0 by default, in seconds, the timeout for executing a SQL query.
//zero or negative value means no timeout.
"timeout": -1.0
}
],
"redis_clients": [
@ -25,7 +88,7 @@
"db": 0,
//is_fast: false by default, if it is true, the client is faster but user can't call
//any synchronous interface of it.
"is_fast": true,
"is_fast": false,
//number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
//connections per IO thread, otherwise it is the total number of all connections.
"number_of_connections": 1,
@ -33,7 +96,7 @@
//zero or negative value means no timeout.
"timeout": -1.0
}
],
],*/
"app": {
//number_of_threads: The number of IO threads, 1 by default, if the value is set to 0, the number of threads
//is the number of CPU cores
@ -41,11 +104,7 @@
//enable_session: False by default
"enable_session": false,
"session_timeout": 0,
//session_cookie_key: The cookie key of the session, "JSESSIONID" by default
"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: Root path of HTTP document, defaut path is ./
"document_root": "./",
//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
@ -111,7 +170,7 @@
],
//max_connections: maximum number of connections, 100000 by default
"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,
//Load_dynamic_views: False by default, when set to true, drogon
//compiles and loads dynamically "CSP View Files" in directories defined
@ -169,6 +228,17 @@
"static_files_cache_time": 5,
//simple_controllers_map: Used to configure mapping from path to simple controller
"simple_controllers_map": [
{
"path": "/path/name",
"controller": "controllerClassName",
"http_methods": [
"get",
"post"
],
"filters": [
"FilterClassName"
]
}
],
//idle_connection_timeout: Defaults to 60 seconds, the lifetime
//of the connection without read or write

View File

@ -1,350 +0,0 @@
/* This is a JSON format configuration file
*/
{
/*
//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`.
"ssl": {
"cert": "../../trantor/trantor/tests/server.crt",
"key": "../../trantor/trantor/tests/server.key",
"conf": [
//["Options", "-SessionTicket"],
//["Options", "Compression"]
]
},
"listeners": [
{
//address: Ip address,0.0.0.0 by default
"address": "0.0.0.0",
//port: Port number
"port": 80,
//https: If true, use https for security,false by default
"https": false
},
{
"address": "0.0.0.0",
"port": 443,
"https": true,
//cert,key: Cert file path and key file path, empty by default,
//if empty, use the global setting
"cert": "",
"key": "",
//use_old_tls: enable the TLS1.0/1.1, false by default
"use_old_tls": false,
"ssl_conf": [
//["MinProtocol", "TLSv1.3"]
]
}
],
"db_clients": [
{
//name: Name of the client,'default' by default
"name": "default",
//rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default
"rdbms": "postgresql",
//filename: Sqlite3 db file name
//"filename":"",
//host: Server address,localhost by default
"host": "127.0.0.1",
//port: Server port, 5432 by default
"port": 5432,
//dbname: Database name
"dbname": "test",
//user: 'postgres' by default
"user": "",
//passwd: '' by default
"passwd": "",
//is_fast: false by default, if it is true, the client is faster but user can't call
//any synchronous interface of it.
"is_fast": false,
//client_encoding: The character set used by the client. it is empty string by default which
//means use the default character set.
//"client_encoding": "",
//number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
//connections per IO thread, otherwise it is the total number of all connections.
"number_of_connections": 1,
//timeout: -1.0 by default, in seconds, the timeout for executing a SQL query.
//zero or negative value means no timeout.
"timeout": -1.0,
//auto_batch: this feature is only available for the PostgreSQL driver(version >= 14.0), see
//the wiki for more details.
"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": [
{
//name: Name of the client,'default' by default
"name": "default",
//host: Server IP, 127.0.0.1 by default
"host": "127.0.0.1",
//port: Server port, 6379 by default
"port": 6379,
//username: '' by default which means 'default' in redis ACL
"username": "",
//passwd: '' by default
"passwd": "",
//db index: 0 by default
"db": 0,
//is_fast: false by default, if it is true, the client is faster but user can't call
//any synchronous interface of it.
"is_fast": false,
//number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
//connections per IO thread, otherwise it is the total number of all connections.
"number_of_connections": 1,
//timeout: -1.0 by default, in seconds, the timeout for executing a command.
//zero or negative value means no timeout.
"timeout": -1.0
}
],*/
"app": {
//number_of_threads: The number of IO threads, 1 by default, if the value is set to 0, the number of threads
//is the number of CPU cores
"number_of_threads": 1,
//enable_session: False by default
"enable_session": false,
"session_timeout": 0,
//string value of SameSite attribute of the Set-Cookie HTTP response header
//valid value is either 'Null' (default), 'Lax', 'Strict' or 'None'
"session_same_site" : "Null",
//session_cookie_key: The cookie key of the session, "JSESSIONID" by default
"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": "./",
//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
//to the request for "/".
"home_page": "index.html",
//use_implicit_page: enable implicit pages if true, true by default
"use_implicit_page": true,
//implicit_page: Set the file which would the server access in a directory that a user accessed.
//For example, by default, http://localhost/a-directory resolves to http://localhost/a-directory/index.html.
"implicit_page": "index.html",
//static_file_headers: Headers for static files
/*"static_file_headers": [
{
"name": "field-name",
"value": "field-value"
}
],*/
//upload_path: The path to save the uploaded file. "uploads" by default.
//If the path isn't prefixed with /, ./ or ../,
//it is relative path of document_root path
"upload_path": "uploads",
/* file_types:
* HTTP download file types,The file types supported by drogon
* by default are "html", "js", "css", "xml", "xsl", "txt", "svg",
* "ttf", "otf", "woff2", "woff" , "eot", "png", "jpg", "jpeg",
* "gif", "bmp", "ico", "icns", etc. */
"file_types": [
"gif",
"png",
"jpg",
"js",
"css",
"html",
"ico",
"swf",
"xap",
"apk",
"cur",
"xml",
"webp",
"svg"
],
// 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.
"mime": {
// "text/markdown": "md",
// "text/gemini": ["gmi", "gemini"]
},
//locations: An array of locations of static files for GET requests.
"locations": [
{
//uri_prefix: The URI prefix of the location prefixed with "/", the default value is "" that disables the location.
//"uri_prefix": "/.well-known/acme-challenge/",
//default_content_type: The default content type of the static files without
//an extension. empty string by default.
"default_content_type": "text/plain",
//alias: The location in file system, if it is prefixed with "/", it
//presents an absolute path, otherwise it presents a relative path to
//the document_root path.
//The default value is "" which means use the document root path as the location base path.
"alias": "",
//is_case_sensitive: indicates whether the URI prefix is case sensitive.
"is_case_sensitive": false,
//allow_all: true by default. If it is set to false, only static files with a valid extension can be accessed.
"allow_all": true,
//is_recursive: true by default. If it is set to false, files in sub directories can't be accessed.
"is_recursive": true,
//filters: string array, the filters applied to the location.
"filters": []
}
],
//max_connections: maximum number of connections, 100000 by default
"max_connections": 100000,
//max_connections_per_ip: maximum number of connections per client, 0 by default which means no limit
"max_connections_per_ip": 0,
//Load_dynamic_views: False by default, when set to true, drogon
//compiles and loads dynamically "CSP View Files" in directories defined
//by "dynamic_views_path"
"load_dynamic_views": false,
//dynamic_views_path: If the path isn't prefixed with /, ./ or ../,
//it is relative path of document_root path
"dynamic_views_path": [
"./views"
],
//dynamic_views_output_path: Default by an empty string which means the output path of source
//files is the path where the csp files locate. If the path isn't prefixed with /, it is relative
//path of the current working directory.
"dynamic_views_output_path": "",
//json_parser_stack_limit: 1000 by default, the maximum number of stack depth when reading a json string by the jsoncpp library.
"json_parser_stack_limit": 1000,
//enable_unicode_escaping_in_json: true by default, enable unicode escaping in json.
"enable_unicode_escaping_in_json": true,
//float_precision_in_json: set precision of float number in json.
"float_precision_in_json": {
//precision: 0 by default, 0 means use the default precision of the jsoncpp lib.
"precision": 0,
//precision_type: must be "significant" or "decimal", defaults to "significant" that means
//setting max number of significant digits in string, "decimal" means setting max number of
//digits after "." in string
"precision_type": "significant"
},
//log: Set log output, drogon output logs to stdout by default
"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": "./",
//logfile_base_name: Log file base name,empty by default which means drogon names logfile as
//drogon.log ...
"logfile_base_name": "",
//log_size_limit: 100000000 bytes by default,
//When the log file size reaches "log_size_limit", the log file is switched.
"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"
//The TRACE level is only valid when built in DEBUG mode.
"log_level": "DEBUG",
//display_local_time: false by default, if true, the log time is displayed in local time
"display_local_time": false
},
//run_as_daemon: False by default
"run_as_daemon": false,
//handle_sig_term: True by default
"handle_sig_term": true,
//relaunch_on_error: False by default, if true, the program will be restart by the parent after exiting;
"relaunch_on_error": false,
//use_sendfile: True by default, if true, the program
//uses sendfile() system-call to send static files to clients;
"use_sendfile": true,
//use_gzip: True by default, use gzip to compress the response body's content;
"use_gzip": true,
//use_brotli: False by default, use brotli to compress the response body's content;
"use_brotli": false,
//static_files_cache_time: 5 (seconds) by default, the time in which the static file response is cached,
//0 means cache forever, the negative value means no cache
"static_files_cache_time": 5,
//simple_controllers_map: Used to configure mapping from path to simple controller
//"simple_controllers_map": [
// {
// "path": "/path/name",
// "controller": "controllerClassName",
// "http_methods": [
// "get",
// "post"
// ],
// "filters": [
// "FilterClassName"
// ]
// }
//],
//idle_connection_timeout: Defaults to 60 seconds, the lifetime
//of the connection without read or write
"idle_connection_timeout": 60,
//server_header_field: Set the 'Server' header field in each response sent by drogon,
//empty string by default with which the 'Server' header field is set to "Server: drogon/version string\r\n"
"server_header_field": "",
//enable_server_header: Set true to force drogon to add a 'Server' header to each HTTP response. The default
//value is true.
"enable_server_header": true,
//enable_date_header: Set true to force drogon to add a 'Date' header to each HTTP response. The default
//value is true.
"enable_date_header": true,
//keepalive_requests: Set the maximum number of requests that can be served through one keep-alive connection.
//After the maximum number of requests are made, the connection is closed.
//The default value of 0 means no limit.
"keepalive_requests": 0,
//pipelining_requests: Set the maximum number of unhandled requests that can be cached in pipelining buffer.
//After the maximum number of requests are made, the connection is closed.
//The default value of 0 means no limit.
"pipelining_requests": 0,
//gzip_static: If it is set to true, when the client requests a static file, drogon first finds the compressed
//file with the extension ".gz" in the same path and send the compressed file to the client.
//The default value of gzip_static is true.
"gzip_static": true,
//br_static: If it is set to true, when the client requests a static file, drogon first finds the compressed
//file with the extension ".br" in the same path and send the compressed file to the client.
//The default value of br_static is true.
"br_static": true,
//client_max_body_size: Set the maximum body size of HTTP requests received by drogon. The default value is "1M".
//One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
"client_max_body_size": "1M",
//max_memory_body_size: Set the maximum body size in memory of HTTP requests received by drogon. The default value is "64K" bytes.
//If the body size of a HTTP request exceeds this limit, the body is stored to a temporary file for processing.
//Setting it to "" means no limit.
"client_max_memory_body_size": "64K",
//client_max_websocket_message_size: Set the maximum size of messages sent by WebSocket client. The default value is "128K".
//One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
"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": false,
// enabled_compressed_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.
// Once when receiving and once when decompressing. i.e. if the decompressed body is larger than max_body_size, the request
// will be rejected.
"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": [
{
//name: The class name of the plugin
"name": "drogon::plugin::PromExporter",
//dependencies: Plugins that the plugin depends on. It can be commented out
"dependencies": [],
//config: The configuration of the plugin. This json object is the parameter to initialize the plugin.
//It can be commented out
"config": {
"path": "/metrics"
}
},
{
"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
}
}
],
//custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method.
"custom_config": {}
}

View File

@ -1,313 +0,0 @@
# This is a YAML format configuration file
# 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`.
# ssl:
# cert: ../../trantor/trantor/tests/server.crt
# key: ../../trantor/trantor/tests/server.key
# conf: [
# # [Options, -SessionTicket],
# # [Options, Compression]
# ]
# listeners:
# # address: Ip address,0.0.0.0 by default
# - address: 0.0.0.0
# # port: Port number
# port: 80
# # https: If true, use https for security,false by default
# https: false
# - address: 0.0.0.0
# port: 443
# https: true
# # cert,key: Cert file path and key file path, empty by default,
# # if empty, use the global setting
# cert: ''
# key: ''
# # use_old_tls: enable the TLS1.0/1.1, false by default
# use_old_tls: false
# ssl_conf: [
# # [MinProtocol, TLSv1.3]
# ]
# db_clients:
# # name: Name of the client,'default' by default
# - name: default
# # rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default
# rdbms: postgresql
# # filename: Sqlite3 db file name
# # filename: ''
# # host: Server address,localhost by default
# host: 127.0.0.1
# # port: Server port, 5432 by default
# port: 5432
# # dbname: Database name
# dbname: test
# # user: 'postgres' by default
# user: ''
# # passwd: '' by default
# passwd: ''
# # is_fast: false by default, if it is true, the client is faster but user can't call
# # any synchronous interface of it.
# is_fast: false
# # client_encoding: The character set used by the client. it is empty string by default which
# # means use the default character set.
# # client_encoding: ''
# # number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
# # connections per IO thread, otherwise it is the total number of all connections.
# number_of_connections: 1
# # timeout: -1 by default, in seconds, the timeout for executing a SQL query.
# # zero or negative value means no timeout.
# timeout: -1
# # auto_batch: this feature is only available for the PostgreSQL driver(version >= 14.0), see
# # the wiki for more details.
# 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:
# # name: Name of the client,'default' by default
# - name: default
# # host: Server IP, 127.0.0.1 by default
# host: 127.0.0.1
# # port: Server port, 6379 by default
# port: 6379
# # username: '' by default which means 'default' in redis ACL
# username: ''
# # passwd: '' by default
# passwd: ''
# # db index: 0 by default
# db: 0
# # is_fast: false by default, if it is true, the client is faster but user can't call
# # any synchronous interface of it.
# is_fast: false
# # number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
# # connections per IO thread, otherwise it is the total number of all connections.
# number_of_connections: 1
# # timeout: -1.0 by default, in seconds, the timeout for executing a command.
# # zero or negative value means no timeout.
# timeout: -1
app:
# number_of_threads: The number of IO threads, 1 by default, if the value is set to 0, the number of threads
# is the number of CPU cores
number_of_threads: 1
# enable_session: False by default
enable_session: false
session_timeout: 0
# string value of SameSite attribute of the Set-Cookie HTTP response header
# valid value is either 'Null' (default), 'Lax', 'Strict' or 'None'
session_same_site: 'Null'
# session_cookie_key: The cookie key of the session, "JSESSIONID" by default
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: ./
# 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
# to the request for "/".
home_page: index.html
# use_implicit_page: enable implicit pages if true, true by default
use_implicit_page: true
# implicit_page: Set the file which would the server access in a directory that a user accessed.
# For example, by default, http://localhost/a-directory resolves to http://localhost/a-directory/index.html.
implicit_page: index.html
# static_file_headers: Headers for static files
# static_file_headers:
# - name: field-name
# value: field-value
# upload_path: The path to save the uploaded file. "uploads" by default.
# If the path isn't prefixed with /, ./ or ../,
# it is relative path of document_root path
upload_path: uploads
# file_types:
# HTTP download file types,The file types supported by drogon
# by default are "html", "js", "css", "xml", "xsl", "txt", "svg",
# "ttf", "otf", "woff2", "woff" , "eot", "png", "jpg", "jpeg",
# "gif", "bmp", "ico", "icns", etc.
file_types:
- gif
- png
- jpg
- js
- css
- html
- ico
- swf
- xap
- apk
- cur
- xml
# 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.
mime: {
# text/markdown: md
# text/gemini:
# - gmi
# - gemini
}
# locations: An array of locations of static files for GET requests.
locations:
# uri_prefix: The URI prefix of the location prefixed with "/", the default value is "" that disables the location.
- uri_prefix: '' # /.well-known/acme-challenge/
# default_content_type: The default content type of the static files without
# an extension. empty string by default.
default_content_type: text/plain
# alias: The location in file system, if it is prefixed with "/", it
# presents an absolute path, otherwise it presents a relative path to
# the document_root path.
# The default value is "" which means use the document root path as the location base path.
alias: ''
# is_case_sensitive: indicates whether the URI prefix is case sensitive.
is_case_sensitive: false
# allow_all: true by default. If it is set to false, only static files with a valid extension can be accessed.
allow_all: true
# is_recursive: true by default. If it is set to false, files in sub directories can't be accessed.
is_recursive: true
# filters: string array, the filters applied to the location.
filters: []
# max_connections: maximum number of connections, 100000 by default
max_connections: 100000
# max_connections_per_ip: maximum number of connections per client, 0 by default which means no limit
max_connections_per_ip: 0
# Load_dynamic_views: False by default, when set to true, drogon
# compiles and loads dynamically "CSP View Files" in directories defined
# by "dynamic_views_path"
load_dynamic_views: false
# dynamic_views_path: If the path isn't prefixed with /, ./ or ../,
# it is relative path of document_root path
dynamic_views_path:
- ./views
# dynamic_views_output_path: Default by an empty string which means the output path of source
# files is the path where the csp files locate. If the path isn't prefixed with /, it is relative
# path of the current working directory.
dynamic_views_output_path: ''
# json_parser_stack_limit: 1000 by default, the maximum number of stack depth when reading a json string by the jsoncpp library.
json_parser_stack_limit: 1000
# enable_unicode_escaping_in_json: true by default, enable unicode escaping in json.
enable_unicode_escaping_in_json: true
# float_precision_in_json: set precision of float number in json.
float_precision_in_json:
# precision: 0 by default, 0 means use the default precision of the jsoncpp lib.
precision: 0
# precision_type: must be "significant" or "decimal", defaults to "significant" that means
# setting max number of significant digits in string, "decimal" means setting max number of
# digits after "." in string
precision_type: significant
# log: Set log output, drogon output logs to stdout by default
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: ./
# logfile_base_name: Log file base name,empty by default which means drogon names logfile as
# drogon.log ...
logfile_base_name: ''
# log_size_limit: 100000000 bytes by default,
# When the log file size reaches "log_size_limit", the log file is switched.
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"
# The TRACE level is only valid when built in DEBUG mode.
log_level: DEBUG
# display_local_time: false by default, if true, the log time is displayed in local time
display_local_time: false
# run_as_daemon: False by default
run_as_daemon: false
# handle_sig_term: True by default
handle_sig_term: true
# relaunch_on_error: False by default, if true, the program will be restart by the parent after exiting;
relaunch_on_error: false
# use_sendfile: True by default, if true, the program
# uses sendfile() system-call to send static files to clients;
use_sendfile: true
# use_gzip: True by default, use gzip to compress the response body's content;
use_gzip: true
# use_brotli: False by default, use brotli to compress the response body's content;
use_brotli: false
# static_files_cache_time: 5 (seconds) by default, the time in which the static file response is cached,
# 0 means cache forever, the negative value means no cache
static_files_cache_time: 5
# simple_controllers_map: Used to configure mapping from path to simple controller
# simple_controllers_map:
# - path: /path/name
# controller: controllerClassName
# http_methods:
# - get
# - post
# filters:
# - FilterClassName
# idle_connection_timeout: Defaults to 60 seconds, the lifetime
# of the connection without read or write
idle_connection_timeout: 60
# server_header_field: Set the 'Server' header field in each response sent by drogon,
# empty string by default with which the 'Server' header field is set to "Server: drogon/version string\r\n"
server_header_field: ''
# enable_server_header: Set true to force drogon to add a 'Server' header to each HTTP response. The default
# value is true.
enable_server_header: true
# enable_date_header: Set true to force drogon to add a 'Date' header to each HTTP response. The default
# value is true.
enable_date_header: true
# keepalive_requests: Set the maximum number of requests that can be served through one keep-alive connection.
# After the maximum number of requests are made, the connection is closed.
# The default value of 0 means no limit.
keepalive_requests: 0
# pipelining_requests: Set the maximum number of unhandled requests that can be cached in pipelining buffer.
# After the maximum number of requests are made, the connection is closed.
# The default value of 0 means no limit.
pipelining_requests: 0
# gzip_static: If it is set to true, when the client requests a static file, drogon first finds the compressed
# file with the extension ".gz" in the same path and send the compressed file to the client.
# The default value of gzip_static is true.
gzip_static: true
# br_static: If it is set to true, when the client requests a static file, drogon first finds the compressed
# file with the extension ".br" in the same path and send the compressed file to the client.
# The default value of br_static is true.
br_static: true
# client_max_body_size: Set the maximum body size of HTTP requests received by drogon. The default value is "1M".
# One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
client_max_body_size: 1M
# max_memory_body_size: Set the maximum body size in memory of HTTP requests received by drogon. The default value is "64K" bytes.
# If the body size of a HTTP request exceeds this limit, the body is stored to a temporary file for processing.
# Setting it to "" means no limit.
client_max_memory_body_size: 64K
# client_max_websocket_message_size: Set the maximum size of messages sent by WebSocket client. The default value is "128K".
# One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
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: false
# enabled_compressed_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.
# Once when receiving and once when decompressing. i.e. if the decompressed body is larger than max_body_size, the request
# will be rejected.
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:
# name: The class name of the plugin
- name: drogon::plugin::PromExporter
# dependencies: Plugins that the plugin depends on. It can be commented out
dependencies: []
# config: The configuration of the plugin. This json object is the parameter to initialize the plugin.
# It can be commented out
config:
path: /metrics
- 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
# custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method.
custom_config: {}

View File

@ -1,11 +1,10 @@
#include <drogon/drogon.h>
int main() {
//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
//drogon::app().loadConfigFile("../config.json");
//drogon::app().loadConfigFile("../config.yaml");
//Run HTTP framework,the method will block in the internal event loop
drogon::app().run();
return 0;
}
}

View File

@ -22,9 +22,9 @@ class [[className]] : public HttpFilter<[[className]]>
{
public:
[[className]]() {}
void doFilter(const HttpRequestPtr &req,
FilterCallback &&fcb,
FilterChainCallback &&fccb) override;
virtual void doFilter(const HttpRequestPtr &req,
FilterCallback &&fcb,
FilterChainCallback &&fccb) override;
};
<%c++for(size_t i=0;i<namespaceVector.size();i++){%>

View File

@ -61,7 +61,6 @@ for(auto convertMethod : convertMethods ) {
%>
using namespace drogon;
using namespace drogon::orm;
using namespace drogon_model::[[dbName]]<%c++
auto &schema=@@.get<std::string>("schema");
if(!schema.empty())
@ -76,7 +75,7 @@ else
<%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++if(@@.get<int>("hasPrimaryKey")<=1){%>
@ -102,7 +101,7 @@ if(!schema.empty())
{
$$<<schema<<".";
}
%>{%escapeIdentifier(@@.get<std::string>("tableName"), rdbms)%}";
%>[[tableName]]";
const std::vector<typename [[className]]::MetaData> [[className]]::metaData_={
<%c++for(size_t i=0;i<cols.size();i++){
@ -177,10 +176,10 @@ const std::string &[[className]]::getColumnName(size_t index) noexcept(false)
$$<<" }\n";
$$<<" }\n";
continue;
}
}
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";
$$<<" str[0]=='\\\\'&&str[1]=='x')\n";
$$<<" {\n";
@ -192,7 +191,7 @@ const std::string &[[className]]::getColumnName(size_t index) noexcept(false)
$$<<" }\n";
$$<<" }\n";
continue;
}
}
%>
{%col.colValName_%}_=std::make_shared<{%col.colType_%}>(r["{%col.colName_%}"].as<{%col.colType_%}>());
<%c++
@ -268,10 +267,10 @@ const std::string &[[className]]::getColumnName(size_t index) noexcept(false)
$$<<" }\n";
$$<<" }\n";
continue;
}
}
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";
$$<<" str[0]=='\\\\'&&str[1]=='x')\n";
$$<<" {\n";
@ -283,7 +282,7 @@ const std::string &[[className]]::getColumnName(size_t index) noexcept(false)
$$<<" }\n";
$$<<" }\n";
continue;
}
}
%>
{%col.colValName_%}_=std::make_shared<{%col.colType_%}>(r[index].as<{%col.colType_%}>());
<%c++
@ -307,7 +306,7 @@ const std::string &[[className]]::getColumnName(size_t index) noexcept(false)
<%c++
for(size_t i=0; i<cols.size(); ++i)
{
auto &col = cols[i];
auto &col = cols[i];
if(col.colType_.empty())
continue;
%>
@ -379,7 +378,7 @@ const std::string &[[className]]::getColumnName(size_t index) noexcept(false)
$$<<" }\n";
$$<<" }\n";
continue;
}
}
else if(col.colDatabaseType_=="bytea" || col.colDatabaseType_.find("blob") != std::string::npos)
{
$$<<" if(!pJson[pMasqueradingVector["<<i<<"]].isNull())\n";
@ -393,7 +392,7 @@ const std::string &[[className]]::getColumnName(size_t index) noexcept(false)
$$<<" }\n";
$$<<" }\n";
continue;
}
}
else if(col.colType_.find("uint") == 0 || col.colType_ == "unsigned short")
{
$$<<" if(!pJson[pMasqueradingVector["<<i<<"]].isNull())\n";
@ -458,10 +457,10 @@ const std::string &[[className]]::getColumnName(size_t index) noexcept(false)
$$<<" }\n";
$$<<" }\n";
continue;
}
}
%>
}
<%c++
<%c++
}
%>
}
@ -539,10 +538,10 @@ const std::string &[[className]]::getColumnName(size_t index) noexcept(false)
$$<<" "<< convertMethod->methodBeforeDbWrite() << "(" << col.colValName_ << "_);\n";
} //endif
$$<<" }\n";
$$<<" }\n";
$$<<" }\n";
$$<<" }\n";
continue;
}
}
else if(col.colDatabaseType_=="bytea" || col.colDatabaseType_.find("blob") != std::string::npos)
{
$$<<" if(!pJson[\""<<col.colName_<<"\"].isNull())\n";
@ -553,10 +552,10 @@ const std::string &[[className]]::getColumnName(size_t index) noexcept(false)
if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != "") {
$$<<" "<< convertMethod->methodBeforeDbWrite() << "(" << col.colValName_ << "_);\n";
} //endif
$$<<" }\n";
$$<<" }\n";
$$<<" }\n";
continue;
}
}
else if(col.colType_.find("uint") == 0 || col.colType_ == "unsigned short")
{
$$<<" if(!pJson[\""<<col.colName_<<"\"].isNull())\n";
@ -566,7 +565,7 @@ const std::string &[[className]]::getColumnName(size_t index) noexcept(false)
if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != "") {
$$<<" "<< convertMethod->methodBeforeDbWrite() << "(" << col.colValName_ << "_);\n";
} //endif
$$<<" }\n";
$$<<" }\n";
$$<<" }\n";
continue;
}
@ -579,7 +578,7 @@ const std::string &[[className]]::getColumnName(size_t index) noexcept(false)
if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != "") {
$$<<" "<< convertMethod->methodBeforeDbWrite() << "(" << col.colValName_ << "_);\n";
} //endif
$$<<" }\n";
$$<<" }\n";
$$<<" }\n";
continue;
}
@ -592,7 +591,7 @@ const std::string &[[className]]::getColumnName(size_t index) noexcept(false)
if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != "") {
$$<<" "<< convertMethod->methodBeforeDbWrite() << "(" << col.colValName_ << "_);\n";
} //endif
$$<<" }\n";
$$<<" }\n";
$$<<" }\n";
continue;
}
@ -605,7 +604,7 @@ const std::string &[[className]]::getColumnName(size_t index) noexcept(false)
if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != "") {
$$<<" "<< convertMethod->methodBeforeDbWrite() << "(" << col.colValName_ << "_);\n";
} //endif
$$<<" }\n";
$$<<" }\n";
$$<<" }\n";
continue;
}
@ -618,10 +617,10 @@ const std::string &[[className]]::getColumnName(size_t index) noexcept(false)
if (convertMethod != convertMethods.end() && convertMethod->methodBeforeDbWrite() != "") {
$$<<" "<< convertMethod->methodBeforeDbWrite() << "(" << col.colValName_ << "_);\n";
} //endif
$$<<" }\n";
$$<<" }\n";
$$<<" }\n";
continue;
}
}
%>
}
<%c++
@ -714,7 +713,7 @@ void [[className]]::updateByMasqueradedJson(const Json::Value &pJson,
$$<<" }\n";
$$<<" }\n";
continue;
}
}
else if(col.colDatabaseType_=="bytea" || col.colDatabaseType_.find("blob") != std::string::npos)
{
$$<<" if(!pJson[pMasqueradingVector["<<i<<"]].isNull())\n";
@ -728,7 +727,7 @@ void [[className]]::updateByMasqueradedJson(const Json::Value &pJson,
$$<<" }\n";
$$<<" }\n";
continue;
}
}
else if(col.colType_.find("uint") == 0 || col.colType_ == "unsigned short")
{
$$<<" if(!pJson[pMasqueradingVector["<<i<<"]].isNull())\n";
@ -793,14 +792,14 @@ void [[className]]::updateByMasqueradedJson(const Json::Value &pJson,
$$<<" }\n";
$$<<" }\n";
continue;
}
}
%>
}
<%c++
}
%>
}
void [[className]]::updateByJson(const Json::Value &pJson) noexcept(false)
{
<%c++
@ -880,7 +879,7 @@ void [[className]]::updateByJson(const Json::Value &pJson) noexcept(false)
$$<<" }\n";
$$<<" }\n";
continue;
}
}
else if(col.colDatabaseType_=="bytea" || col.colDatabaseType_.find("blob") != std::string::npos)
{
$$<<" if(!pJson[\""<<col.colName_<<"\"].isNull())\n";
@ -894,7 +893,7 @@ void [[className]]::updateByJson(const Json::Value &pJson) noexcept(false)
$$<<" }\n";
$$<<" }\n";
continue;
}
}
else if(col.colType_.find("uint") == 0 || col.colType_ == "unsigned short")
{
$$<<" if(!pJson[\""<<col.colName_<<"\"].isNull())\n";
@ -959,7 +958,7 @@ void [[className]]::updateByJson(const Json::Value &pJson) noexcept(false)
$$<<" }\n";
$$<<" }\n";
continue;
}
}
%>
}
<%c++
@ -975,7 +974,7 @@ void [[className]]::updateByJson(const Json::Value &pJson) noexcept(false)
{
$$<<"const "<<col.colType_<<" &"<<className<<"::getValueOf"<<col.colTypeName_<<"() const noexcept\n";
$$<<"{\n";
$$<<" static const "<<col.colType_<<" defaultValue = "<<col.colType_<<"();\n";
$$<<" const static "<<col.colType_<<" defaultValue = "<<col.colType_<<"();\n";
$$<<" if("<<col.colValName_<<"_)\n";
$$<<" return *"<<col.colValName_<<"_;\n";
$$<<" return defaultValue;\n";
@ -984,7 +983,7 @@ void [[className]]::updateByJson(const Json::Value &pJson) noexcept(false)
{
$$<<"std::string "<<className<<"::getValueOf"<<col.colTypeName_<<"AsString() const noexcept\n";
$$<<"{\n";
$$<<" static const std::string defaultValue = std::string();\n";
$$<<" const static std::string defaultValue = std::string();\n";
$$<<" if("<<col.colValName_<<"_)\n";
$$<<" return std::string("<<col.colValName_<<"_->data(),"<<col.colValName_<<"_->size());\n";
$$<<" return defaultValue;\n";
@ -994,7 +993,7 @@ void [[className]]::updateByJson(const Json::Value &pJson) noexcept(false)
$$<<"{\n";
$$<<" return "<<col.colValName_<<"_;\n";
$$<<"}\n";
$$<<"void "<<className<<"::set"<<col.colTypeName_<<"(const "<<col.colType_<<" &p"<<col.colTypeName_<<") noexcept\n";
$$<<"{\n";
if(col.colDatabaseType_=="date")
@ -1007,7 +1006,7 @@ void [[className]]::updateByJson(const Json::Value &pJson) noexcept(false)
}
$$<<" dirtyFlag_["<<i<<"] = true;\n";
$$<<"}\n";
if(col.colType_=="std::string")
{
$$<<"void "<<className<<"::set"<<col.colTypeName_<<"("<<col.colType_<<" &&p"<<col.colTypeName_<<") noexcept\n";
@ -1044,7 +1043,7 @@ void [[className]]::updateByJson(const Json::Value &pJson) noexcept(false)
$$<<"}\n";
}
}
$$<<"\n";
$$<<"\n";
}
if(@@.get<std::string>("rdbms")=="postgresql"||@@.get<int>("hasPrimaryKey")!=1)
{
@ -1329,7 +1328,7 @@ bool [[className]]::validateMasqueradedJsonForCreation(const Json::Value &pJson,
}
<%c++}%>
}
catch(const Json::LogicError &e)
catch(const Json::LogicError &e)
{
err = e.what();
return false;
@ -1359,7 +1358,7 @@ bool [[className]]::validateJsonForUpdate(const Json::Value &pJson, std::string
err = "The value of primary key must be set in the json object for update";
return false;
}
<%c++
<%c++
}
}%>
return true;
@ -1386,7 +1385,7 @@ bool [[className]]::validateMasqueradedJsonForUpdate(const Json::Value &pJson,
if(!validJsonOfField({%i%}, pMasqueradingVector[{%i%}], pJson[pMasqueradingVector[{%i%}]], err, false))
return false;
}
<%c++
<%c++
if(col.isPrimaryKey_)
{
%>
@ -1395,11 +1394,11 @@ bool [[className]]::validateMasqueradedJsonForUpdate(const Json::Value &pJson,
err = "The value of primary key must be set in the json object for update";
return false;
}
<%c++
<%c++
}
}%>
}
catch(const Json::LogicError &e)
catch(const Json::LogicError &e)
{
err = e.what();
return false;
@ -1408,8 +1407,8 @@ bool [[className]]::validateMasqueradedJsonForUpdate(const Json::Value &pJson,
}
bool [[className]]::validJsonOfField(size_t index,
{%indentStr%} const std::string &fieldName,
{%indentStr%} const Json::Value &pJson,
{%indentStr%} std::string &err,
{%indentStr%} const Json::Value &pJson,
{%indentStr%} std::string &err,
{%indentStr%} bool isForCreation)
{
switch(index)
@ -1430,7 +1429,7 @@ bool [[className]]::validJsonOfField(size_t index,
}
<%c++}
if(col.isAutoVal_)
{
{
if(col.isPrimaryKey_)
{
%>
@ -1438,8 +1437,8 @@ bool [[className]]::validJsonOfField(size_t index,
{
err="The automatic primary key cannot be set";
return false;
}
<%c++
}
<%c++
}else
{
%>
@ -1453,15 +1452,15 @@ bool [[className]]::validJsonOfField(size_t index,
err="The automatic primary key cannot be update";
return false;
}
<%c++
<%c++
}
}
}
if(!col.notNull_){%>
if(pJson.isNull())
{
return true;
}
<%c++}
<%c++}
if(col.colType_ == "uint64_t")
{
%>
@ -1492,7 +1491,7 @@ if(!col.notNull_){%>
}
<%c++
}
else if(col.colType_.find("int") == 0 || col.colType_ == "short")
else if(col.colType_.find("int") == 0)
{
%>
if(!pJson.isInt())
@ -1520,7 +1519,7 @@ if(!col.notNull_){%>
err="Type error in the "+fieldName+" field";
return false;
}
<%c++
<%c++
}
else
{
@ -1528,18 +1527,19 @@ if(!col.notNull_){%>
if(!pJson.isString())
{
err="Type error in the "+fieldName+" field";
return false;
return false;
}
<%c++
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 " +
fieldName +
" field (the maximum value is {%col.colLength_%})";
return false;
return false;
}
<%c++
@ -1548,10 +1548,11 @@ if(!col.notNull_){%>
%>
break;
<%c++
}%>
}%>
default:
err="Internal error in the server";
return false;
break;
}
return true;
}
@ -1567,54 +1568,33 @@ for(auto &relationship : relationships)
auto relationshipValName=nameTransform(name, false);
auto alias=relationship.targetTableAlias();
auto aliasValName=nameTransform(alias, false);
if(!alias.empty())
{
if(alias[0] <= 'z' && alias[0] >= 'a')
{
alias[0] += ('A' - 'a');
}
}
else
{
alias = relationshipClassName;
}
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")
if(!alias.empty())
{
$$<<"$1";
if(alias[0] <= 'z' && alias[0] >= 'a')
{
alias[0] += ('A' - 'a');
}
std::string alind(alias.length(), ' ');
%>
void [[className]]::get{%alias%}(const DbClientPtr &clientPtr,
{%indentStr%} {%alind%} const std::function<void({%relationshipClassName%})> &rcb,
{%indentStr%} {%alind%} const ExceptionCallback &ecb) const
<%c++
}
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]);
}
void [[className]]::get{%alias%}(const DbClientPtr &clientPtr,
{%indentStr%} {%alind%} const std::function<void({%relationshipClassName%})> &rcb,
{%indentStr%} {%alind%} const ExceptionCallback &ecb) const
std::string relationshipClassInde(relationshipClassName.length(), ' ');
%>
void [[className]]::get{%relationshipClassName%}(const DbClientPtr &clientPtr,
{%indentStr%} {%relationshipClassInde%} const std::function<void({%relationshipClassName%})> &rcb,
{%indentStr%} {%relationshipClassInde%} const ExceptionCallback &ecb) const
<%c++
}
%>
{
static const std::string sql = "select * from {%name%} where {%relationship.targetKey()%} = <%c++
const static std::string sql = "select * from {%name%} where {%relationship.targetKey()%} = <%c++
if(rdbms=="postgresql")
{
$$<<"$1";
@ -1624,7 +1604,7 @@ void [[className]]::get{%alias%}(const DbClientPtr &clientPtr,
$$<<"?";
}%>";
*clientPtr << sql
<< *{%nameTransform(relationship.originalKey(), false)%}_
<< *{%nameTransform(relationship.originalKey(), false)%}_
>> [rcb = std::move(rcb), ecb](const Result &r){
if (r.size() == 0)
{
@ -1645,38 +1625,31 @@ void [[className]]::get{%alias%}(const DbClientPtr &clientPtr,
}
else if(relationship.type() == Relationship::Type::HasMany)
{
%>
std::vector<{%relationshipClassName%}> [[className]]::get{%alias%}(const DbClientPtr &clientPtr) const {
static const std::string sql = "select * from {%name%} where {%relationship.targetKey()%} = <%c++
if(rdbms=="postgresql")
if(!alias.empty())
{
$$<<"$1";
if(alias[0] <= 'z' && alias[0] >= 'a')
{
alias[0] += ('A' - 'a');
}
std::string alind(alias.length(), ' ');
%>
void [[className]]::get{%alias%}(const DbClientPtr &clientPtr,
{%indentStr%} {%alind%} const std::function<void(std::vector<{%relationshipClassName%}>)> &rcb,
{%indentStr%} {%alind%} const ExceptionCallback &ecb) const
<%c++
}
else
{
$$<<"?";
}%>";
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;
ret.reserve(r.size());
for (auto const &row : r)
{
ret.emplace_back({%relationshipClassName%}(row));
}
return ret;
}
void [[className]]::get{%alias%}(const DbClientPtr &clientPtr,
{%indentStr%} {%alind%} const std::function<void(std::vector<{%relationshipClassName%}>)> &rcb,
{%indentStr%} {%alind%} const ExceptionCallback &ecb) const
std::string relationshipClassInde(relationshipClassName.length(), ' ');
%>
void [[className]]::get{%relationshipClassName%}(const DbClientPtr &clientPtr,
{%indentStr%} {%relationshipClassInde%} const std::function<void(std::vector<{%relationshipClassName%}>)> &rcb,
{%indentStr%} {%relationshipClassInde%} const ExceptionCallback &ecb) const
<%c++
}
%>
{
static const std::string sql = "select * from {%name%} where {%relationship.targetKey()%} = <%c++
const static std::string sql = "select * from {%name%} where {%relationship.targetKey()%} = <%c++
if(rdbms=="postgresql")
{
$$<<"$1";
@ -1686,10 +1659,10 @@ void [[className]]::get{%alias%}(const DbClientPtr &clientPtr,
$$<<"?";
}%>";
*clientPtr << sql
<< *{%nameTransform(relationship.originalKey(), false)%}_
<< *{%nameTransform(relationship.originalKey(), false)%}_
>> [rcb = std::move(rcb)](const Result &r){
std::vector<{%relationshipClassName%}> ret;
ret.reserve(r.size());
ret.reserve(ret.size());
for (auto const &row : r)
{
ret.emplace_back({%relationshipClassName%}(row));
@ -1706,39 +1679,31 @@ void [[className]]::get{%alias%}(const DbClientPtr &clientPtr,
auto pivotTableClassName=nameTransform(pivotTableName, true);
auto &pivotOriginalKey=relationship.pivotTable().originalKey();
auto &pivotTargetKey=relationship.pivotTable().targetKey();
%>
std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>> [[className]]::get{%alias%}(const DbClientPtr &clientPtr) const {
static const std::string sql = "select * from {%name%},{%pivotTableName%} where {%pivotTableName%}.{%pivotOriginalKey%} = <%c++
if(rdbms=="postgresql")
if(!alias.empty())
{
$$<<"$1";
if(alias[0] <= 'z' && alias[0] >= 'a')
{
alias[0] += ('A' - 'a');
}
std::string alind(alias.length(), ' ');
%>
void [[className]]::get{%alias%}(const DbClientPtr &clientPtr,
{%indentStr%} {%alind%} const std::function<void(std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>)> &rcb,
{%indentStr%} {%alind%} const ExceptionCallback &ecb) const
<%c++
}
else
{
$$<<"?";
}%> 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;
ret.reserve(r.size());
for (auto const &row : r)
{
ret.emplace_back(std::pair<{%relationshipClassName%},{%pivotTableClassName%}>(
{%relationshipClassName%}(row),{%pivotTableClassName%}(row,{%relationshipClassName%}::getColumnNumber())));
}
return ret;
}
void [[className]]::get{%alias%}(const DbClientPtr &clientPtr,
{%indentStr%} {%alind%} const std::function<void(std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>)> &rcb,
{%indentStr%} {%alind%} const ExceptionCallback &ecb) const
std::string relationshipClassInde(relationshipClassName.length(), ' ');
%>
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++
}
%>
{
static const std::string sql = "select * from {%name%},{%pivotTableName%} where {%pivotTableName%}.{%pivotOriginalKey%} = <%c++
const static std::string sql = "select * from {%name%},{%pivotTableName%} where {%pivotTableName%}.{%pivotOriginalKey%} = <%c++
if(rdbms=="postgresql")
{
$$<<"$1";
@ -1748,10 +1713,10 @@ void [[className]]::get{%alias%}(const DbClientPtr &clientPtr,
$$<<"?";
}%> and {%pivotTableName%}.{%pivotTargetKey%} = {%name%}.{%relationship.targetKey()%}";
*clientPtr << sql
<< *{%nameTransform(relationship.originalKey(), false)%}_
<< *{%nameTransform(relationship.originalKey(), false)%}_
>> [rcb = std::move(rcb)](const Result &r){
std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>> ret;
ret.reserve(r.size());
ret.reserve(ret.size());
for (auto const &row : r)
{
ret.emplace_back(std::pair<{%relationshipClassName%},{%pivotTableClassName%}>(

View File

@ -14,7 +14,6 @@ using namespace drogon_ctl;
#include <drogon/orm/Field.h>
#include <drogon/orm/SqlBinder.h>
#include <drogon/orm/Mapper.h>
#include <drogon/orm/BaseBuilder.h>
#ifdef __cpp_impl_coroutine
#include <drogon/orm/CoroMapper.h>
#endif
@ -22,13 +21,13 @@ using namespace drogon_ctl;
#include <trantor/utils/Logger.h>
#include <json/json.h>
#include <string>
#include <string_view>
#include <memory>
#include <vector>
#include <tuple>
#include <stdint.h>
#include <iostream>
using namespace drogon::orm;
namespace drogon
{
namespace orm
@ -39,7 +38,7 @@ using DbClientPtr = std::shared_ptr<DbClient>;
}
namespace drogon_model
{
namespace [[dbName]]
namespace [[dbName]]
{
<%c++
auto &schema=@@.get<std::string>("schema");
@ -86,11 +85,11 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
%>
};
static const int primaryKeyNumber;
static const std::string tableName;
static const bool hasPrimaryKey;
const static int primaryKeyNumber;
const static std::string tableName;
const static bool hasPrimaryKey;
<%c++if(@@.get<int>("hasPrimaryKey")<=1){%>
static const std::string primaryKeyName;
const static std::string primaryKeyName;
<%c++if(!@@.get<std::string>("primaryKeyType").empty()){%>
using PrimaryKeyType = [[primaryKeyType]];
const PrimaryKeyType &getPrimaryKey() const;
@ -99,7 +98,7 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
int getPrimaryKey() const { assert(false); return 0; }
<%c++}%>
<%c++}else{
auto pkTypes=@@.get<std::vector<std::string>>("primaryKeyType");
auto pkTypes=@@.get<std::vector<std::string>>("primaryKeyType");
std::string typelist;
for(size_t i=0;i<pkTypes.size();i++)
{
@ -108,7 +107,7 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
typelist += ",";
}
%>
static const std::vector<std::string> primaryKeyName;
const static std::vector<std::string> primaryKeyName;
using PrimaryKeyType = std::tuple<{%typelist%}>;//<%c++
auto pkName=@@.get<std::vector<std::string>>("primaryKeyName");
for(size_t i=0;i<pkName.size();i++)
@ -119,18 +118,18 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
}
%>
PrimaryKeyType getPrimaryKey() const;
PrimaryKeyType getPrimaryKey() const;
<%c++}%>
/**
* @brief constructor
* @param r One row of records in the SQL query result.
* @param indexOffset Set the offset to -1 to access all columns by column names,
* @param indexOffset Set the offset to -1 to access all columns by column names,
* otherwise access all columns by offsets.
* @note If the SQL is not a style of 'select * from table_name ...' (select all
* @note If the SQL is not a style of 'select * from table_name ...' (select all
* columns by an asterisk), please set the offset to -1.
*/
explicit [[className]](const drogon::orm::Row &r, const ssize_t indexOffset = 0) noexcept;
explicit [[className]](const Row &r, const ssize_t indexOffset = 0) noexcept;
/**
* @brief constructor
@ -146,7 +145,7 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
[[className]](const Json::Value &pJson, const std::vector<std::string> &pMasqueradingVector) noexcept(false);
[[className]]() = default;
void updateByJson(const Json::Value &pJson) noexcept(false);
void updateByMasqueradedJson(const Json::Value &pJson,
const std::vector<std::string> &pMasqueradingVector) noexcept(false);
@ -160,8 +159,8 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
std::string &err);
static bool validJsonOfField(size_t index,
const std::string &fieldName,
const Json::Value &pJson,
std::string &err,
const Json::Value &pJson,
std::string &err,
bool isForCreation);
<%c++
@ -196,7 +195,7 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
}
else
$$<<" //FIXME!!"<<" getValueOf"<<col.colTypeName_<<"() const noexcept;\n";
$$<<"\n";
$$<<"\n";
}
%>
@ -206,7 +205,7 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
Json::Value toJson() const;
Json::Value toMasqueradedJson(const std::vector<std::string> &pMasqueradingVector) const;
/// Relationship interfaces
<%c++
<%c++
for(auto &relationship : relationships)
{
if(relationship.targetKey().empty() || relationship.originalKey().empty())
@ -228,20 +227,18 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
}
std::string alind(alias.length(), ' ');
%>
{%relationshipClassName%} get{%alias%}(const drogon::orm::DbClientPtr &clientPtr) const;
void get{%alias%}(const drogon::orm::DbClientPtr &clientPtr,
void get{%alias%}(const DbClientPtr &clientPtr,
{%alind%} const std::function<void({%relationshipClassName%})> &rcb,
{%alind%} const drogon::orm::ExceptionCallback &ecb) const;
{%alind%} const ExceptionCallback &ecb) const;
<%c++
}
else
{
std::string relationshipClassInde(relationshipClassName.length(), ' ');
%>
{%relationshipClassName%} get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr) const;
void get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr,
void get{%relationshipClassName%}(const DbClientPtr &clientPtr,
{%relationshipClassInde%} const std::function<void({%relationshipClassName%})> &rcb,
{%relationshipClassInde%} const drogon::orm::ExceptionCallback &ecb) const;
{%relationshipClassInde%} const ExceptionCallback &ecb) const;
<%c++
}
}
@ -255,20 +252,18 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
}
std::string alind(alias.length(), ' ');
%>
std::vector<{%relationshipClassName%}> get{%alias%}(const drogon::orm::DbClientPtr &clientPtr) const;
void get{%alias%}(const drogon::orm::DbClientPtr &clientPtr,
void get{%alias%}(const DbClientPtr &clientPtr,
{%alind%} const std::function<void(std::vector<{%relationshipClassName%}>)> &rcb,
{%alind%} const drogon::orm::ExceptionCallback &ecb) const;
{%alind%} const ExceptionCallback &ecb) const;
<%c++
}
else
{
std::string relationshipClassInde(relationshipClassName.length(), ' ');
%>
std::vector<{%relationshipClassName%}> get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr) const;
void get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr,
void get{%relationshipClassName%}(const DbClientPtr &clientPtr,
{%relationshipClassInde%} const std::function<void(std::vector<{%relationshipClassName%}>)> &rcb,
{%relationshipClassInde%} const drogon::orm::ExceptionCallback &ecb) const;
{%relationshipClassInde%} const ExceptionCallback &ecb) const;
<%c++
}
}
@ -284,33 +279,27 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
}
std::string alind(alias.length(), ' ');
%>
std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>> get{%alias%}(const drogon::orm::DbClientPtr &clientPtr) const;
void get{%alias%}(const drogon::orm::DbClientPtr &clientPtr,
void get{%alias%}(const DbClientPtr &clientPtr,
{%alind%} const std::function<void(std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>)> &rcb,
{%alind%} const drogon::orm::ExceptionCallback &ecb) const;
{%alind%} const ExceptionCallback &ecb) const;
<%c++
}
else
{
std::string relationshipClassInde(relationshipClassName.length(), ' ');
%>
std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>> get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr) const;
void get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr,
void get{%relationshipClassName%}(const DbClientPtr &clientPtr,
{%relationshipClassInde%} const std::function<void(std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>)> &rcb,
{%relationshipClassInde%} const drogon::orm::ExceptionCallback &ecb) const;
{%relationshipClassInde%} const ExceptionCallback &ecb) const;
<%c++
}
}
}
%>
private:
friend drogon::orm::Mapper<[[className]]>;
friend drogon::orm::BaseBuilder<[[className]], true, true>;
friend drogon::orm::BaseBuilder<[[className]], true, false>;
friend drogon::orm::BaseBuilder<[[className]], false, true>;
friend drogon::orm::BaseBuilder<[[className]], false, false>;
friend Mapper<[[className]]>;
#ifdef __cpp_impl_coroutine
friend drogon::orm::CoroMapper<[[className]]>;
friend CoroMapper<[[className]]>;
#endif
static const std::vector<std::string> &insertColumns() noexcept;
void outputArgs(drogon::orm::internal::SqlBinder &binder) const;
@ -368,7 +357,7 @@ if(@@.get<int>("hasPrimaryKey")<=1){
$$<<"\";\n";
}
%>
return sql;
return sql;
}
static const std::string &sqlForDeletingByPrimaryKey()
@ -400,7 +389,7 @@ if(@@.get<int>("hasPrimaryKey")<=1){
$$<<"\";\n";
}
%>
return sql;
return sql;
}
std::string sqlForInserting(bool &needSelection) const
{
@ -424,7 +413,7 @@ if(@@.get<int>("hasPrimaryKey")<=1){
$$<<" sql += \""<<cols[i].colName_<<",\";\n";
$$<<" ++parametersCount;\n";
continue;
}
}
if(cols[i].colType_.empty())
continue;
if(cols[i].hasDefaultVal_)
@ -445,13 +434,13 @@ if(@@.get<int>("hasPrimaryKey")<=1){
<%c++
}
if(@@.get<int>("hasPrimaryKey")>0||@@.get<std::string>("rdbms")=="postgresql")
{
{
%>
if(!dirtyFlag_[{%i%}])
{
needSelection=true;
}
<%c++
<%c++
}
}
else
@ -462,7 +451,7 @@ if(@@.get<int>("hasPrimaryKey")<=1){
sql += "{%cols[i].colName_%},";
++parametersCount;
}
<%c++
<%c++
}
}
if(selFlag)
@ -477,7 +466,7 @@ if(@@.get<int>("hasPrimaryKey")<=1){
}
else
sql += ") values (";
<%c++
if(@@.get<std::string>("rdbms")=="postgresql")
{
@ -485,8 +474,8 @@ if(@@.get<std::string>("rdbms")=="postgresql")
int placeholder=1;
char placeholderStr[64];
size_t n=0;
<%c++
}
<%c++
}
for(size_t i=0;i<cols.size();i++)
{
if(cols[i].isAutoVal_)
@ -498,7 +487,7 @@ if(@@.get<std::string>("rdbms")=="postgresql")
<%c++
}
continue;
}
}
if(cols[i].colType_.empty())
continue;
%>
@ -508,7 +497,7 @@ 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);
<%c++
}else
@ -519,7 +508,7 @@ if(@@.get<std::string>("rdbms")=="postgresql")
<%c++
}
%>
}
}
<%c++
if(cols[i].hasDefaultVal_&&@@.get<std::string>("rdbms")!="sqlite3")
{
@ -556,7 +545,7 @@ if(cols[i].hasDefaultVal_&&@@.get<std::string>("rdbms")!="sqlite3")
}
%>
LOG_TRACE << sql;
return sql;
return sql;
}
};
<%c++

View File

@ -23,11 +23,11 @@ class [[className]] : public drogon::Plugin<[[className]]>
[[className]]() {}
/// This method must be called by drogon to initialize and start the plugin.
/// It must be implemented by the user.
void initAndStart(const Json::Value &config) override;
virtual void initAndStart(const Json::Value &config) override;
/// This method must be called by drogon to shutdown the plugin.
/// It must be implemented by the user.
void shutdown() override;
virtual void shutdown() override;
};
<%c++for(size_t i=0;i<namespaceVector.size();i++){%>

View File

@ -19,7 +19,6 @@ auto modelName = tableInfo.get<std::string>("className");
$$<<"#include \""<<modelName<<".h\"\n";
bool hasPrimaryKey = (tableInfo.get<int>("hasPrimaryKey")==1);
$$<<"using namespace drogon;\n";
$$<<"using namespace drogon::orm;\n";
$$<<"using namespace drogon_model::"<<tableInfo.get<std::string>("dbName");
auto &schema=tableInfo.get<std::string>("schema");
@ -84,4 +83,4 @@ class [[className]]Base : public RestfulController
{
$$<<"}\n";
}
%>
%>

View File

@ -6,9 +6,9 @@ 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)
# target_link_libraries(${PROJECT_NAME}_test PRIVATE drogon)
#
# and comment out the following lines
target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)
ParseAndAddDrogonTests(${PROJECT_NAME})
ParseAndAddDrogonTests(${PROJECT_NAME})

View File

@ -15,25 +15,20 @@
#include "version.h"
#include <drogon/config.h>
#include <drogon/version.h>
#include <drogon/utils/Utilities.h>
#include <trantor/net/Resolver.h>
#include <trantor/utils/Utilities.h>
#include <iostream>
using namespace drogon_ctl;
static const char banner[] =
R"( _
__| |_ __ ___ __ _ ___ _ __
/ _` | '__/ _ \ / _` |/ _ \| '_ \
| (_| | | | (_) | (_| | (_) | | | |
\__,_|_| \___/ \__, |\___/|_| |_|
|___/
)";
" _ \n"
" __| |_ __ ___ __ _ ___ _ __ \n"
" / _` | '__/ _ \\ / _` |/ _ \\| '_ \\ \n"
"| (_| | | | (_) | (_| | (_) | | | |\n"
" \\__,_|_| \\___/ \\__, |\\___/|_| |_|\n"
" |___/ \n";
void version::handleCommand(std::vector<std::string> &parameters)
{
const auto tlsBackend = trantor::utils::tlsBackend();
const bool tlsSupported = drogon::utils::supportsTls();
std::cout << banner << std::endl;
std::cout << "A utility for drogon" << std::endl;
std::cout << "Version: " << DROGON_VERSION << std::endl;
@ -43,16 +38,25 @@ void version::handleCommand(std::vector<std::string> &parameters)
<< "\n Compilation flags: " << COMPILATION_FLAGS
<< INCLUDING_DIRS << std::endl;
std::cout << "Libraries: \n postgresql: "
<< (USE_POSTGRESQL ? "yes" : "no") << " (pipeline mode: "
<< (USE_POSTGRESQL ? "yes" : "no") << " (batch mode: "
<< (LIBPQ_SUPPORTS_BATCH_MODE ? "yes)\n" : "no)\n")
<< " mariadb: " << (USE_MYSQL ? "yes\n" : "no\n")
<< " sqlite3: " << (USE_SQLITE3 ? "yes\n" : "no\n");
std::cout << " ssl/tls backend: " << tlsBackend << "\n";
#ifdef OpenSSL_FOUND
std::cout << " openssl: yes\n";
#else
std::cout << " openssl: no\n";
#endif
#ifdef USE_BROTLI
std::cout << " brotli: yes\n";
#else
std::cout << " brotli: no\n";
#endif
#ifdef Boost_FOUND
std::cout << " boost: yes\n";
#else
std::cout << " boost: no\n";
#endif
#ifdef USE_REDIS
std::cout << " hiredis: yes\n";
#else
@ -60,9 +64,4 @@ void version::handleCommand(std::vector<std::string> &parameters)
#endif
std::cout << " c-ares: "
<< (trantor::Resolver::isCAresUsed() ? "yes\n" : "no\n");
#ifdef HAS_YAML_CPP
std::cout << " yaml-cpp: yes\n";
#else
std::cout << " yaml-cpp: no\n";
#endif
}

View File

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

View File

@ -24,18 +24,6 @@ drogon_create_views(login_session
add_executable(jsonstore jsonstore/main.cc)
add_executable(redis_simple redis/main.cc
redis/controllers/Client.cc
redis/controllers/WsClient.cc)
add_executable(redis_chat redis_chat/main.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
benchmark
client
@ -44,15 +32,11 @@ set(example_targets
helloworld
file_upload
login_session
jsonstore
redis_simple
redis_chat
async_stream
cors)
jsonstore)
# 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.
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})
target_compile_options(${target} PRIVATE -Wall -Wextra -Werror)
endforeach()

View File

@ -2,21 +2,18 @@
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!"
2. [client_example](https://github.com/drogonframework/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
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
5. [file_upload](https://github.com/drogonframework/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
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
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
9. [redis](https://github.com/drogonframework/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
11. [redis_cache](https://github.com/drogonframework/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
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
1. [helloworld](https://github.com/an-tao/drogon/tree/master/examples/helloworld) - The multiple ways of "Hello, World!"
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/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/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/an-tao/drogon/tree/master/examples/file_upload) - How to handle file uploads in Drogon
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.
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/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/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) - Example WebSocker chat room server
### [TechEmpower Framework Benchmarks](https://github.com/TechEmpower/FrameworkBenchmarks) test suite
@ -24,4 +21,4 @@ I created a benchmark suite for the `tfb`, see [here](https://github.com/TechEmp
### 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"
void BenchmarkCtrl::asyncHandleHttpRequest(
const HttpRequestPtr &,
std::function<void(const HttpResponsePtr &)> &&callback)

View File

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

View File

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

View File

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

View File

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

View File

@ -1,12 +1,6 @@
#include <drogon/drogon.h>
#include <future>
#include <iostream>
#ifdef __linux__
#include <sys/socket.h>
#include <netinet/tcp.h>
#endif
#include <future>
using namespace drogon;
@ -17,28 +11,6 @@ int main()
trantor::Logger::setLogLevel(trantor::Logger::kTrace);
{
auto client = HttpClient::newHttpClient("http://www.baidu.com");
client->setSockOptCallback([](int fd) {
std::cout << "setSockOptCallback:" << fd << std::endl;
#ifdef __linux__
int optval = 10;
::setsockopt(fd,
SOL_TCP,
TCP_KEEPCNT,
&optval,
static_cast<socklen_t>(sizeof optval));
::setsockopt(fd,
SOL_TCP,
TCP_KEEPIDLE,
&optval,
static_cast<socklen_t>(sizeof optval));
::setsockopt(fd,
SOL_TCP,
TCP_KEEPINTVL,
&optval,
static_cast<socklen_t>(sizeof optval));
#endif
});
auto req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/s");
@ -48,15 +20,7 @@ int main()
for (int i = 0; i < 10; ++i)
{
client->sendRequest(
req, [](ReqResult result, const HttpResponsePtr &response) {
if (result != ReqResult::Ok)
{
std::cout
<< "error while sending request to server! result: "
<< result << std::endl;
return;
}
req, [](ReqResult, const HttpResponsePtr &response) {
std::cout << "receive response!" << std::endl;
// auto headers=response.
++nth_resp;
@ -72,8 +36,6 @@ int main()
std::cout << "count=" << nth_resp << std::endl;
});
}
std::cout << "requestsBufferSize:" << client->requestsBufferSize()
<< std::endl;
}
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

@ -43,4 +43,4 @@ int main()
.setUploadPath("./uploads")
.addListener("127.0.0.1", 8848)
.run();
}
}

View File

@ -37,4 +37,4 @@ class SayHello : public HttpController<SayHello>
"Hi there, this is another hello from the SayHello Controller");
callback(resp);
}
};
};

View File

@ -14,7 +14,7 @@ class HelloViewController : public HttpSimpleController<HelloViewController>
// Also unlike HttpContoller, HttpSimpleController does not automatically
// prepend the namespace and class name to the path. Thus the path of this
// controller is at "/view".
PATH_ADD("/view");
PATH_ADD("/view")
PATH_LIST_END
void asyncHandleHttpRequest(
@ -26,4 +26,4 @@ class HelloViewController : public HttpSimpleController<HelloViewController>
auto resp = HttpResponse::newHttpViewResponse("HelloView", data);
callback(resp);
}
};
};

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>
using namespace drogon;
@ -16,10 +8,8 @@ int main()
// sent to Drogon
app().registerHandler(
"/",
[](const HttpRequestPtr &request,
[](const HttpRequestPtr &,
std::function<void(const HttpResponsePtr &)> &&callback) {
LOG_INFO << "connected:"
<< (request->connected() ? "true" : "false");
auto resp = HttpResponse::newHttpResponse();
resp->setBody("Hello, World!");
callback(resp);
@ -71,23 +61,6 @@ int main()
},
{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
// 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

View File

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

View File

@ -50,8 +50,8 @@ class JsonStore : public HttpController<JsonStore>
ADD_METHOD_VIA_REGEX(JsonStore::updateItem, "/([a-f0-9]{64})/(.*)", Put);
METHOD_LIST_END
void getToken(const HttpRequestPtr &,
std::function<void(const HttpResponsePtr &)> &&callback)
void getToken(const HttpRequestPtr&,
std::function<void(const HttpResponsePtr&)>&& callback)
{
std::string randomString = getRandomString(64);
Json::Value res;
@ -60,10 +60,10 @@ class JsonStore : public HttpController<JsonStore>
callback(HttpResponse::newHttpJsonResponse(std::move(res)));
}
void getItem(const HttpRequestPtr &,
std::function<void(const HttpResponsePtr &)> &&callback,
const std::string &token,
const std::string &path)
void getItem(const HttpRequestPtr&,
std::function<void(const HttpResponsePtr&)>&& callback,
const std::string& token,
const std::string& path)
{
auto itemPtr = [this, &token]() -> std::shared_ptr<DataItem> {
// It is possible that the item is being removed while another
@ -81,12 +81,12 @@ class JsonStore : public HttpController<JsonStore>
return;
}
auto &item = *itemPtr;
auto& item = *itemPtr;
// Prevents another thread from writing to the same item while this
// thread reads. Could cause blockage if multiple clients are asking to
// read the same object. But that should be rare.
std::lock_guard<std::mutex> lock(item.mtx);
Json::Value *valuePtr = walkJson(item.item, path);
Json::Value* valuePtr = walkJson(item.item, path);
if (valuePtr == nullptr)
{
@ -98,10 +98,10 @@ class JsonStore : public HttpController<JsonStore>
callback(resp);
}
void updateItem(const HttpRequestPtr &req,
std::function<void(const HttpResponsePtr &)> &&callback,
const std::string &token,
const std::string &path)
void updateItem(const HttpRequestPtr& req,
std::function<void(const HttpResponsePtr&)>&& callback,
const std::string& token,
const std::string& path)
{
auto jsonPtr = req->jsonObject();
auto itemPtr = [this, &token]() -> std::shared_ptr<DataItem> {
@ -121,9 +121,9 @@ class JsonStore : public HttpController<JsonStore>
return;
}
auto &item = *itemPtr;
auto& item = *itemPtr;
std::lock_guard<std::mutex> lock(item.mtx);
Json::Value *valuePtr = walkJson(item.item, path, 1);
Json::Value* valuePtr = walkJson(item.item, path, 1);
if (valuePtr == nullptr)
{
@ -137,9 +137,9 @@ class JsonStore : public HttpController<JsonStore>
callback(makeSuccessResponse());
}
void createItem(const HttpRequestPtr &req,
std::function<void(const HttpResponsePtr &)> &&callback,
const std::string &token)
void createItem(const HttpRequestPtr& req,
std::function<void(const HttpResponsePtr&)>&& callback,
const std::string& token)
{
auto jsonPtr = req->jsonObject();
if (jsonPtr == nullptr)
@ -163,9 +163,9 @@ class JsonStore : public HttpController<JsonStore>
}
}
void deleteItem(const HttpRequestPtr &,
std::function<void(const HttpResponsePtr &)> &&callback,
const std::string &token)
void deleteItem(const HttpRequestPtr&,
std::function<void(const HttpResponsePtr&)>&& callback,
const std::string& token)
{
std::lock_guard<std::mutex> lock(storageMtx_);
dataStore_.erase(token);
@ -174,19 +174,19 @@ class JsonStore : public HttpController<JsonStore>
}
protected:
static Json::Value *walkJson(Json::Value &json,
const std::string &path,
static Json::Value* walkJson(Json::Value& json,
const std::string& path,
size_t ignore_back = 0)
{
auto pathElem = utils::splitString(path, "/", false);
if (pathElem.size() >= ignore_back)
pathElem.resize(pathElem.size() - ignore_back);
Json::Value *valuePtr = &json;
for (const auto &elem : pathElem)
Json::Value* valuePtr = &json;
for (const auto& elem : pathElem)
{
if (valuePtr->isArray())
{
Json::Value &value = (*valuePtr)[std::stoi(elem)];
Json::Value& value = (*valuePtr)[std::stoi(elem)];
if (value.isNull())
return nullptr;
@ -194,7 +194,7 @@ class JsonStore : public HttpController<JsonStore>
}
else
{
Json::Value &value = (*valuePtr)[elem];
Json::Value& value = (*valuePtr)[elem];
if (value.isNull())
return nullptr;
@ -211,4 +211,4 @@ class JsonStore : public HttpController<JsonStore>
int main()
{
app().addListener("127.0.0.1", 8848).run();
}
}

View File

@ -65,4 +65,4 @@ int main()
.enableSession(24h)
.addListener("127.0.0.1", 8848)
.run();
}
}

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)
# 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")
find_package(Boost 1.61.0 REQUIRED)
target_include_directories(${PROJECT_NAME} PRIVATE ${Boost_INCLUDE_DIRS})

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