Compare commits

..

No commits in common. "master" and "v1.0.0-beta9" have entirely different histories.

570 changed files with 15557 additions and 79180 deletions

View File

@ -21,24 +21,23 @@ AlwaysBreakTemplateDeclarations: true
BinPackArguments: false BinPackArguments: false
BinPackParameters: false BinPackParameters: false
BraceWrapping: BraceWrapping:
AfterClass: true AfterClass: false
AfterControlStatement: Always AfterControlStatement: false
AfterEnum: true AfterEnum: false
AfterFunction: true AfterFunction: false
AfterNamespace: true AfterNamespace: false
AfterObjCDeclaration: false AfterObjCDeclaration: false
AfterStruct: true AfterStruct: false
AfterUnion: true AfterUnion: false
AfterExternBlock: true AfterExternBlock: false
BeforeCatch: true BeforeCatch: false
BeforeElse: true BeforeElse: false
IndentBraces: false IndentBraces: false
SplitEmptyFunction: true SplitEmptyFunction: true
SplitEmptyRecord: true SplitEmptyRecord: true
SplitEmptyNamespace: true SplitEmptyNamespace: true
AfterCaseLabel: true
BreakBeforeBinaryOperators: None BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom BreakBeforeBraces: Allman
BreakBeforeInheritanceComma: false BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true BreakBeforeTernaryOperators: true
@ -53,7 +52,7 @@ ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4 ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4 ContinuationIndentWidth: 4
Cpp11BracedListStyle: true Cpp11BracedListStyle: true
DerivePointerAlignment: false DerivePointerAlignment: true
DisableFormat: false DisableFormat: false
FixNamespaceComments: true FixNamespaceComments: true
ForEachMacros: ForEachMacros:
@ -75,7 +74,6 @@ IndentCaseLabels: true
IndentPPDirectives: None IndentPPDirectives: None
IndentWidth: 4 IndentWidth: 4
IndentWrappedFunctionNames: false IndentWrappedFunctionNames: false
InsertNewlineAtEOF: true
JavaScriptQuotes: Leave JavaScriptQuotes: Leave
JavaScriptWrapImports: true JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false KeepEmptyLinesAtTheStartOfBlocks: false
@ -95,8 +93,7 @@ PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10 PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000 PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 2000 PenaltyReturnTypeOnItsOwnLine: 2000
PointerAlignment: Right PointerAlignment: Left
ReferenceAlignment: Right
RawStringFormats: RawStringFormats:
- Language: Cpp - Language: Cpp
Delimiters: Delimiters:
@ -126,8 +123,7 @@ RawStringFormats:
CanonicalDelimiter: '' CanonicalDelimiter: ''
BasedOnStyle: google BasedOnStyle: google
ReflowComments: true ReflowComments: true
SeparateDefinitionBlocks: Always SortIncludes: false
SortIncludes: Never
SortUsingDeclarations: true SortUsingDeclarations: true
SpaceAfterCStyleCast: false SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true SpaceAfterTemplateKeyword: true

12
.github/FUNDING.yml vendored
View File

@ -1,12 +0,0 @@
# These are supported funding model platforms
github: drogonframework
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # ['https://paypal.me/antao2019']

View File

@ -1,17 +1,10 @@
--- ---
name: Bug report name: Bug report
about: Create a report to help us improve about: Create a report to help us improve
title: '' labels:
labels: ''
assignees: ''
--- ---
**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** **Describe the bug**
A clear and concise description of what the bug is. A clear and concise description of what the bug is.

View File

@ -1,17 +1,10 @@
--- ---
name: Feature request name: Feature request
about: Suggest an idea for this project about: Suggest an idea for this project
title: '' labels:
labels: ''
assignees: ''
--- ---
**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.** **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 [...] 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,237 +0,0 @@
name: Build & Test
on:
push:
branches: [master]
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
strategy:
fail-fast: false
matrix:
link: ["STATIC", "SHARED"]
steps:
- name: Checkout Drogon source code
uses: actions/checkout@v4
with:
submodules: true
fetch-depth: 0
- name: Install dependencies
run: pip install conan
- name: Create build directory
run: mkdir build
- name: Install conan packages
working-directory: ./build
run: |
conan profile detect
conan install .. -s compiler="msvc" -sbuild_type=Debug --build=missing -s compiler.cppstd=17
- 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
- name: Build
run: cmake --build build --target install --parallel
- name: Test
shell: bash
run: ./test.sh -w
macos:
runs-on: macos-${{ matrix.osver }}
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 }}
steps:
- name: Checkout Drogon source code
uses: actions/checkout@v4
with:
submodules: true
fetch-depth: 0
- name: Install dependencies
run: |
# Installing packages might fail as the github image becomes outdated
sudo apt update
# These aren't available or don't work well in vcpkg
sudo apt-get install -y libjsoncpp-dev uuid-dev libssl-dev zlib1g-dev libsqlite3-dev
sudo apt-get install -y ninja-build libbrotli-dev
sudo apt-get install -y libspdlog-dev
- name: Install postgresql
run: |
sudo apt-get --purge remove postgresql postgresql-doc postgresql-common postgresql-client-common
sudo apt-get -y install postgresql-all
- name: Install g++-13
if: startsWith(matrix.compiler.cxx, 'g++') && matrix.compiler.ver == 13
run: |
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt-get install g++-${{ matrix.compiler.ver }}
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 13
- name: Install Clang
if: startsWith(matrix.compiler.cxx, 'clang') && matrix.compiler.ver < 13
run: sudo apt-get install clang-${{ matrix.compiler.ver }}
- name: Install Clang
if: startsWith(matrix.compiler.cxx, 'clang') && matrix.compiler.ver >= 13
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x ./llvm.sh
sudo ./llvm.sh ${{ matrix.compiler.ver }}
- name: Export `shared`
run: |
[[ ${{ matrix.link }} == "SHARED" ]] && shared="ON" || shared="OFF"
echo "shared=$shared" >> $GITHUB_ENV
- name: Create Build Environment & Configure Cmake
# Some projects don't allow in-source building, so create a separate build directory
# We'll use this as our working directory for all subsequent commands
if: matrix.compiler.feature != 'coroutines'
run: |
cmake -B build -G Ninja \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DBUILD_TESTING=on \
-DUSE_SPDLOG=ON \
-DBUILD_SHARED_LIBS=$shared
- name: Create Build Environment & Configure Cmake (coroutines)
# Some projects don't allow in-source building, so create a separate build directory
# We'll use this as our working directory for all subsequent commands
if: matrix.compiler.feature == 'coroutines'
run: |
cmake -B build -G Ninja \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DBUILD_TESTING=on \
-DUSE_SPDLOG=ON \
-DCMAKE_CXX_FLAGS="-fcoroutines" \
-DBUILD_SHARED_LIBS=$shared \
- name: Build
working-directory: ./build
# Execute the build. You can specify a specific target with "--target <NAME>"
run: ninja && sudo ninja install
- name: Prepare for testing
run: |
sudo systemctl start postgresql
sleep 1
sudo -u postgres psql -c "ALTER USER postgres WITH PASSWORD '12345'" 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

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

16
.gitignore vendored
View File

@ -31,19 +31,11 @@
*.out *.out
*.app *.app
build/ build
cmake-build-debug/ cmake-build-debug
cmake-build-debug-visual-studio/ .idea
.idea/ lib/inc/drogon/version.h
html/ html/
latex/ latex/
.vscode .vscode
*.kdev4 *.kdev4
.cproject
.project
.settings/
.vs/
CMakeSettings.json
install
trace.json
.cache/

1
.gitmodules vendored
View File

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

51
.travis.yml Normal file
View File

@ -0,0 +1,51 @@
matrix:
include:
- os: linux
dist: xenial
- os: osx
osx_image: xcode11
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
- build-essential
- cmake
- boost1.67
homebrew:
packages:
- jsoncpp
- ossp-uuid
- openssl
- cmake
- libtool
- lz4
- mariadb
- sqlite3
before_script:
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then
brew services restart postgresql;
sleep 2;
psql -c 'create user postgres superuser;' postgres;
fi
services:
- postgresql
script:
- ./build.sh -t && ./test.sh -t

899
CMakeLists.txt Normal file → Executable file

File diff suppressed because it is too large Load Diff

View File

@ -1,78 +0,0 @@
# Contributing
**Drogon** is an open source project at its heart and every contribution is
welcome. By participating in this project you agree to stick to common sense and
contribute in an overall positive way.
## Getting Started
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
make sure to have the `drogon_ctl` executable in your shells `PATH`
environment variable in case you use a terminal.
Now you can create branches, start adding features & bugfixes to the code, and
[create pull requests](https://github.com/an-tao/drogon/compare).
## Pull Requests
Feel free to [create a pull request](https://github.com/an-tao/drogon/compare)
if you think you can contribute to the project. You will be listed as a
[contributor](https://github.com/an-tao/drogon/graphs/contributors), agree to
this document, and the
[LICENSE](https://github.com/an-tao/drogon/blob/master/LICENSE).
There are also some recommendations you can follow. These arent requirements,
but they will make the development more straightforward:
1. If you are unsure about a specific change, have questions, or want to get
feedback about a feature you want to introduce, [open a new
issue](https://github.com/an-tao/drogon/issues) (please make sure that there
is no previous issue about a similar topic).
1. You should branch off the current state of the `master` branch, and also
merge it into your local branch before creating a pull request if there were
other changes introduced in the meantime.
1. You can use the following branch names to make your intent clearer:
* `bugfix/123-fix-template-parser` when you want to fix a bug in the
template parser.
* `feature/123-add-l10n-and-i18n` if you want to add localization (l10n) and
internationalization (i18n) as a new feature to the project.
* If theres no open issue and no need to open one you can skip the number,
and just use the descriptive part: `bugfix/fix-typo-in-docs`.
1. Write a brief, but good, and descriptive commit message / pull request title in English,
e. g. “Added Internationalization and Localization”.
If you follow these recommendations your pull request will have more success:
1. Keep the style consistent to the project, when in doubt refer to the [Google
C++ Style Guide](https://google.github.io/styleguide/cppguide.html#C++_Version).
1. Please write all comments in English. Comments for new public API introduced by
your pull request must be added and written in [Doxygen](http://www.doxygen.nl/) format.
1. Format the code with `clang-format` (>= 8.0.0). The configuration is already
provided in the `.clang-format` file, just run the `./format.sh` script
before submitting your pull request.
1. Install [Google Test](https://github.com/google/googletest), and write a test
case.
1. In case it is a bugfix, its best to write a test that breaks in the old
version, but works in the new one. This way regressions can be tracked
over time.
1. If you add a feature, it is best to write the test as if it would be an
example how to use the newly introduced feature and to test all major,
newly introduced code.
## Project Maintainers & Collaborators
In addition to the guidelines mentioned above, collaborators with write access
to the repository should also follow these guidelines:
1. If there are new tests as part of the pull request, you should make sure that
they succeed.
1. When merging **Pull Requests** you should use the option *Squash & Merge* and
chose a descriptive commit message for the bugfix / feature (if not already
done by the individual contributor).
This way the history in the `master` branch will be free of small
corrections and easier to follow for people who arent engaged in the
project on a day-to-day basis.

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

27
Dockerfile Normal file
View File

@ -0,0 +1,27 @@
FROM ubuntu:18.04
RUN apt-get update -yqq \
&& apt-get install -yqq --no-install-recommends software-properties-common \
sudo curl wget cmake locales git gcc-8 g++-8 \
openssl libssl-dev libjsoncpp-dev uuid-dev zlib1g-dev \
postgresql-server-dev-all libmariadbclient-dev libsqlite3-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-8 \
CXX=g++-8 \
AR=gcc-ar-8 \
RANLIB=gcc-ranlib-8 \
IROOT=/install
ENV DROGON_ROOT="$IROOT/drogon"
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
RUN ./build.sh

View File

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

View File

@ -1,18 +1,17 @@
![](https://github.com/an-tao/drogon/wiki/images/drogon-white17.jpg) ![](https://github.com/an-tao/drogon/wiki/images/drogon-white.jpg)
[![Build Status](https://github.com/drogonframework/drogon/actions/workflows/cmake.yml/badge.svg?branch=master)](https://github.com/drogonframework/drogon/actions) [![Build Status](https://travis-ci.com/an-tao/drogon.svg?branch=master)](https://travis-ci.com/an-tao/drogon)
[![Conan Center](https://img.shields.io/conan/v/drogon)](https://conan.io/center/recipes/drogon) [![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 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) [![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/an-tao/drogon.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/an-tao/drogon/context:cpp)
[![Join our Discord](https://dcbadge.vercel.app/api/server/3DvHY6Ewuj?style=flat)](https://discord.gg/3DvHY6Ewuj) [![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)
[![Docker image](https://img.shields.io/badge/Docker-image-blue.svg)](https://cloud.docker.com/u/drogonframework/repository/docker/drogonframework/drogon) [![Docker image](https://img.shields.io/badge/Docker-image-blue.svg)](https://cloud.docker.com/u/drogonframework/repository/docker/drogonframework/drogon)
English | [简体中文](./README.zh-CN.md) | [繁體中文](./README.zh-TW.md)
### Overview ### Overview
**Drogon** is a C++17/20 based HTTP application framework. Drogon can be used to easily build various types of web application server programs using C++. **Drogon** is the name of a dragon from the American TV series *Game of Thrones*, which I really enjoy. **Drogon** is a C++14/17-based HTTP application framework. Drogon can be used to easily build various types of web application server programs using C++. **Drogon** is the name of a dragon in the American TV series "Game of Thrones" that I really like.
Drogon is a cross-platform framework, It supports Linux, macOS, FreeBSD, OpenBSD, HaikuOS, and Windows. Its main features are as follows: Drogon's main application platform is Linux. It also supports Mac OS and FreeBSD. Currently, it does not support windows. Its main features are as follows:
* Use a non-blocking I/O network lib based on epoll (kqueue under macOS/FreeBSD) to provide high-concurrency, high-performance network IO, please visit the [TFB Tests Results](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=composite) for more details; * Use a non-blocking I/O network lib based on epoll (kqueue under MacOS/FreeBSD) to provide high-concurrency, high-performance network IO, please visit the [benchmarks](https://github.com/an-tao/drogon/wiki/13-Benchmarks) page and [TFB Live Results](https://tfb-status.techempower.com/) for more details;
* Provide a completely asynchronous programming mode; * Provide a completely asynchronous programming mode;
* Support Http1.0/1.1 (server side and client side); * Support Http1.0/1.1 (server side and client side);
* Based on template, a simple reflection mechanism is implemented to completely decouple the main program framework, controllers and views. * Based on template, a simple reflection mechanism is implemented to completely decouple the main program framework, controllers and views.
@ -25,17 +24,15 @@ Drogon is a cross-platform framework, It supports Linux, macOS, FreeBSD, OpenBSD
* Support WebSocket (server side and client side); * Support WebSocket (server side and client side);
* Support JSON format request and response, very friendly to the Restful API application development; * Support JSON format request and response, very friendly to the Restful API application development;
* Support file download and upload; * Support file download and upload;
* Support gzip, brotli compression transmission; * Support gzip compression transmission;
* Support pipelining; * Support pipelining;
* Provide a lightweight command line tool, drogon_ctl, to simplify the creation of various classes in Drogon and the generation of view code; * Provide a lightweight command line tool, drogon_ctl, to simplify the creation of various classes in Drogon and the generation of view code;
* Support non-blocking I/O based asynchronously reading and writing database (PostgreSQL and MySQL(MariaDB) database); * Support non-blocking I/O based asynchronously reading and writing database (PostgreSQL and MySQL(MariaDB) database);
* Support asynchronously reading and writing sqlite3 database based on thread pool; * Support asynchronously reading and writing sqlite3 database based on thread pool;
* Support Redis with asynchronous reading and writing;
* Support ARM Architecture; * Support ARM Architecture;
* Provide a convenient lightweight ORM implementation that supports for regular object-to-database bidirectional mapping; * Provide a convenient lightweight ORM implementation that supports for regular object-to-database bidirectional mapping;
* Support plugins which can be installed by the configuration file at load time; * Support plugins which can be installed by the configuration file at load time;
* Support AOP with built-in joinpoints. * Support AOP with build-in joinpoints.
* Support C++ coroutines
## A very simple example ## A very simple example
@ -49,7 +46,7 @@ using namespace drogon;
int main() int main()
{ {
app().setLogPath("./") app().setLogPath("./")
.setLogLevel(trantor::Logger::kWarn) .setLogLevel(trantor::Logger::WARN)
.addListener("0.0.0.0", 80) .addListener("0.0.0.0", 80)
.setThreadNum(16) .setThreadNum(16)
.enableRunAsDaemon() .enableRunAsDaemon()
@ -71,7 +68,7 @@ int main()
Drogon provides some interfaces for adding controller logic directly in the main() function, for example, user can register a handler like this in Drogon: Drogon provides some interfaces for adding controller logic directly in the main() function, for example, user can register a handler like this in Drogon:
```c++ ```c++
app().registerHandler("/test?username={name}", app.registerHandler("/test?username={name}",
[](const HttpRequestPtr& req, [](const HttpRequestPtr& req,
std::function<void (const HttpResponsePtr &)> &&callback, std::function<void (const HttpResponsePtr &)> &&callback,
const std::string &name) const std::string &name)
@ -95,7 +92,7 @@ using namespace drogon;
class TestCtrl:public drogon::HttpSimpleController<TestCtrl> class TestCtrl:public drogon::HttpSimpleController<TestCtrl>
{ {
public: 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_LIST_BEGIN
PATH_ADD("/test",Get); PATH_ADD("/test",Get);
PATH_LIST_END PATH_LIST_END
@ -114,7 +111,7 @@ void TestCtrl::asyncHandleHttpRequest(const HttpRequestPtr& req,
} }
``` ```
**Most of the above programs can be automatically generated by the command line tool `drogon_ctl` provided by drogon** (The command is `drogon_ctl create controller TestCtrl`). All the user needs to do is add their own business logic. In the example, the controller returns a `Hello, world!` string when the client accesses the `http://ip/test` URL. **Most of the above programs can be automatically generated by the command line tool `drogon_ctl` provided by drogon** (The cammand is `drogon_ctl create controller TestCtrl`). All the user needs to do is add their own business logic. In the example, the controller returns a `Hello, world!` string when the client accesses the `http://ip/test` URL.
For JSON format response, we create the controller as follows: For JSON format response, we create the controller as follows:
@ -126,7 +123,7 @@ using namespace drogon;
class JsonCtrl : public drogon::HttpSimpleController<JsonCtrl> class JsonCtrl : public drogon::HttpSimpleController<JsonCtrl>
{ {
public: 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_LIST_BEGIN
//list path definitions here; //list path definitions here;
PATH_ADD("/json", Get); PATH_ADD("/json", Get);
@ -183,40 +180,4 @@ As you can see, users can use the `HttpController` to map paths and parameters a
In addition, you can also find that all handler interfaces are in asynchronous mode, where the response is returned by a callback object. This design is for performance reasons because in asynchronous mode the drogon application can handle a large number of concurrent requests with a small number of threads. In addition, you can also find that all handler interfaces are in asynchronous mode, where the response is returned by a callback object. This design is for performance reasons because in asynchronous mode the drogon application can handle a large number of concurrent requests with a small number of threads.
After compiling all of the above source files, we get a very simple web application. This is a good start. **For more information, please visit the [documentation](https://drogonframework.github.io/drogon-docs/#/) on GitHub**. After compiling all of the above source files, we get a very simple web application. This is a good start. **for more information, please visit the [wiki](https://github.com/an-tao/drogon/wiki/01-Overview) site**
## 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 |
## 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,19 +1,17 @@
![](https://github.com/an-tao/drogon/wiki/images/drogon-white17.jpg) ![](https://github.com/an-tao/drogon/wiki/images/drogon-white.jpg)
[![Build Status](https://github.com/drogonframework/drogon/actions/workflows/cmake.yml/badge.svg?branch=master)](https://github.com/drogonframework/drogon/actions) [![Build Status](https://travis-ci.com/an-tao/drogon.svg?branch=master)](https://travis-ci.com/an-tao/drogon)
[![Conan Center](https://img.shields.io/conan/v/drogon)](https://conan.io/center/recipes/drogon) [![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 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) [![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/an-tao/drogon.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/an-tao/drogon/context:cpp)
[![Join our Discord](https://dcbadge.vercel.app/api/server/3DvHY6Ewuj?style=flat)](https://discord.gg/3DvHY6Ewuj) [![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)
[![Docker image](https://img.shields.io/badge/Docker-image-blue.svg)](https://cloud.docker.com/u/drogonframework/repository/docker/drogonframework/drogon) [![Docker image](https://img.shields.io/badge/Docker-image-blue.svg)](https://cloud.docker.com/u/drogonframework/repository/docker/drogonframework/drogon)
[English](./README.md) | 简体中文 | [繁體中文](./README.zh-TW.md) **Drogon**是一个基于C++14/17的Http应用框架使用Drogon可以方便的使用C++构建各种类型的Web应用服务端程序。
**Drogon**是一个基于C++17/20的Http应用框架使用Drogon可以方便的使用C++构建各种类型的Web应用服务端程序。
本版本库是github上[Drogon工程](https://github.com/an-tao/drogon)的镜像库。**Drogon**是作者非常喜欢的美剧《权力的游戏》中的一条龙的名字(汉译作卓耿)和龙有关但并不是dragon的误写为了不至于引起不必要的误会这里说明一下。 本版本库是github上[Drogon工程](https://github.com/an-tao/drogon)的镜像库。**Drogon**是作者非常喜欢的美剧《权力的游戏》中的一条龙的名字(汉译作卓耿)和龙有关但并不是dragon的误写为了不至于引起不必要的误会这里说明一下。
Drogon是一个跨平台框架它支持Linux也支持macOS、FreeBSDOpenBSDHaikuOSWindows。它的主要特点如下 Drogon的主要应用平台是Linux也支持Mac OS、FreeBSD目前还不支持Windows。它的主要特点如下
* 网络层使用基于epoll(macOS/FreeBSD下是kqueue)的非阻塞IO框架提供高并发、高性能的网络IO。详细请见[TFB Tests Results](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=composite) * 网络层使用基于epoll(MacOS/FreeBSD下是kqueue)的非阻塞IO框架提供高并发、高性能的网络IO。详细请见[性能测试](https://github.com/an-tao/drogon/wiki/13-%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95)和[TFB Live Results](https://tfb-status.techempower.com/)
* 全异步编程模式; * 全异步编程模式;
* 支持Http1.0/1.1(server端和client端) * 支持Http1.0/1.1(server端和client端)
* 基于template实现了简单的反射机制使主程序框架、控制器(controller)和视图(view)完全解耦; * 基于template实现了简单的反射机制使主程序框架、控制器(controller)和视图(view)完全解耦;
@ -26,180 +24,14 @@ Drogon是一个跨平台框架它支持Linux也支持macOS、FreeBSDOpe
* 支持websocket(server端和client端); * 支持websocket(server端和client端);
* 支持Json格式请求和应答, 对Restful API应用开发非常友好; * 支持Json格式请求和应答, 对Restful API应用开发非常友好;
* 支持文件下载和上传,支持sendfile系统调用 * 支持文件下载和上传,支持sendfile系统调用
* 支持gzip/brotli压缩传输; * 支持gzip压缩传输
* 支持pipelining * 支持pipelining
* 提供一个轻量的命令行工具drogon_ctl帮助简化各种类的创建和视图代码的生成过程 * 提供一个轻量的命令行工具drogon_ctl帮助简化各种类的创建和视图代码的生成过程
* 基于非阻塞IO实现的异步数据库读写目前支持PostgreSQL和MySQL(MariaDB)数据库; * 基于非阻塞IO实现的异步数据库读写目前支持PostgreSQL和MySQL(MariaDB)数据库;
* 基于线程池实现sqlite3数据库的异步读写提供与上文数据库相同的接口 * 基于线程池实现sqlite3数据库的异步读写提供与上文数据库相同的接口
* 支持Redis异步读写
* 支持ARM架构 * 支持ARM架构
* 方便的轻量级ORM实现支持常规的对象到数据库的双向映射操作 * 方便的轻量级ORM实现支持常规的对象到数据库的双向映射操作
* 支持插件,可通过配置文件在加载期动态拆装; * 支持插件,可通过配置文件在加载期动态拆装;
* 支持内建插入点的AOP * 支持内建插入点的AOP
* 支持C++协程
## 一个非常简单的例子 ### 更多详情请浏览 [wiki](https://github.com/an-tao/drogon/wiki/01-概述)
不像大多数C++框架那样drogon的主程序可以保持非常简单。 Drogon使用了一些小技巧使主程序和控制器解耦合. 控制器的路由设置可以在控制器类中定义或者配置文件中完成.
下面是一个典型的主程序的样子:
```c++
#include <drogon/drogon.h>
using namespace drogon;
int main()
{
app().setLogPath("./")
.setLogLevel(trantor::Logger::kWarn)
.addListener("0.0.0.0", 80)
.setThreadNum(16)
.enableRunAsDaemon()
.run();
}
```
如果使用配置文件,可以进一步简化成如下的样子:
```c++
#include <drogon/drogon.h>
using namespace drogon;
int main()
{
app().loadConfigFile("./config.json").run();
}
```
当然Drogon也提供了一些接口使用户可以在main()函数中直接添加控制器逻辑比如用户可以注册一个lambda处理器到drogon框架中如下所示
```c++
app().registerHandler("/test?username={name}",
[](const HttpRequestPtr& req,
std::function<void (const HttpResponsePtr &)> &&callback,
const std::string &name)
{
Json::Value json;
json["result"]="ok";
json["message"]=std::string("hello,")+name;
auto resp=HttpResponse::newHttpJsonResponse(json);
callback(resp);
},
{Get,"LoginFilter"});
```
这看起来是很方便但是这并不适用于复杂的应用试想假如有数十个或者数百个处理函数要注册进框架main()函数将膨胀到不可读的程度。显然让每个包含处理函数的类在自己的定义中完成注册是更好的选择。所以除非你的应用逻辑非常简单我们不推荐使用上述接口更好的实践是我们可以创建一个HttpSimpleController对象如下
```c++
/// The TestCtrl.h file
#pragma once
#include <drogon/HttpSimpleController.h>
using namespace drogon;
class TestCtrl:public drogon::HttpSimpleController<TestCtrl>
{
public:
void asyncHandleHttpRequest(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback) override;
PATH_LIST_BEGIN
PATH_ADD("/test",Get);
PATH_LIST_END
};
/// The TestCtrl.cc file
#include "TestCtrl.h"
void TestCtrl::asyncHandleHttpRequest(const HttpRequestPtr& req,
std::function<void (const HttpResponsePtr &)> &&callback)
{
//write your application logic here
auto resp = HttpResponse::newHttpResponse();
resp->setBody("<p>Hello, world!</p>");
resp->setExpiredTime(0);
callback(resp);
}
```
**上面程序的大部分代码都可以由`drogon_ctl`命令创建**(这个命令是`drogon_ctl create controller TestCtr`。用户所需做的就是添加自己的业务逻辑。在这个例子中当客户端访问URL`http://ip/test`时,控制器简单的返回了一个`Hello, world!`页面。
对于JSON格式的响应我们可以像下面这样创建控制器
```c++
/// The header file
#pragma once
#include <drogon/HttpSimpleController.h>
using namespace drogon;
class JsonCtrl : public drogon::HttpSimpleController<JsonCtrl>
{
public:
void asyncHandleHttpRequest(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback) override;
PATH_LIST_BEGIN
//list path definitions here;
PATH_ADD("/json", Get);
PATH_LIST_END
};
/// The source file
#include "JsonCtrl.h"
void JsonCtrl::asyncHandleHttpRequest(const HttpRequestPtr &req,
std::function<void(const HttpResponsePtr &)> &&callback)
{
Json::Value ret;
ret["message"] = "Hello, World!";
auto resp = HttpResponse::newHttpJsonResponse(ret);
callback(resp);
}
```
让我们更进一步通过HttpController类创建一个RESTful API的例子如下所示忽略了实现文件
```c++
/// The header file
#pragma once
#include <drogon/HttpController.h>
using namespace drogon;
namespace api
{
namespace v1
{
class User : public drogon::HttpController<User>
{
public:
METHOD_LIST_BEGIN
//use METHOD_ADD to add your custom processing function here;
METHOD_ADD(User::getInfo, "/{id}", Get); //path is /api/v1/User/{arg1}
METHOD_ADD(User::getDetailInfo, "/{id}/detailinfo", Get); //path is /api/v1/User/{arg1}/detailinfo
METHOD_ADD(User::newUser, "/{name}", Post); //path is /api/v1/User/{arg1}
METHOD_LIST_END
//your declaration of processing function maybe like this:
void getInfo(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, int userId) const;
void getDetailInfo(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, int userId) const;
void newUser(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, std::string &&userName);
public:
User()
{
LOG_DEBUG << "User constructor!";
}
};
} // namespace v1
} // namespace api
```
如你所见,通过`HttpController`用户可以同时映射路径和路径参数这对RESTful API应用来说非常方便。
另外,你可以发现前面所有的处理函数接口都是异步的,处理器的响应是通过回调对象返回的。这种设计是出于对高性能的考虑,因为在异步模式下,可以使用少量的线程(比如和处理器核心数相等的线程)处理大量的并发请求。
编译上述的所有源文件后我们得到了一个非常简单的web应用程序这是一个不错的开始。**请访问GitHub上的[文档](https://drogonframework.github.io/drogon-docs/#/CHN/CHN-01-%E6%A6%82%E8%BF%B0)**
## 贡献方式
欢迎您的贡献。 请阅读[贡献指南](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,198 +0,0 @@
![](https://github.com/an-tao/drogon/wiki/images/drogon-white17.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)
[![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 伺服器端程式。
這個版本庫是 GitHub 上 [Drogon](https://github.com/an-tao/drogon) 的鏡像庫。**Drogon** 是作者非常喜歡的美劇《冰與火之歌:權力遊戲》中的一條龍的名字(中文譯作卓耿),和龍有關但並不是 dragon 的誤寫,為了避免不必要的誤會在此說明。
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。
## 一個非常簡單的例子
不像大多數 C++ 框架drogon 的主程式可以非常簡單。Drogon 使用了一些小技巧使主程式和控制器解耦。控制器的路由設定可以在控制器類別中定義或在設定檔案中完成。
下面是一個典型主程式的樣子:
```c++
#include <drogon/drogon.h>
using namespace drogon;
int main()
{
app().setLogPath("./")
.setLogLevel(trantor::Logger::kWarn)
.addListener("0.0.0.0", 80)
.setThreadNum(16)
.enableRunAsDaemon()
.run();
}
```
如果使用設定檔案,可以進一步簡化成:
```c++
#include <drogon/drogon.h>
using namespace drogon;
int main()
{
app().loadConfigFile("./config.json").run();
}
```
當然Drogon 也提供了一些函式,讓使用者可以在 `main()` 函式中直接加入控制器邏輯,例如,使用者可以註冊一個 lambda 處理常式到 drogon 框架中,如下所示:
```c++
app().registerHandler("/test?username={name}",
[](const HttpRequestPtr& req,
std::function<void (const HttpResponsePtr &)> &&callback,
const std::string &name)
{
Json::Value json;
json["result"]="ok";
json["message"]=std::string("hello,")+name;
auto resp=HttpResponse::newHttpJsonResponse(json);
callback(resp);
},
{Get,"LoginFilter"});
```
這看起來很方便,但不適用於複雜的場景,試想如果有數十個或數百個處理函式要註冊進框架,`main()` 函式將變得難以閱讀。顯然,讓每個包含處理函式的類別在自己的定義中完成註冊是更好的選擇。所以,除非你的應用邏輯非常簡單,我們不建議使用上述介面,更好的做法是建立一個 HttpSimpleController 類別,如下:
```c++
/// The TestCtrl.h file
#pragma once
#include <drogon/HttpSimpleController.h>
using namespace drogon;
class TestCtrl:public drogon::HttpSimpleController<TestCtrl>
{
public:
void asyncHandleHttpRequest(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback) override;
PATH_LIST_BEGIN
PATH_ADD("/test",Get);
PATH_LIST_END
};
/// The TestCtrl.cc file
#include "TestCtrl.h"
void TestCtrl::asyncHandleHttpRequest(const HttpRequestPtr& req,
std::function<void (const HttpResponsePtr &)> &&callback)
{
//write your application logic here
auto resp = HttpResponse::newHttpResponse();
resp->setBody("<p>Hello, world!</p>");
resp->setExpiredTime(0);
callback(resp);
}
```
**上述程式的大部分程式碼都可以由 `drogon_ctl` 指令產生**(使用指令 `drogon_ctl create controller TestCtr`)。使用者只需要加入自己的業務邏輯。在這個範例中,當用戶端存取 URL `http://ip/test` 時,控制器簡單地回傳一個 `Hello, world!` 頁面。
對於 JSON 格式的回應,我們可以這樣建立控制器:
```c++
/// The header file
#pragma once
#include <drogon/HttpSimpleController.h>
using namespace drogon;
class JsonCtrl : public drogon::HttpSimpleController<JsonCtrl>
{
public:
void asyncHandleHttpRequest(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback) override;
PATH_LIST_BEGIN
//list path definitions here;
PATH_ADD("/json", Get);
PATH_LIST_END
};
/// The source file
#include "JsonCtrl.h"
void JsonCtrl::asyncHandleHttpRequest(const HttpRequestPtr &req,
std::function<void(const HttpResponsePtr &)> &&callback)
{
Json::Value ret;
ret["message"] = "Hello, World!";
auto resp = HttpResponse::newHttpJsonResponse(ret);
callback(resp);
}
```
讓我們更進一步,透過 HttpController 類別建立一個 RESTful API 的範例,如下所示(省略實作檔案):
```c++
/// The header file
#pragma once
#include <drogon/HttpController.h>
using namespace drogon;
namespace api
{
namespace v1
{
class User : public drogon::HttpController<User>
{
public:
METHOD_LIST_BEGIN
//use METHOD_ADD to add your custom processing function here;
METHOD_ADD(User::getInfo, "/{id}", Get); //path is /api/v1/User/{arg1}
METHOD_ADD(User::getDetailInfo, "/{id}/detailinfo", Get); //path is /api/v1/User/{arg1}/detailinfo
METHOD_ADD(User::newUser, "/{name}", Post); //path is /api/v1/User/{arg1}
METHOD_LIST_END
//your declaration of processing function maybe like this:
void getInfo(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, int userId) const;
void getDetailInfo(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, int userId) const;
void newUser(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, std::string &&userName);
public:
User()
{
LOG_DEBUG << "User constructor!";
}
};
} // namespace v1
} // namespace api
```
如你所見,透過 `HttpController` 類別,使用者可以同時對應路徑和路徑參數,這對 RESTful API 應用來說非常方便。
另外,你可以發現前面所有的處理函式介面都是非同步的,處理器的回應是透過回呼物件回傳的。這種設計是考慮到效能,因為在非同步模式下,可以使用少量的執行緒(例如和處理器核心數相等的執行緒)處理大量的並行請求。
編譯上述所有原始檔案後,我們得到了一個非常簡單的網頁應用程式,這是一個不錯的開始。**請瀏覽 GitHub 上的[文件](https://drogonframework.github.io/drogon-docs/#/CHN/CHN-01-%E6%A6%82%E8%BF%B0)**
## 貢獻方式
歡迎您的貢獻。請閱讀[貢獻指南](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
歡迎交流討論。

1
_config.yml Normal file
View File

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

View File

@ -1,4 +1,4 @@
#!/usr/bin/env bash #!/bin/bash
#build drogon #build drogon
function build_drogon() { function build_drogon() {
@ -28,11 +28,9 @@ function build_drogon() {
echo "Start building drogon ..." echo "Start building drogon ..."
if [ $1 -eq 1 ]; then if [ $1 -eq 1 ]; then
cmake .. -DBUILD_TESTING=YES $cmake_gen cmake .. -DMAKETEST=YES
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
else else
cmake .. -DCMAKE_BUILD_TYPE=release $cmake_gen cmake ..
fi fi
#If errors then exit #If errors then exit
@ -40,7 +38,7 @@ function build_drogon() {
exit -1 exit -1
fi fi
$make_program $make_flags make
#If errors then exit #If errors then exit
if [ "$?" != "0" ]; then if [ "$?" != "0" ]; then
@ -48,54 +46,15 @@ function build_drogon() {
fi fi
echo "Installing ..." echo "Installing ..."
$make_program install sudo make install
#Go back to the current directory #Go back to the current directory
cd $current_dir cd $current_dir
#Ok! #Ok!
} }
make_program=make if [ "$1" = "-t" ]; then
make_flags=''
cmake_gen=''
parallel=1
case $(uname) in
FreeBSD)
nproc=$(sysctl -n hw.ncpu)
;;
Darwin)
nproc=$(sysctl -n hw.ncpu) # sysctl -n hw.ncpu is the equivalent to nproc on macOS.
;;
*)
nproc=$(nproc)
;;
esac
# simulate ninja's parallelism
case nproc in
1)
parallel=$(( nproc + 1 ))
;;
2)
parallel=$(( nproc + 1 ))
;;
*)
parallel=$(( nproc + 2 ))
;;
esac
if [ -f /bin/ninja ]; then
make_program=ninja
cmake_gen='-GNinja'
else
make_flags="$make_flags -j$parallel"
fi
if [ "X$1" = "X-t" ]; then
build_drogon 1 build_drogon 1
elif [ "X$1" = "X-tshared" ]; then
build_drogon 2
else else
build_drogon 0 build_drogon 0
fi fi

View File

@ -1,72 +0,0 @@
# ##############################################################################
# function drogon_create_views(target source_path output_path
# [TRUE to use_path_as_namespace] [prefixed namespace])
# ##############################################################################
function(drogon_create_views arg)
if(ARGC LESS 3)
message(STATUS "arguments error when calling drogon_create_views")
return()
endif()
file(MAKE_DIRECTORY ${ARGV2})
file(GLOB_RECURSE SCP_LIST ${ARGV1}/*.csp)
foreach(cspFile ${SCP_LIST})
file(RELATIVE_PATH
inFile
${CMAKE_CURRENT_SOURCE_DIR}
${cspFile})
if(ARGC GREATER 3 AND ARGV3)
string(REPLACE "/"
"_"
f1
${inFile})
string(REPLACE "\\"
"_"
f2
${f1})
string(REPLACE ".csp"
""
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}
-o
${ARGV2}
${ns}
DEPENDS ${cspFile}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
VERBATIM)
set(VIEWSRC ${VIEWSRC} ${ARGV2}/${outputFile}.cc)
else()
get_filename_component(classname ${cspFile} NAME_WE)
add_custom_command(OUTPUT ${ARGV2}/${classname}.h ${ARGV2}/${classname}.cc
COMMAND drogon_ctl
ARGS
create
view
${inFile}
-o
${ARGV2}
DEPENDS ${cspFile}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
VERBATIM)
set(VIEWSRC ${VIEWSRC} ${ARGV2}/${classname}.cc)
endif()
endforeach()
target_sources(${ARGV0} PRIVATE ${VIEWSRC})
endfunction(drogon_create_views)

View File

@ -1,37 +0,0 @@
include(GNUInstallDirs)
set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
set(CPACK_PACKAGE_CONTACT "https://github.com/drogonframework/drogon")
set(CPACK_PACKAGE_NAME "${PROJECT_NAME}")
set(CPACK_PACKAGE_VERSION "${DROGON_VERSION}")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A C++14/17 based HTTP web application framework running on Linux/macOS/Unix/Windows")
# DEB
# Figure out dependencies automatically.
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
# Should be set automatically, but it is not.
execute_process(COMMAND dpkg --print-architecture
OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE
OUTPUT_STRIP_TRAILING_WHITESPACE)
# The default does not produce valid Debian package names.
set(CPACK_DEBIAN_FILE_NAME
"${CPACK_PACKAGE_NAME}_${CPACK_PACKAGE_VERSION}-0_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}.deb")
# RPM
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
# Figure out dependencies automatically.
set(CPACK_RPM_PACKAGE_AUTOREQ ON)
# Should be set automatically, but it is not.
execute_process(COMMAND uname -m
OUTPUT_VARIABLE CPACK_RPM_PACKAGE_ARCHITECTURE
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(CPACK_PACKAGE_FILE_NAME
"${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-0.${CPACK_RPM_PACKAGE_ARCHITECTURE}")
include(CPack)

View File

@ -1,79 +0,0 @@
#==================================================================================================#
# Adapted and re-written from Catch2 to work with Drogon Test #
# #
# Usage #
# 1. make sure this module is in the path or add this otherwise: #
# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake_modules/") #
# 2. make sure that you've enabled testing option for the project by the call: #
# enable_testing() #
# 3. add the lines to the script for testing target (sample CMakeLists.txt): #
# project(testing_target) #
# enable_testing() #
# #
# file(GLOB SOURCE_FILES "*.cpp") #
# add_executable(${PROJECT_NAME} ${SOURCE_FILES}) #
# #
# include(ParseAndAddDrogonTests) #
# ParseAndAddDrogonTests(${PROJECT_NAME}) #
#==================================================================================================#
cmake_minimum_required(VERSION 3.5...3.31)
# This removes the contents between
# - block comments (i.e. /* ... */)
# - full line comments (i.e. // ... )
# contents have been read into '${CppCode}'.
# !keep partial line comments
function(RemoveComments CppCode)
string(ASCII 2 CMakeBeginBlockComment)
string(ASCII 3 CMakeEndBlockComment)
string(REGEX REPLACE "/\\*" "${CMakeBeginBlockComment}" ${CppCode} "${${CppCode}}")
string(REGEX REPLACE "\\*/" "${CMakeEndBlockComment}" ${CppCode} "${${CppCode}}")
string(REGEX REPLACE "${CMakeBeginBlockComment}[^${CMakeEndBlockComment}]*${CMakeEndBlockComment}" "" ${CppCode} "${${CppCode}}")
string(REGEX REPLACE "\n[ \t]*//+[^\n]+" "\n" ${CppCode} "${${CppCode}}")
set(${CppCode} "${${CppCode}}" PARENT_SCOPE)
endfunction()
# Worker function
function(ParseFile SourceFile TestTarget)
set(FullSourcePath ${CMAKE_CURRENT_SOURCE_DIR}/${SourceFile})
if(NOT EXISTS ${FullSourcePath})
return()
endif()
file(STRINGS ${FullSourcePath} Contents NEWLINE_CONSUME)
# Remove block and fullline comments
RemoveComments(Contents)
# Find definition of test names
string(REGEX MATCHALL "[ \t]*DROGON_TEST[ \t]*\\\([a-zA-Z0-9_]+\\\)" Tests "${Contents}")
foreach(TestLine ${Tests})
# Strip newlines
string(REGEX REPLACE "\\\\\n|\n" "" TestLine "${TestLine}")
# Get the name of the test
string(REGEX REPLACE "[ \t]*DROGON_TEST[ \t]*" "" TestLine "${TestLine}")
string(REGEX MATCHALL "[a-zA-Z0-9_]+" TestName "${TestLine}")
# Validate that a test name and tags have been provided
list(LENGTH TestName TestNameLength)
if(NOT TestNameLength EQUAL 1)
message(FATAL_ERROR "${TestName} in ${SourceFile} is not a valid test name."
" Either a bug in the Drogon Test CMake parser or a bug in the test itself")
endif()
# Add the test and set its properties
add_test(NAME "${TestName}" COMMAND ${TestTarget} -r ${TestName} ${AdditionalCatchParameters})
endforeach()
endfunction()
# entry point
function(ParseAndAddDrogonTests TestTarget)
get_target_property(SourceFiles ${TestTarget} SOURCES)
foreach(SourceFile ${SourceFiles})
ParseFile(${SourceFile} ${TestTarget})
endforeach()
endfunction()

View File

@ -3,60 +3,25 @@
# DROGON_INCLUDE_DIRS - include directories for Drogon # DROGON_INCLUDE_DIRS - include directories for Drogon
# DROGON_LIBRARIES - libraries to link against # DROGON_LIBRARIES - libraries to link against
# DROGON_EXECUTABLE - the drogon_ctl executable # DROGON_EXECUTABLE - the drogon_ctl executable
# Drogon_FOUND # DROGON_FOUND
# This module defines the following IMPORTED target: # This module defines the following IMPORTED target:
# Drogon::Drogon # Drogon::Drogon
@PACKAGE_INIT@ @PACKAGE_INIT@
include(CMakeFindDependencyMacro) if(NOT TRANTOR_FOUND)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) # find trantor
find_package(Trantor REQUIRED)
find_dependency(Jsoncpp REQUIRED)
find_dependency(Trantor REQUIRED)
if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD" AND NOT WIN32)
find_dependency(UUID REQUIRED)
endif(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD" AND NOT WIN32)
find_dependency(ZLIB REQUIRED)
if(@pg_FOUND@)
find_dependency(pg)
endif() endif()
if(@SQLite3_FOUND@)
find_dependency(SQLite3)
endif()
if(@MySQL_FOUND@)
find_dependency(MySQL)
endif()
if(@Brotli_FOUND@)
find_dependency(Brotli)
endif()
if(@COZ-PROFILER_FOUND@)
find_dependency(coz-profiler)
endif()
if(@Hiredis_FOUND@)
find_dependency(Hiredis)
endif()
if(@yaml-cpp_FOUND@)
find_dependency(yaml-cpp)
endif()
if(@BUILD_SHARED_LIBS@)
find_dependency(Threads)
endif()
if(@HAS_STD_FILESYSTEM_PATH@)
find_dependency(Filesystem)
find_package(Filesystem COMPONENTS Final REQUIRED)
endif()
# Our library dependencies (contains definitions for IMPORTED targets) # Our library dependencies (contains definitions for IMPORTED targets)
get_filename_component(DROGON_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) get_filename_component(DROGON_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
if(NOT TARGET Drogon::Drogon) if(NOT TARGET Drogon::Drogon)
include("${DROGON_CMAKE_DIR}/DrogonTargets.cmake") include("${DROGON_CMAKE_DIR}/DrogonTargets.cmake")
include("${DROGON_CMAKE_DIR}/DrogonUtilities.cmake")
include("${DROGON_CMAKE_DIR}/ParseAndAddDrogonTests.cmake")
endif() endif()
get_target_property(DROGON_INCLUDE_DIRS Drogon::Drogon INTERFACE_INCLUDE_DIRECTORIES) get_target_property(DROGON_INCLUDE_DIRS Drogon::Drogon INTERFACE_INCLUDE_DIRECTORIES)
set(DROGON_LIBRARIES Drogon::Drogon) set(DROGON_LIBRARIES Drogon::Drogon)
set(DROGON_EXECUTABLE drogon_ctl) set(DROGON_EXECUTABLE drogon_ctl)
set(DROGON_FOUND TRUE)

View File

@ -4,9 +4,7 @@
#cmakedefine01 LIBPQ_SUPPORTS_BATCH_MODE #cmakedefine01 LIBPQ_SUPPORTS_BATCH_MODE
#cmakedefine01 USE_MYSQL #cmakedefine01 USE_MYSQL
#cmakedefine01 USE_SQLITE3 #cmakedefine01 USE_SQLITE3
#cmakedefine01 HAS_STD_FILESYSTEM_PATH
#cmakedefine OpenSSL_FOUND #cmakedefine OpenSSL_FOUND
#cmakedefine Boost_FOUND
#cmakedefine COMPILATION_FLAGS "@COMPILATION_FLAGS@@DROGON_CXX_STANDARD@" #cmakedefine COMPILATION_FLAGS "@COMPILATION_FLAGS@@DROGON_CXX_STANDARD@"
#cmakedefine COMPILER_COMMAND "@COMPILER_COMMAND@" #cmakedefine COMPILER_COMMAND "@COMPILER_COMMAND@"

View File

@ -1,7 +0,0 @@
#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@"

View File

@ -1,7 +0,0 @@
#include <filesystem>
int main()
{
std::filesystem::path aPath("../");
return 0;
}

View File

@ -1,7 +0,0 @@
#include <uuid.h>
int main()
{
uuid_t uu;
uuid_generate(uu);
return 0;
}

View File

@ -1,8 +0,0 @@
#include <uuid.h>
int main()
{
uuid_t *uuid;
uuid_create(&uuid);
uuid_make(uuid, UUID_MAKE_V1);
return 0;
}

View File

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

View File

@ -1,50 +0,0 @@
# ***************************************************************************
# _ _ ____ _
# Project ___| | | | _ \| |
# / __| | | | |_) | |
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which you should
# have received as part of this distribution. The terms are also available at
# https://curl.haxx.se/docs/copyright.html.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is furnished
# to do so, under the terms of the COPYING file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
# ##############################################################################
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_package_handle_standard_args(Brotli
REQUIRED_VARS
BROTLIDEC_LIBRARY
BROTLIENC_LIBRARY
BROTLICOMMON_LIBRARY
BROTLI_INCLUDE_DIR
FAIL_MESSAGE
"Could NOT find BROTLI")
set(BROTLI_INCLUDE_DIRS ${BROTLI_INCLUDE_DIR})
set(BROTLI_LIBRARIES ${BROTLIDEC_LIBRARY}
${BROTLIENC_LIBRARY} ${BROTLICOMMON_LIBRARY})
if(Brotli_FOUND)
add_library(Brotli_lib INTERFACE IMPORTED)
set_target_properties(Brotli_lib
PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
"${BROTLI_INCLUDE_DIRS}"
INTERFACE_LINK_LIBRARIES
"${BROTLI_LIBRARIES}")
endif(Brotli_FOUND)

View File

@ -1,261 +0,0 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
#[=======================================================================[.rst:
FindFilesystem
##############
This module supports the C++17 standard library's filesystem utilities. Use the
:imp-target:`std::filesystem` imported target to
Options
*******
The ``COMPONENTS`` argument to this module supports the following values:
.. find-component:: Experimental
:name: fs.Experimental
Allows the module to find the "experimental" Filesystem TS version of the
Filesystem library. This is the library that should be used with the
``std::experimental::filesystem`` namespace.
.. find-component:: Final
:name: fs.Final
Finds the final C++17 standard version of the filesystem library.
If no components are provided, behaves as if the
:find-component:`fs.Final` component was specified.
If both :find-component:`fs.Experimental` and :find-component:`fs.Final` are
provided, first looks for ``Final``, and falls back to ``Experimental`` in case
of failure. If ``Final`` is found, :imp-target:`std::filesystem` and all
:ref:`variables <fs.variables>` will refer to the ``Final`` version.
Imported Targets
****************
.. imp-target:: std::filesystem
The ``std::filesystem`` imported target is defined when any requested
version of the C++ filesystem library has been found, whether it is
*Experimental* or *Final*.
If no version of the filesystem library is available, this target will not
be defined.
.. note::
This target has ``cxx_std_17`` as an ``INTERFACE``
:ref:`compile language standard feature <req-lang-standards>`. Linking
to this target will automatically enable C++17 if no later standard
version is already required on the linking target.
.. _fs.variables:
Variables
*********
.. variable:: CXX_FILESYSTEM_IS_EXPERIMENTAL
Set to ``TRUE`` when the :find-component:`fs.Experimental` version of C++
filesystem library was found, otherwise ``FALSE``.
.. variable:: CXX_FILESYSTEM_HAVE_FS
Set to ``TRUE`` when a filesystem header was found.
.. variable:: CXX_FILESYSTEM_HEADER
Set to either ``filesystem`` or ``experimental/filesystem`` depending on
whether :find-component:`fs.Final` or :find-component:`fs.Experimental` was
found.
.. variable:: CXX_FILESYSTEM_NAMESPACE
Set to either ``std::filesystem`` or ``std::experimental::filesystem``
depending on whether :find-component:`fs.Final` or
:find-component:`fs.Experimental` was found.
Examples
********
Using `find_package(Filesystem)` with no component arguments:
.. code-block:: cmake
find_package(Filesystem REQUIRED)
add_executable(my-program main.cpp)
target_link_libraries(my-program PRIVATE std::filesystem)
#]=======================================================================]
if(TARGET std::filesystem)
# This module has already been processed. Don't do it again.
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)
# If we're not cross-compiling, try to run test executables.
# Otherwise, assume that compile + link is a sufficient check.
if(CMAKE_CROSSCOMPILING)
include(CheckCXXSourceCompiles)
macro(_cmcm_check_cxx_source code var)
check_cxx_source_compiles("${code}" ${var})
endmacro()
else()
include(CheckCXXSourceRuns)
macro(_cmcm_check_cxx_source code var)
check_cxx_source_runs("${code}" ${var})
endmacro()
endif()
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
set(want_components ${Filesystem_FIND_COMPONENTS})
if(Filesystem_FIND_COMPONENTS STREQUAL "")
set(want_components Final)
endif()
# Warn on any unrecognized components
set(extra_components ${want_components})
list(REMOVE_ITEM extra_components Final Experimental)
foreach(component IN LISTS extra_components)
message(WARNING "Extraneous find_package component for Filesystem: ${component}")
endforeach()
# Detect which of Experimental and Final we should look for
set(find_experimental TRUE)
set(find_final TRUE)
if(NOT "Final" IN_LIST want_components)
set(find_final FALSE)
endif()
if(NOT "Experimental" IN_LIST want_components)
set(find_experimental FALSE)
endif()
if(find_final)
check_include_file_cxx("filesystem" _CXX_FILESYSTEM_HAVE_HEADER)
mark_as_advanced(_CXX_FILESYSTEM_HAVE_HEADER)
if(_CXX_FILESYSTEM_HAVE_HEADER)
# We found the non-experimental header. Don't bother looking for the
# experimental one.
set(find_experimental FALSE)
endif()
else()
set(_CXX_FILESYSTEM_HAVE_HEADER FALSE)
endif()
if(find_experimental)
check_include_file_cxx("experimental/filesystem" _CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER)
mark_as_advanced(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER)
else()
set(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER FALSE)
endif()
if(_CXX_FILESYSTEM_HAVE_HEADER)
set(_have_fs TRUE)
set(_fs_header filesystem)
set(_fs_namespace std::filesystem)
set(_is_experimental FALSE)
elseif(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER)
set(_have_fs TRUE)
set(_fs_header experimental/filesystem)
set(_fs_namespace std::experimental::filesystem)
set(_is_experimental TRUE)
else()
set(_have_fs FALSE)
endif()
set(CXX_FILESYSTEM_HAVE_FS ${_have_fs} CACHE BOOL "TRUE if we have the C++ filesystem headers")
set(CXX_FILESYSTEM_HEADER ${_fs_header} CACHE STRING "The header that should be included to obtain the filesystem APIs")
set(CXX_FILESYSTEM_NAMESPACE ${_fs_namespace} CACHE STRING "The C++ namespace that contains the filesystem APIs")
set(CXX_FILESYSTEM_IS_EXPERIMENTAL ${_is_experimental} CACHE BOOL "TRUE if the C++ filesystem library is the experimental version")
set(_found FALSE)
if(CXX_FILESYSTEM_HAVE_FS)
# We have some filesystem library available. Do link checks
string(CONFIGURE [[
#include <cstdio>
#include <@CXX_FILESYSTEM_HEADER@>
int main() {
auto cwd = @CXX_FILESYSTEM_NAMESPACE@::current_path();
printf("%s", cwd.generic_string().c_str());
return EXIT_SUCCESS;
}
]] 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)
set(can_link ${CXX_FILESYSTEM_NO_LINK_NEEDED})
if(NOT CXX_FILESYSTEM_NO_LINK_NEEDED)
set(prev_libraries ${CMAKE_REQUIRED_LIBRARIES})
# Add the libstdc++ flag
set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lstdc++fs)
_cmcm_check_cxx_source("${code}" CXX_FILESYSTEM_STDCPPFS_NEEDED)
set(can_link ${CXX_FILESYSTEM_STDCPPFS_NEEDED})
if(NOT CXX_FILESYSTEM_STDCPPFS_NEEDED)
# Try the libc++ flag
set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lc++fs)
_cmcm_check_cxx_source("${code}" CXX_FILESYSTEM_CPPFS_NEEDED)
set(can_link ${CXX_FILESYSTEM_CPPFS_NEEDED})
endif()
endif()
if(can_link)
add_library(std::filesystem INTERFACE IMPORTED)
set_property(TARGET std::filesystem APPEND PROPERTY INTERFACE_COMPILE_FEATURES cxx_std_17)
set(_found TRUE)
if(CXX_FILESYSTEM_NO_LINK_NEEDED)
# Nothing to add...
elseif(CXX_FILESYSTEM_STDCPPFS_NEEDED)
set_property(TARGET std::filesystem APPEND PROPERTY INTERFACE_LINK_LIBRARIES -lstdc++fs)
elseif(CXX_FILESYSTEM_CPPFS_NEEDED)
set_property(TARGET std::filesystem APPEND PROPERTY INTERFACE_LINK_LIBRARIES -lc++fs)
endif()
endif()
endif()
cmake_pop_check_state()
set(Filesystem_FOUND ${_found} CACHE BOOL "TRUE if we can run a program using std::filesystem" FORCE)
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

@ -1,41 +0,0 @@
# Try to find hiredis
# Once done, this will define
#
# HIREDIS_FOUND - system has hiredis
# HIREDIS_INCLUDE_DIRS - hiredis include directories
# HIREDIS_LIBRARIES - libraries need to use hiredis
if (HIREDIS_INCLUDE_DIRS AND HIREDIS_LIBRARIES)
set(HIREDIS_FIND_QUIETLY TRUE)
set(Hiredis_FOUND TRUE)
else ()
find_path(
HIREDIS_INCLUDE_DIR
NAMES hiredis/hiredis.h
HINTS ${HIREDIS_ROOT_DIR}
PATH_SUFFIXES include)
find_library(
HIREDIS_LIBRARY
NAMES hiredis
HINTS ${HIREDIS_ROOT_DIR}
PATH_SUFFIXES ${CMAKE_INSTALL_LIBDIR})
set(HIREDIS_INCLUDE_DIRS ${HIREDIS_INCLUDE_DIR})
set(HIREDIS_LIBRARIES ${HIREDIS_LIBRARY})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
Hiredis DEFAULT_MSG HIREDIS_LIBRARY HIREDIS_INCLUDE_DIR)
mark_as_advanced(HIREDIS_LIBRARY HIREDIS_INCLUDE_DIR)
endif ()
if(Hiredis_FOUND)
add_library(Hiredis_lib INTERFACE IMPORTED)
set_target_properties(Hiredis_lib
PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
"${HIREDIS_INCLUDE_DIRS}"
INTERFACE_LINK_LIBRARIES
"${HIREDIS_LIBRARIES}")
endif(Hiredis_FOUND)

View File

@ -2,72 +2,62 @@
# #
# Find the jsoncpp includes and library # Find the jsoncpp includes and library
# #
# if you nee to add a custom library search path, do it via via # if you nee to add a custom library search path, do it via via CMAKE_PREFIX_PATH
# CMAKE_PREFIX_PATH
# #
# This module defines JSONCPP_INCLUDE_DIRS, where to find header, etc. # This module defines
# JSONCPP_LIBRARIES, the libraries needed to use jsoncpp. JSONCPP_FOUND, If # JSONCPP_INCLUDE_DIRS, where to find header, etc.
# false, do not try to use jsoncpp. # JSONCPP_LIBRARIES, the libraries needed to use jsoncpp.
# Jsoncpp_lib - The imported target library. # JSONCPP_FOUND, If false, do not try to use jsoncpp.
# JSONCPP_INCLUDE_PREFIX, include prefix for jsoncpp
# only look in default directories # only look in default directories
find_path(JSONCPP_INCLUDE_DIRS find_path(
NAMES json/json.h JSONCPP_INCLUDE_DIR
NAMES jsoncpp/json/json.h json/json.h
DOC "jsoncpp include dir" DOC "jsoncpp include dir"
PATH_SUFFIXES jsoncpp)
find_library(JSONCPP_LIBRARIES NAMES jsoncpp DOC "jsoncpp library")
# debug library on windows same naming convention as in qt (appending debug
# library with d) boost is using the same "hack" as us with "optimized" and
# "debug" if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# find_library(JSONCPP_LIBRARIES_DEBUG NAMES jsoncppd DOC "jsoncpp debug
# library") if("${JSONCPP_LIBRARIES_DEBUG}" STREQUAL "JSONCPP_LIBRARIES_DEBUG-
# NOTFOUND") set(JSONCPP_LIBRARIES_DEBUG ${JSONCPP_LIBRARIES}) endif()
# set(JSONCPP_LIBRARIES optimized ${JSONCPP_LIBRARIES} debug
# ${JSONCPP_LIBRARIES_DEBUG})
# endif()
# handle the QUIETLY and REQUIRED arguments and set JSONCPP_FOUND to TRUE if all
# listed variables are TRUE, hide their existence from configuration view
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Jsoncpp
DEFAULT_MSG
JSONCPP_INCLUDE_DIRS
JSONCPP_LIBRARIES)
mark_as_advanced(JSONCPP_INCLUDE_DIRS JSONCPP_LIBRARIES)
if(Jsoncpp_FOUND)
if(NOT EXISTS ${JSONCPP_INCLUDE_DIRS}/json/version.h)
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()
if(jsoncpp_ver LESS 1.7)
message(
FATAL_ERROR
"jsoncpp lib is too old, please get new version from https://github.com/open-source-parsers/jsoncpp"
) )
endif(jsoncpp_ver LESS 1.7)
endif()
if (NOT TARGET Jsoncpp_lib)
add_library(Jsoncpp_lib INTERFACE IMPORTED)
endif()
set_target_properties(Jsoncpp_lib
PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
"${JSONCPP_INCLUDE_DIRS}"
INTERFACE_LINK_LIBRARIES
"${JSONCPP_LIBRARIES}")
endif(Jsoncpp_FOUND) find_library(
JSONCPP_LIBRARY
NAMES jsoncpp
DOC "jsoncpp library"
)
set(JSONCPP_INCLUDE_DIRS ${JSONCPP_INCLUDE_DIR})
set(JSONCPP_LIBRARIES ${JSONCPP_LIBRARY})
# debug library on windows
# same naming convention as in qt (appending debug library with d)
# boost is using the same "hack" as us with "optimized" and "debug"
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
find_library(
JSONCPP_LIBRARY_DEBUG
NAMES jsoncppd
DOC "jsoncpp debug library"
)
set(JSONCPP_LIBRARIES optimized ${JSONCPP_LIBRARIES} debug ${JSONCPP_LIBRARY_DEBUG})
endif()
# find JSONCPP_INCLUDE_PREFIX
find_path(
JSONCPP_INCLUDE_PREFIX
NAMES json.h
PATH_SUFFIXES jsoncpp/json json
)
if (${JSONCPP_INCLUDE_PREFIX} MATCHES "jsoncpp")
set(JSONCPP_INCLUDE_PREFIX "jsoncpp")
set(JSONCPP_INCLUDE_DIRS "${JSONCPP_INCLUDE_DIRS}/jsoncpp")
else()
set(JSONCPP_INCLUDE_PREFIX "")
endif()
# handle the QUIETLY and REQUIRED arguments and set JSONCPP_FOUND to TRUE
# if all listed variables are TRUE, hide their existence from configuration view
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(jsoncpp DEFAULT_MSG
JSONCPP_INCLUDE_DIR JSONCPP_LIBRARY)
mark_as_advanced (JSONCPP_INCLUDE_DIR JSONCPP_LIBRARY)

View File

@ -1,144 +1,114 @@
#-------------------------------------------------------- #--------------------------------------------------------
# Copyright (C) 1995-2007 MySQL AB # Copyright (C) 1995-2007 MySQL AB
# #
# This program is free software; you can redistribute it and/or modify it under # This program is free software; you can redistribute it and/or modify
# the terms of version 2 of the GNU General Public License as published by the # it under the terms of version 2 of the GNU General Public License as
# Free Software Foundation. # published by the Free Software Foundation.
# #
# There are special exceptions to the terms and conditions of the GPL as it is # There are special exceptions to the terms and conditions of the GPL
# applied to this software. View the full text of the exception in file # as it is applied to this software. View the full text of the exception
# LICENSE.exceptions in the top-level directory of this software distribution. # in file LICENSE.exceptions in the top-level directory of this software
# distribution.
# #
# This program is distributed in the hope that it will be useful, but WITHOUT # This program is distributed in the hope that it will be useful,
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # but WITHOUT ANY WARRANTY; without even the implied warranty of
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License along with # You should have received a copy of the GNU General Public License
# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin # along with this program; if not, write to the Free Software
# Street, Fifth Floor, Boston, MA 02110-1301, USA # Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
# #
# The MySQL Connector/ODBC is licensed under the terms of the GPL, like most # The MySQL Connector/ODBC is licensed under the terms of the
# MySQL Connectors. There are special exceptions to the terms and conditions of # GPL, like most MySQL Connectors. There are special exceptions
# the GPL as it is applied to this software, see the FLOSS License Exception # to the terms and conditions of the GPL as it is applied to
# available on mysql.com. # this software, see the FLOSS License Exception available on
# MySQL_lib - The imported target library. # mysql.com.
# ############################################################################## ##########################################################################
# -------------- FIND MYSQL_INCLUDE_DIRS ------------------
find_path(MARIADB_INCLUDE_DIRS #-------------- FIND MYSQL_INCLUDE_DIR ------------------
NAMES mysql.h FIND_PATH(MYSQL_INCLUDE_DIR mysql.h
PATH_SUFFIXES mariadb /usr/include/mysql
PATHS /usr/include/mysql
/usr/local/include/mysql /usr/local/include/mysql
/usr/include/mariadb
/usr/local/include/mariadb
/opt/mysql/mysql/include /opt/mysql/mysql/include
/opt/mysql/mysql/include/mysql /opt/mysql/mysql/include/mysql
/opt/mysql/include /opt/mysql/include
/opt/local/include/mysql5 /opt/local/include/mysql5
/usr/local/mysql/include /usr/local/mysql/include
/usr/local/mysql/include/mysql /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{ProgramFiles}/MySQL/*/include
$ENV{SystemDrive}/MySQL/*/include) $ENV{SystemDrive}/MySQL/*/include)
find_path(MYSQL_INCLUDE_DIRS #----------------- FIND MYSQL_LIB_DIR -------------------
NAMES mysql.h IF (WIN32)
PATH_SUFFIXES mysql # Set lib path suffixes
PATHS /usr/include/mysql # dist = for mysql binary distributions
/usr/local/include/mysql # build = for custom built tree
/usr/include/mariadb IF (CMAKE_BUILD_TYPE STREQUAL Debug)
/usr/local/include/mariadb SET(libsuffixDist debug)
/opt/mysql/mysql/include SET(libsuffixBuild Debug)
/opt/mysql/mysql/include/mysql ELSE (CMAKE_BUILD_TYPE STREQUAL Debug)
/opt/mysql/include SET(libsuffixDist opt)
/opt/local/include/mysql5 SET(libsuffixBuild Release)
/usr/local/mysql/include ADD_DEFINITIONS(-DDBUG_OFF)
/usr/local/mysql/include/mysql ENDIF (CMAKE_BUILD_TYPE STREQUAL Debug)
/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)
if(EXISTS "${MARIADB_INCLUDE_DIRS}/mysql.h") FIND_LIBRARY(MYSQL_LIB NAMES mysqlclient
set(MYSQL_INCLUDE_DIRS ${MARIADB_INCLUDE_DIRS}) PATHS
elseif(EXISTS "${MYSQL_INCLUDE_DIRS}/mysql.h") $ENV{MYSQL_DIR}/lib/${libsuffixDist}
elseif(EXISTS "${MYSQL_INCLUDE_DIRS}/mysql/mysql.h")
set(MYSQL_INCLUDE_DIRS ${MYSQL_INCLUDE_DIRS}/mysql)
endif()
# ----------------- FIND MYSQL_LIBRARIES_DIR -------------------
if(WIN32)
# Set lib path suffixes dist = for mysql binary distributions build = for
# custom built tree
if(CMAKE_BUILD_TYPE STREQUAL Debug)
set(libsuffixDist debug)
set(libsuffixBuild Debug)
else(CMAKE_BUILD_TYPE STREQUAL Debug)
set(libsuffixDist opt)
set(libsuffixBuild Release)
add_definitions(-DDBUG_OFF)
endif(CMAKE_BUILD_TYPE STREQUAL Debug)
find_library(MYSQL_LIBRARIES
NAMES mariadbclient
PATHS $ENV{MYSQL_DIR}/lib/${libsuffixDist}
$ENV{MYSQL_DIR}/libmysql $ENV{MYSQL_DIR}/libmysql
$ENV{MYSQL_DIR}/libmysql/${libsuffixBuild} $ENV{MYSQL_DIR}/libmysql/${libsuffixBuild}
$ENV{MYSQL_DIR}/client/${libsuffixBuild} $ENV{MYSQL_DIR}/client/${libsuffixBuild}
$ENV{MYSQL_DIR}/libmysql/${libsuffixBuild} $ENV{MYSQL_DIR}/libmysql/${libsuffixBuild}
$ENV{ProgramFiles}/MySQL/*/lib/${libsuffixDist} $ENV{ProgramFiles}/MySQL/*/lib/${libsuffixDist}
$ENV{SystemDrive}/MySQL/*/lib/${libsuffixDist}) $ENV{SystemDrive}/MySQL/*/lib/${libsuffixDist})
else(WIN32) ELSE (WIN32)
find_library(MYSQL_LIBRARIES FIND_LIBRARY(MYSQL_LIB NAMES mysqlclient_r mariadbclient
NAMES mysqlclient_r mariadbclient mariadb PATHS
PATHS /usr/lib/mysql /usr/lib/mysql
/usr/lib/mariadb
/usr/local/lib/mysql /usr/local/lib/mysql
/usr/local/lib/mariadb
/usr/local/mysql/lib /usr/local/mysql/lib
/usr/local/mysql/lib/mysql /usr/local/mysql/lib/mysql
/opt/local/mysql5/lib /opt/local/mysql5/lib
/opt/local/lib/mysql5/mysql /opt/local/lib/mysql5/mysql
/opt/mysql/mysql/lib/mysql /opt/mysql/mysql/lib/mysql
/opt/mysql/lib/mysql /opt/mysql/lib/mysql)
/opt/rh/rh-mariadb105/root/usr/lib64) ENDIF (WIN32)
endif(WIN32)
if(MYSQL_INCLUDE_DIRS AND MYSQL_LIBRARIES) IF(MYSQL_LIB)
message(STATUS "MySQL Include dir: ${MYSQL_INCLUDE_DIRS}") GET_FILENAME_COMPONENT(MYSQL_LIB_DIR ${MYSQL_LIB} PATH)
message(STATUS "MySQL client libraries: ${MYSQL_LIBRARIES}") ENDIF(MYSQL_LIB)
elseif(MySQL_FIND_REQUIRED)
message(
FATAL_ERROR
"Cannot find MySQL. Include dir: ${MYSQL_INCLUDE_DIRS} library dir: ${MYSQL_LIBRARIES_DIR}"
)
endif(MYSQL_INCLUDE_DIRS AND MYSQL_LIBRARIES)
include(FindPackageHandleStandardArgs) set(MYSQL_VERSION_STRING "")
find_package_handle_standard_args(MySQL
DEFAULT_MSG
MYSQL_LIBRARIES
MYSQL_INCLUDE_DIRS)
# Copy the results to the output variables.
if(MySQL_FOUND)
add_library(MySQL_lib INTERFACE IMPORTED)
set_target_properties(MySQL_lib
PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
"${MYSQL_INCLUDE_DIRS}"
INTERFACE_LINK_LIBRARIES
"${MYSQL_LIBRARIES}")
else(MySQL_FOUND)
set(MYSQL_LIBRARIES)
set(MYSQL_INCLUDE_DIRS)
endif(MySQL_FOUND)
mark_as_advanced(MYSQL_INCLUDE_DIRS MYSQL_LIBRARIES) EXEC_PROGRAM (grep ARGS "MARIADB_BASE_VERSION ${MYSQL_INCLUDE_DIR}/*.h|awk '{print $3}'" OUTPUT_VARIABLE MYSQL_VERSION_STRING)
IF (MYSQL_INCLUDE_DIR AND MYSQL_LIB_DIR)
SET(MYSQL_FOUND TRUE)
FIND_LIBRARY(MYSQL_ZLIB zlib PATHS ${MYSQL_LIB_DIR})
FIND_LIBRARY(MYSQL_TAOCRYPT taocrypt PATHS ${MYSQL_LIB_DIR})
IF (MYSQL_LIB)
SET(MYSQL_CLIENT_LIBS ${MYSQL_LIB})
ELSE()
SET(MYSQL_CLIENT_LIBS mysqlclient_r)
ENDIF()
IF (MYSQL_ZLIB)
SET(MYSQL_CLIENT_LIBS ${MYSQL_CLIENT_LIBS} zlib)
ENDIF (MYSQL_ZLIB)
IF (MYSQL_TAOCRYPT)
SET(MYSQL_CLIENT_LIBS ${MYSQL_CLIENT_LIBS} taocrypt)
ENDIF (MYSQL_TAOCRYPT)
# Added needed mysqlclient dependencies on Windows
IF (WIN32)
SET(MYSQL_CLIENT_LIBS ${MYSQL_CLIENT_LIBS} ws2_32)
ENDIF (WIN32)
MESSAGE(STATUS "MySQL Include dir: ${MYSQL_INCLUDE_DIR} library dir: ${MYSQL_LIB_DIR}")
MESSAGE(STATUS "MySQL client libraries: ${MYSQL_CLIENT_LIBS}")
ELSEIF (MySQL_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Cannot find MySQL. Include dir: ${MYSQL_INCLUDE_DIR} library dir: ${MYSQL_LIB_DIR}")
ENDIF (MYSQL_INCLUDE_DIR AND MYSQL_LIB_DIR)

View File

@ -1,43 +1,37 @@
# Copyright (C) 2007-2009 LuaDist. Created by Peter Kapec <kapecp@gmail.com> # Copyright (C) 2007-2009 LuaDist.
# Redistribution and use of this file is allowed according to the terms of the # Created by Peter Kapec <kapecp@gmail.com>
# MIT license. For details see the COPYRIGHT file distributed with LuaDist. # Redistribution and use of this file is allowed according to the terms of the MIT license.
# Note: Searching headers and libraries is very simple and is NOT as powerful as # For details see the COPYRIGHT file distributed with LuaDist.
# scripts distributed with CMake, because LuaDist defines directories to search # Note:
# for. Everyone is encouraged to contact the author with improvements. Maybe # Searching headers and libraries is very simple and is NOT as powerful as scripts
# this file becomes part of CMake distribution sometimes. # distributed with CMake, because LuaDist defines directories to search for.
# Everyone is encouraged to contact the author with improvements. Maybe this file
# becomes part of CMake distribution sometimes.
# * Find sqlite3 Find the native SQLITE3 headers and libraries. # - Find sqlite3
# Find the native SQLITE3 headers and libraries.
# #
# SQLITE3_INCLUDE_DIRS - where to find sqlite3.h, etc. # SQLITE3_INCLUDE_DIRS - where to find sqlite3.h, etc.
# SQLITE3_LIBRARIES - List of libraries when using sqlite. # SQLITE3_LIBRARIES - List of libraries when using sqlite.
# SQLite3_FOUND - True if sqlite3 found. # SQLITE3_FOUND - True if sqlite found.
# SQLite3_lib - The imported target library.
# Look for the header file. # Look for the header file.
find_path(SQLITE3_INCLUDE_DIRS NAMES sqlite3.h) FIND_PATH(SQLITE3_INCLUDE_DIR NAMES sqlite3.h)
# Look for the library. # Look for the library.
find_library(SQLITE3_LIBRARIES NAMES sqlite3) FIND_LIBRARY(SQLITE3_LIBRARY NAMES sqlite3)
# Handle the QUIETLY and REQUIRED arguments and set SQLite3_FOUND to TRUE if all # Handle the QUIETLY and REQUIRED arguments and set SQLITE3_FOUND to TRUE if all listed variables are TRUE.
# listed variables are TRUE. INCLUDE(FindPackageHandleStandardArgs)
include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(SQLITE3 DEFAULT_MSG SQLITE3_LIBRARY SQLITE3_INCLUDE_DIR)
find_package_handle_standard_args(SQLite3
DEFAULT_MSG
SQLITE3_LIBRARIES
SQLITE3_INCLUDE_DIRS)
# Copy the results to the output variables. # Copy the results to the output variables.
if(SQLite3_FOUND) IF(SQLITE3_FOUND)
add_library(SQLite3_lib INTERFACE IMPORTED) SET(SQLITE3_LIBRARIES ${SQLITE3_LIBRARY})
set_target_properties(SQLite3_lib SET(SQLITE3_INCLUDE_DIRS ${SQLITE3_INCLUDE_DIR})
PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ELSE(SQLITE3_FOUND)
"${SQLITE3_INCLUDE_DIRS}" SET(SQLITE3_LIBRARIES)
INTERFACE_LINK_LIBRARIES SET(SQLITE3_INCLUDE_DIRS)
"${SQLITE3_LIBRARIES}") ENDIF(SQLITE3_FOUND)
else(SQLite3_FOUND)
set(SQLITE3_LIBRARIES)
set(SQLITE3_INCLUDE_DIRS)
endif(SQLite3_FOUND)
mark_as_advanced(SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES) MARK_AS_ADVANCED(SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES)

View File

@ -1,25 +1,30 @@
# * Try to find UUID Once done this will define # - Try to find UUID
# Once done this will define
# #
# UUID_FOUND - system has UUID # UUID_FOUND - system has UUID
# UUID_INCLUDE_DIRS - the UUID include directory # UUID_INCLUDE_DIRS - the UUID include directory
# UUID_LIBRARIES - Link these to use UUID UUID_DEFINITIONS - Compiler switches # UUID_LIBRARIES - Link these to use UUID
# required for using UUID # UUID_DEFINITIONS - Compiler switches required for using UUID
# #
# Copyright (c) 2006 Andreas Schneider <mail@cynapses.org> # Copyright (c) 2006 Andreas Schneider <mail@cynapses.org>
# #
# Redistribution and use is allowed according to the terms of the New BSD # Redistribution and use is allowed according to the terms of the New
# license. For details see the accompanying COPYING-CMAKE-SCRIPTS file. # BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
# #
if (UUID_LIBRARIES AND UUID_INCLUDE_DIRS) if (UUID_LIBRARIES AND UUID_INCLUDE_DIRS)
# in cache already # in cache already
set(UUID_FOUND TRUE) set(UUID_FOUND TRUE)
else() else (UUID_LIBRARIES AND UUID_INCLUDE_DIRS)
find_path( find_path(UUID_INCLUDE_DIR
UUID_INCLUDE_DIR NAMES
NAMES uuid.h uuid.h
PATH_SUFFIXES uuid PATH_SUFFIXES
HINTS ${UUID_DIR}/include uuid
HINTS
${UUID_DIR}/include
$ENV{UUID_DIR}/include $ENV{UUID_DIR}/include
$ENV{UUID_DIR} $ENV{UUID_DIR}
${DELTA3D_EXT_DIR}/inc ${DELTA3D_EXT_DIR}/inc
@ -36,18 +41,22 @@ else()
/opt/csw/include # Blastwave /opt/csw/include # Blastwave
/opt/include /opt/include
[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session\ Manager\\Environment;OSG_ROOT]/include [HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session\ Manager\\Environment;OSG_ROOT]/include
/usr/freeware/include) /usr/freeware/include
)
find_library(UUID_LIBRARY find_library(UUID_LIBRARY
NAMES uuid ossp-uuid NAMES
HINTS ${UUID_DIR}/lib uuid ossp-uuid
HINTS
${UUID_DIR}/lib
$ENV{UUID_DIR}/lib $ENV{UUID_DIR}/lib
$ENV{UUID_DIR} $ENV{UUID_DIR}
${DELTA3D_EXT_DIR}/lib ${DELTA3D_EXT_DIR}/lib
$ENV{DELTA_ROOT}/ext/lib $ENV{DELTA_ROOT}/ext/lib
$ENV{DELTA_ROOT} $ENV{DELTA_ROOT}
$ENV{OSG_ROOT}/lib $ENV{OSG_ROOT}/lib
PATHS ~/Library/Frameworks PATHS
~/Library/Frameworks
/Library/Frameworks /Library/Frameworks
/usr/local/lib /usr/local/lib
/usr/lib /usr/lib
@ -55,18 +64,22 @@ else()
/opt/local/lib /opt/local/lib
/opt/csw/lib /opt/csw/lib
/opt/lib /opt/lib
/usr/freeware/lib64) /usr/freeware/lib64
)
find_library(UUID_LIBRARY_DEBUG find_library(UUID_LIBRARY_DEBUG
NAMES uuidd NAMES
HINTS ${UUID_DIR}/lib uuidd
HINTS
${UUID_DIR}/lib
$ENV{UUID_DIR}/lib $ENV{UUID_DIR}/lib
$ENV{UUID_DIR} $ENV{UUID_DIR}
${DELTA3D_EXT_DIR}/lib ${DELTA3D_EXT_DIR}/lib
$ENV{DELTA_ROOT}/ext/lib $ENV{DELTA_ROOT}/ext/lib
$ENV{DELTA_ROOT} $ENV{DELTA_ROOT}
$ENV{OSG_ROOT}/lib $ENV{OSG_ROOT}/lib
PATHS ~/Library/Frameworks PATHS
~/Library/Frameworks
/Library/Frameworks /Library/Frameworks
/usr/local/lib /usr/local/lib
/usr/lib /usr/lib
@ -74,45 +87,33 @@ else()
/opt/local/lib /opt/local/lib
/opt/csw/lib /opt/csw/lib
/opt/lib /opt/lib
/usr/freeware/lib64) /usr/freeware/lib64
)
if(NOT UUID_LIBRARY AND (BSD OR APPLE)) if (NOT UUID_LIBRARY AND BSD)
set(UUID_LIBRARY "") set(UUID_LIBRARY "")
endif() endif(NOT UUID_LIBRARY AND BSD)
set(UUID_INCLUDE_DIRS ${UUID_INCLUDE_DIR}) set(UUID_INCLUDE_DIRS ${UUID_INCLUDE_DIR})
set(UUID_LIBRARIES ${UUID_LIBRARY}) set(UUID_LIBRARIES ${UUID_LIBRARY})
if (UUID_INCLUDE_DIRS) if (UUID_INCLUDE_DIRS)
if((BSD OR APPLE) OR UUID_LIBRARIES) if (BSD OR UUID_LIBRARIES)
set(UUID_FOUND TRUE) set(UUID_FOUND TRUE)
endif() endif (BSD OR UUID_LIBRARIES)
endif() endif (UUID_INCLUDE_DIRS)
if (UUID_FOUND) if (UUID_FOUND)
if (NOT UUID_FIND_QUIETLY) if (NOT UUID_FIND_QUIETLY)
message(STATUS "Found UUID: ${UUID_LIBRARIES}") message(STATUS "Found UUID: ${UUID_LIBRARIES}")
endif() endif (NOT UUID_FIND_QUIETLY)
else() else (UUID_FOUND)
if (UUID_FIND_REQUIRED) if (UUID_FIND_REQUIRED)
message(FATAL_ERROR "Could not find UUID") message(FATAL_ERROR "Could not find UUID")
endif() endif (UUID_FIND_REQUIRED)
endif() endif (UUID_FOUND)
# show the UUID_INCLUDE_DIRS and UUID_LIBRARIES variables only in the advanced # show the UUID_INCLUDE_DIRS and UUID_LIBRARIES variables only in the advanced view
# view
mark_as_advanced(UUID_INCLUDE_DIRS UUID_LIBRARIES) 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)
set_target_properties(UUID_lib
PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
"${UUID_INCLUDE_DIRS}"
INTERFACE_LINK_LIBRARIES
"${UUID_LIBRARIES}")
else()
set(UUID_LIBRARIES)
set(UUID_INCLUDE_DIRS)
endif()

View File

@ -1,23 +0,0 @@
find_path(COZ_INCLUDE_DIRS NAMES coz.h)
find_library(COZ_LIBRARIES NAMES coz PATH_SUFFIXES coz-profiler)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(coz-profiler
DEFAULT_MSG
COZ_LIBRARIES
COZ_INCLUDE_DIRS)
if(COZ-PROFILER_FOUND)
add_library(coz::coz INTERFACE IMPORTED)
set_target_properties(coz::coz
PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
${COZ_INCLUDE_DIRS}
INTERFACE_LINK_LIBRARIES
${COZ_LIBRARIES})
else(COZ-PROFILER_FOUND)
set(COZ_LIBRARIES)
set(COZ_INCLUDE_DIRS)
endif(COZ-PROFILER_FOUND)
mark_as_advanced(COZ_INCLUDE_DIRS COZ_LIBRARIES)

View File

@ -1,27 +0,0 @@
# Find PostgreSQL
#
# Find the PostgreSQL includes and library
#
# This module defines PG_INCLUDE_DIRS, where to find header, etc. PG_LIBRARIES,
# the libraries needed to use PostgreSQL. pg_FOUND, If false, do not try to use
# PostgreSQL.
# pg_lib - The imported target library.
find_package(PostgreSQL)
if(PostgreSQL_FOUND)
set(PG_LIBRARIES ${PostgreSQL_LIBRARIES})
set(PG_INCLUDE_DIRS ${PostgreSQL_INCLUDE_DIRS})
message(STATUS "pg inc: " ${PostgreSQL_INCLUDE_DIRS})
add_library(pg_lib INTERFACE IMPORTED)
set_target_properties(pg_lib
PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
"${PostgreSQL_INCLUDE_DIRS}"
INTERFACE_LINK_LIBRARIES
"${PostgreSQL_LIBRARIES}")
mark_as_advanced(PG_INCLUDE_DIRS PG_LIBRARIES)
endif(PostgreSQL_FOUND)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(pg
DEFAULT_MSG
PG_LIBRARIES
PG_INCLUDE_DIRS)

View File

@ -1,16 +0,0 @@
[requires]
jsoncpp/1.9.4
zlib/1.2.11
gtest/1.10.0
sqlite3/3.40.1
#libpq/13.2
openssl/1.1.1t
hiredis/1.0.0
brotli/1.0.9
[generators]
CMakeToolchain
[options]
[imports]

View File

@ -2,15 +2,10 @@
*/ */
{ {
/* /*
//ssl:The global SSL settings. "key" and "cert" are the path to the SSL key and certificate. While //ssl:The global ssl files setting
// "conf" is an array of 1 or 2-element tuples that supplies file style options for `SSL_CONF_cmd`.
"ssl": { "ssl": {
"cert": "../../trantor/trantor/tests/server.crt", "cert": "../../trantor/trantor/tests/server.pem",
"key": "../../trantor/trantor/tests/server.key", "key": "../../trantor/trantor/tests/server.pem"
"conf": [
//["Options", "-SessionTicket"],
//["Options", "Compression"]
]
}, },
"listeners": [ "listeners": [
{ {
@ -28,18 +23,13 @@
//cert,key: Cert file path and key file path, empty by default, //cert,key: Cert file path and key file path, empty by default,
//if empty, use the global setting //if empty, use the global setting
"cert": "", "cert": "",
"key": "", "key": ""
//use_old_tls: enable the TLS1.0/1.1, false by default
"use_old_tls": false,
"ssl_conf": [
//["MinProtocol", "TLSv1.3"]
]
} }
], ],
"db_clients": [ "db_clients": [
{ {
//name: Name of the client,'default' by default //name: Name of the client,'default' by default
"name": "default", //"name":"",
//rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default //rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default
"rdbms": "postgresql", "rdbms": "postgresql",
//filename: Sqlite3 db file name //filename: Sqlite3 db file name
@ -57,80 +47,24 @@
//is_fast: false by default, if it is true, the client is faster but user can't call //is_fast: false by default, if it is true, the client is faster but user can't call
//any synchronous interface of it. //any synchronous interface of it.
"is_fast": false, "is_fast": false,
//client_encoding: The character set used by the client. it is empty string by default which //connection_number: 1 by default, if the 'is_fast' is true, the number is the number of
//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. //connections per IO thread, otherwise it is the total number of all connections.
"number_of_connections": 1, "connection_number": 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": { "app": {
//number_of_threads: The number of IO threads, 1 by default, if the value is set to 0, the number of threads //threads_num: 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 //is the number of CPU cores
"number_of_threads": 1, "threads_num": 1,
//enable_session: False by default //enable_session: False by default
"enable_session": true, "enable_session": true,
"session_timeout": 0, "session_timeout": 0,
//string value of SameSite attribute of the Set-Cookie HTTP response header //document_root: Root path of HTTP document, defaut path is ./
//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": "./", "document_root": "./",
//home_page: Set the HTML file of the home page, the default value is "index.html" //home_page: Set the HTML file of the home page, the default value is "index.html"
//If there isn't any handler registered to the path "/", the home page file in the "document_root" is send to clients as a response //If there isn't any handler registered to the path "/", the home page file in the "document_root" is send to clients as a response
//to the request for "/". //to the request for "/".
"home_page": "index.html", "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. //upload_path: The path to save the uploaded file. "uploads" by default.
//If the path isn't prefixed with /, ./ or ../, //If the path isn't prefixed with /, ./ or ../,
//it is relative path of document_root path //it is relative path of document_root path
@ -152,42 +86,11 @@
"xap", "xap",
"apk", "apk",
"cur", "cur",
"xml", "xml"
"webp",
"svg"
], ],
// mime: A dictionary that extends the internal MIME type support. Maps extensions into new MIME types //max_connections: maximum connections number,100000 by default
// 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": 100000,
//max_connections_per_ip: maximum number of connections per client, 0 by default which means no limit //max_connections_per_ip: maximum connections number per clinet,0 by default which means no limit
"max_connections_per_ip": 0, "max_connections_per_ip": 0,
//Load_dynamic_views: False by default, when set to true, drogon //Load_dynamic_views: False by default, when set to true, drogon
//compiles and loads dynamically "CSP View Files" in directories defined //compiles and loads dynamically "CSP View Files" in directories defined
@ -198,27 +101,8 @@
"dynamic_views_path": [ "dynamic_views_path": [
"./views" "./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: Set log output, drogon output logs to stdout by default
"log": { "log": {
//use_spdlog: Use spdlog library to log
"use_spdlog": false,
//log_path: Log file path,empty by default,in which case,logs are output to the stdout //log_path: Log file path,empty by default,in which case,logs are output to the stdout
//"log_path": "./", //"log_path": "./",
//logfile_base_name: Log file base name,empty by default which means drogon names logfile as //logfile_base_name: Log file base name,empty by default which means drogon names logfile as
@ -227,45 +111,34 @@
//log_size_limit: 100000000 bytes by default, //log_size_limit: 100000000 bytes by default,
//When the log file size reaches "log_size_limit", the log file is switched. //When the log file size reaches "log_size_limit", the log file is switched.
"log_size_limit": 100000000, "log_size_limit": 100000000,
//max_files: 0 by default,
//When the number of old log files exceeds "max_files", the oldest file will be deleted. 0 means never delete.
"max_files": 0,
//log_level: "DEBUG" by default,options:"TRACE","DEBUG","INFO","WARN" //log_level: "DEBUG" by default,options:"TRACE","DEBUG","INFO","WARN"
//The TRACE level is only valid when built in DEBUG mode. //The TRACE level is only valid when built in DEBUG mode.
"log_level": "DEBUG", "log_level": "DEBUG"
//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 by default
"run_as_daemon": false, "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 by default, if true, the program will be restart by the parent after exiting;
"relaunch_on_error": false, "relaunch_on_error": false,
//use_sendfile: True by default, if true, the program //use_sendfile: True by default, if ture, the program
//uses sendfile() system-call to send static files to clients; //uses sendfile() system-call to send static files to clients;
"use_sendfile": true, "use_sendfile": true,
//use_gzip: True by default, use gzip to compress the response body's content; //use_gzip: True by default, use gzip to compress the response body's content;
"use_gzip": true, "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, //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 //0 means cache forever, the negative value means no cache
"static_files_cache_time": 5, "static_files_cache_time": 5,
//simple_controllers_map: Used to configure mapping from path to simple controller //simple_controllers_map: Used to configure mapping from path to simple controller
//"simple_controllers_map": [ "simple_controllers_map": [{
// { "path": "/path/name",
// "path": "/path/name", "controller": "controllerClassName",
// "controller": "controllerClassName", "http_methods": [
// "http_methods": [ "get",
// "get", "post"
// "post" ],
// ], "filters": [
// "filters": [ "FilterClassName"
// "FilterClassName" ]
// ] }],
// }
//],
//idle_connection_timeout: Defaults to 60 seconds, the lifetime //idle_connection_timeout: Defaults to 60 seconds, the lifetime
//of the connection without read or write //of the connection without read or write
"idle_connection_timeout": 60, "idle_connection_timeout": 60,
@ -290,10 +163,6 @@
//file with the extension ".gz" in the same path and send the compressed file to the client. //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. //The default value of gzip_static is true.
"gzip_static": 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". //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. //One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
"client_max_body_size": "1M", "client_max_body_size": "1M",
@ -303,57 +172,20 @@
"client_max_memory_body_size": "64K", "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". //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. //One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
"client_max_websocket_message_size": "128K", "client_max_websocket_message_size": "128K"
//reuse_port: Defaults to false, users can run multiple processes listening on the same port at the same time.
"reuse_port": 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: Define all plugins running in the application
"plugins": [ "plugins": [{
{
//name: The class name of the plugin //name: The class name of the plugin
"name": "drogon::plugin::PromExporter", //"name": "TestPlugin",
//dependencies: Plugins that the plugin depends on. It can be commented out //dependencies: Plugins that the plugin depends on. It can be commented out
"dependencies": [], "dependencies": [],
//config: The configuration of the plugin. This json object is the parameter to initialize the plugin. //config: The configuration of the plugin. This json object is the parameter to initialize the plugin.
//It can be commented out //It can be commented out
"config": { "config": {
"path": "/metrics" "heartbeat_interval": 2
} }
}, }],
{
"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: custom configuration for users. This object can be get by the app().getCustomConfig() method.
"custom_config": { "custom_config": {}
"realm": "drogonRealm",
"opaque": "drogonOpaque",
"credentials": [
{
"user": "drogon",
"password": "dr0g0n"
}
]
}
} }

View File

@ -1,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,42 +0,0 @@
FROM alpine:3.14
ARG USER=drogon
ARG UID=1000
ARG GID=1000
ARG USER_HOME=/drogon
ENV TZ=UTC
RUN apk update && apk --no-cache --upgrade add tzdata \
&& ln -snf /usr/share/zoneinfo/$TZ /etc/localtime \
&& echo $TZ > /etc/timezone
RUN apk --no-cache --upgrade add \
sudo curl wget cmake make pkgconfig git gcc g++ \
openssl libressl-dev jsoncpp-dev util-linux-dev zlib-dev c-ares-dev \
postgresql-dev mariadb-dev sqlite-dev hiredis-dev
RUN addgroup -S -g $GID $USER \
&& adduser -D -u $UID -G $USER -h $USER_HOME $USER \
&& mkdir -p /etc/sudoers.d \
&& echo "$USER ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/$USER \
&& chmod 0440 /etc/sudoers.d/$USER
USER $USER
WORKDIR $USER_HOME
ENV LANG=en_US.UTF-8 \
LANGUAGE=en_US:en \
LC_ALL=en_US.UTF-8 \
CC=gcc \
CXX=g++ \
AR=gcc-ar \
RANLIB=gcc-ranlib \
DROGON_INSTALLED_ROOT=$USER_HOME/install
RUN wget -O $USER_HOME/version.json https://api.github.com/repos/an-tao/drogon/git/refs/heads/master \
&& git clone https://github.com/an-tao/drogon $DROGON_INSTALLED_ROOT
RUN cd $DROGON_INSTALLED_ROOT \
&& sed -i 's/bash/sh/' ./build.sh \
&& ./build.sh

View File

@ -1,32 +0,0 @@
## Build Docker Image
```shell
$ cd drogon/docker/alpine # from this repository
$ docker build --no-cache --build-arg UID=`id -u` --build-arg GID=`id -g` -t drogon-alpine . # include last dot(.)
```
## Create a Drogon Project
```shell
$ cd ~/drogon_app # example
$ docker run --rm -v="$PWD:/drogon/app" -w="/drogon/app" drogon-alpine drogon_ctl create project hello_world
```
## Build the Project
```shell
$ cd hello_world
$ docker run --rm --volume="$PWD:/drogon/app" -w="/drogon/app/build" drogon-alpine sh -c "cmake .. && make"
```
## Start Server
```shell
$ docker run --name drogon_test --rm -u 0 -v="$PWD/build:/drogon/app" -w="/drogon/app" -p 8080:80 -d drogon-alpine ./hello_world # expose port 80 to 8080
```
## Stop Server
```shell
$ docker kill drogon_test
```

View File

@ -1,21 +0,0 @@
FROM archlinux:base-20210307.0.16708
RUN pacman -Syu --noconfirm && pacman -S wget sudo cmake make git gcc jsoncpp postgresql mariadb-clients hiredis --noconfirm
ENV LANG=en_US.UTF-8 \
LANGUAGE=en_US:en \
LC_ALL=en_US.UTF-8 \
CC=gcc \
CXX=g++ \
AR=gcc-ar \
RANLIB=gcc-ranlib \
IROOT=/install
ENV DROGON_ROOT="$IROOT/drogon"
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
RUN ./build.sh

View File

@ -1,30 +0,0 @@
FROM ubuntu:22.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 \
openssl libssl-dev libjsoncpp-dev uuid-dev zlib1g-dev libc-ares-dev\
postgresql-server-dev-all libmariadb-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 \
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
WORKDIR $DROGON_ROOT
RUN ./build.sh

View File

@ -17,33 +17,17 @@ add_executable(_drogon_ctl
create.cc create.cc
create_view.cc) create_view.cc)
target_link_libraries(_drogon_ctl ${PROJECT_NAME}) target_link_libraries(_drogon_ctl ${PROJECT_NAME})
if (WIN32 AND BUILD_SHARED_LIBS)
set(DROGON_FILE $<TARGET_FILE:drogon>)
if (USE_SUBMODULE)
set(TRANTOR_FILE $<TARGET_FILE:trantor>)
else()
set(TRANTOR_FILE $<TARGET_FILE:Trantor::Trantor>)
endif()
add_custom_command(TARGET _drogon_ctl POST_BUILD
COMMAND ${CMAKE_COMMAND}
-DCTL_FILE=${DROGON_FILE}
-DINSTALL_BIN_DIR=$<TARGET_FILE_DIR:_drogon_ctl>
-P
${CMAKE_CURRENT_SOURCE_DIR}/CopyDlls.cmake)
add_custom_command(TARGET _drogon_ctl POST_BUILD
COMMAND ${CMAKE_COMMAND}
-DCTL_FILE=${TRANTOR_FILE}
-DINSTALL_BIN_DIR=$<TARGET_FILE_DIR:_drogon_ctl>
-P
${CMAKE_CURRENT_SOURCE_DIR}/CopyDlls.cmake)
endif()
file(GLOB SCP_LIST ${CMAKE_CURRENT_SOURCE_DIR}/templates/*.csp) file(GLOB SCP_LIST ${CMAKE_CURRENT_SOURCE_DIR}/templates/*.csp)
foreach(cspFile ${SCP_LIST}) foreach(cspFile ${SCP_LIST})
message(STATUS "cspFile:" ${cspFile}) message(STATUS "cspFile:" ${cspFile})
get_filename_component(classname ${cspFile} NAME_WE) exec_program(basename
ARGS
"${cspFile} .csp"
OUTPUT_VARIABLE
classname)
message(STATUS "view classname:" ${classname}) message(STATUS "view classname:" ${classname})
add_custom_command(OUTPUT ${classname}.h ${classname}.cc add_custom_command(OUTPUT ${classname}.h ${classname}.cc
COMMAND $<TARGET_FILE:_drogon_ctl> COMMAND _drogon_ctl
ARGS ARGS
create create
view view
@ -55,33 +39,15 @@ endforeach()
add_executable(drogon_ctl ${ctl_sources} ${TEMPL_SRC}) add_executable(drogon_ctl ${ctl_sources} ${TEMPL_SRC})
target_link_libraries(drogon_ctl PRIVATE ${PROJECT_NAME}) target_link_libraries(drogon_ctl PRIVATE ${PROJECT_NAME})
target_include_directories(drogon_ctl PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(drogon_ctl PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
add_dependencies(drogon_ctl _drogon_ctl) add_dependencies(drogon_ctl
if(WIN32) trantor
target_link_libraries(drogon_ctl PRIVATE ws2_32 rpcrt4 iphlpapi) makeVersion
endif(WIN32) _drogon_ctl)
if(APPLE)
target_link_libraries(drogon_ctl PRIVATE resolv)
endif()
message(STATUS "bin:" ${INSTALL_BIN_DIR}) message(STATUS "bin:" ${INSTALL_BIN_DIR})
install(TARGETS drogon_ctl RUNTIME DESTINATION ${INSTALL_BIN_DIR}) install(TARGETS drogon_ctl RUNTIME DESTINATION ${INSTALL_BIN_DIR})
if(WIN32) install(PROGRAMS $<TARGET_FILE_DIR:drogon_ctl>/drogon_ctl
set(CTL_FILE $<TARGET_FILE:drogon_ctl>) DESTINATION ${INSTALL_BIN_DIR}
add_custom_command(TARGET drogon_ctl POST_BUILD RENAME dg_ctl)
COMMAND ${CMAKE_COMMAND}
-DCTL_FILE=${CTL_FILE}
-DINSTALL_BIN_DIR=${INSTALL_BIN_DIR}
-DRENAME_EXE=ON
-P
${CMAKE_CURRENT_SOURCE_DIR}/CopyDlls.cmake)
else(WIN32)
install(CODE "execute_process( \
COMMAND ${CMAKE_COMMAND} -E create_symlink \
./drogon_ctl \
./dg_ctl \
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})")
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/dg_ctl"
DESTINATION ${INSTALL_BIN_DIR})
endif(WIN32)
set(ctl_targets _drogon_ctl drogon_ctl) set(ctl_targets _drogon_ctl drogon_ctl)
set_property(TARGET ${ctl_targets} PROPERTY CXX_STANDARD ${DROGON_CXX_STANDARD}) set_property(TARGET ${ctl_targets} PROPERTY CXX_STANDARD ${DROGON_CXX_STANDARD})
set_property(TARGET ${ctl_targets} PROPERTY CXX_STANDARD_REQUIRED ON) set_property(TARGET ${ctl_targets} PROPERTY CXX_STANDARD_REQUIRED ON)

View File

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

View File

@ -1,8 +0,0 @@
make_directory("${INSTALL_BIN_DIR}")
get_filename_component(CTL_PATH ${CTL_FILE} DIRECTORY)
file(GLOB DLL_FILES ${CTL_PATH}/*.dll)
file(COPY ${DLL_FILES} DESTINATION ${INSTALL_BIN_DIR})
file(COPY ${CTL_FILE} DESTINATION ${INSTALL_BIN_DIR})
if (RENAME_EXE)
file(RENAME ${INSTALL_BIN_DIR}/drogon_ctl.exe ${INSTALL_BIN_DIR}/dg_ctl.exe)
endif()

View File

@ -1,7 +1,7 @@
/** /**
* *
* @file create.cc * create.cc
* @author An Tao * An Tao
* *
* Copyright 2018, An Tao. All rights reserved. * Copyright 2018, An Tao. All rights reserved.
* https://github.com/an-tao/drogon * https://github.com/an-tao/drogon
@ -18,15 +18,13 @@
#include <iostream> #include <iostream>
#include <memory> #include <memory>
using namespace drogon_ctl; using namespace drogon_ctl;
std::string create::detail() std::string create::detail()
{ {
return "Use create command to create some source files of drogon webapp\n\n" return "Use create command to create some source files of drogon webapp\n\n"
"Usage:drogon_ctl create <view|controller|filter|project|model> " "Usage:drogon_ctl create <view|controller|filter|project|model> "
"[-options] <object name>\n\n" "[-options] <object name>\n\n"
"drogon_ctl create view <csp file name> [-o <output path>] [-n " "drogon_ctl create view <csp file name> //create HttpView source "
"<namespace>] [--path-to-namespace] //create HttpView source files " "files from csp file\n\n"
"from csp files, namespace is prefixed of path-to-namespace\n\n"
"drogon_ctl create controller [-s] <[namespace::]class_name> //" "drogon_ctl create controller [-s] <[namespace::]class_name> //"
"create HttpSimpleController source files\n\n" "create HttpSimpleController source files\n\n"
"drogon_ctl create controller -h <[namespace::]class_name> //" "drogon_ctl create controller -h <[namespace::]class_name> //"
@ -42,8 +40,7 @@ std::string create::detail()
"create a plugin named class_name\n\n" "create a plugin named class_name\n\n"
"drogon_ctl create project <project_name> //" "drogon_ctl create project <project_name> //"
"create a project named project_name\n\n" "create a project named project_name\n\n"
"drogon_ctl create model <model_path> [-o <output path>] " "drogon_ctl create model <model_path> [--table=<table_name>] [-f]//"
"[--table=<table_name>] [-f]//"
"create model classes in model_path\n"; "create model classes in model_path\n";
} }

View File

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

View File

@ -95,11 +95,8 @@ void create_controller::newSimpleControllerHeaderFile(
const std::string &className) const std::string &className)
{ {
file << "#pragma once\n"; file << "#pragma once\n";
file << "\n";
file << "#include <drogon/HttpSimpleController.h>\n"; file << "#include <drogon/HttpSimpleController.h>\n";
file << "\n";
file << "using namespace drogon;\n"; file << "using namespace drogon;\n";
file << "\n";
std::string class_name = className; std::string class_name = className;
std::string namepace_path = "/"; std::string namepace_path = "/";
auto pos = class_name.find("::"); auto pos = class_name.find("::");
@ -118,7 +115,7 @@ void create_controller::newSimpleControllerHeaderFile(
<< class_name << ">\n"; << class_name << ">\n";
file << "{\n"; file << "{\n";
file << " public:\n"; file << " public:\n";
file << " void asyncHandleHttpRequest(const HttpRequestPtr& " file << " virtual void asyncHandleHttpRequest(const HttpRequestPtr& "
"req, std::function<void (const HttpResponsePtr &)> &&callback) " "req, std::function<void (const HttpResponsePtr &)> &&callback) "
"override;\n"; "override;\n";
@ -135,21 +132,18 @@ void create_controller::newSimpleControllerHeaderFile(
file << "}\n"; file << "}\n";
} }
} }
void create_controller::newSimpleControllerSourceFile( void create_controller::newSimpleControllerSourceFile(
std::ofstream &file, std::ofstream &file,
const std::string &className, const std::string &className,
const std::string &filename) const std::string &filename)
{ {
file << "#include \"" << filename << ".h\"\n"; file << "#include \"" << filename << ".h\"\n";
file << "\n";
auto pos = className.rfind("::"); auto pos = className.rfind("::");
auto class_name = className; auto class_name = className;
if (pos != std::string::npos) if (pos != std::string::npos)
{ {
auto namespacename = className.substr(0, pos); auto namespacename = className.substr(0, pos);
file << "using namespace " << namespacename << ";\n"; file << "using namespace " << namespacename << ";\n";
file << "\n";
class_name = className.substr(pos + 2); class_name = className.substr(pos + 2);
} }
file << "void " << class_name file << "void " << class_name
@ -157,7 +151,7 @@ void create_controller::newSimpleControllerSourceFile(
"std::function<void (const HttpResponsePtr &)> &&callback)\n"; "std::function<void (const HttpResponsePtr &)> &&callback)\n";
file << "{\n"; file << "{\n";
file << " //write your application logic here\n"; file << " //write your application logic here\n";
file << "}\n"; file << "}";
} }
void create_controller::newWebsockControllerHeaderFile( void create_controller::newWebsockControllerHeaderFile(
@ -165,11 +159,8 @@ void create_controller::newWebsockControllerHeaderFile(
const std::string &className) const std::string &className)
{ {
file << "#pragma once\n"; file << "#pragma once\n";
file << "\n";
file << "#include <drogon/WebSocketController.h>\n"; file << "#include <drogon/WebSocketController.h>\n";
file << "\n";
file << "using namespace drogon;\n"; file << "using namespace drogon;\n";
file << "\n";
std::string class_name = className; std::string class_name = className;
std::string namepace_path = "/"; std::string namepace_path = "/";
auto pos = class_name.find("::"); auto pos = class_name.find("::");
@ -188,14 +179,15 @@ void create_controller::newWebsockControllerHeaderFile(
<< class_name << ">\n"; << class_name << ">\n";
file << "{\n"; file << "{\n";
file << " public:\n"; file << " public:\n";
file << " void handleNewMessage(const WebSocketConnectionPtr&,\n"; file
<< " virtual void handleNewMessage(const WebSocketConnectionPtr&,\n";
file << " std::string &&,\n"; file << " std::string &&,\n";
file << " const WebSocketMessageType &) " file << " const WebSocketMessageType &) "
"override;\n"; "override;\n";
file << " void handleNewConnection(const HttpRequestPtr &,\n"; file << " virtual void handleNewConnection(const HttpRequestPtr &,\n";
file << " const " file << " const "
"WebSocketConnectionPtr&)override;\n"; "WebSocketConnectionPtr&)override;\n";
file << " void handleConnectionClosed(const " file << " virtual void handleConnectionClosed(const "
"WebSocketConnectionPtr&)override;\n"; "WebSocketConnectionPtr&)override;\n";
file << " WS_PATH_LIST_BEGIN\n"; file << " WS_PATH_LIST_BEGIN\n";
file << " //list path definitions here;\n"; file << " //list path definitions here;\n";
@ -208,21 +200,18 @@ void create_controller::newWebsockControllerHeaderFile(
file << "}\n"; file << "}\n";
} }
} }
void create_controller::newWebsockControllerSourceFile( void create_controller::newWebsockControllerSourceFile(
std::ofstream &file, std::ofstream &file,
const std::string &className, const std::string &className,
const std::string &filename) const std::string &filename)
{ {
file << "#include \"" << filename << ".h\"\n"; file << "#include \"" << filename << ".h\"\n";
file << "\n";
auto pos = className.rfind("::"); auto pos = className.rfind("::");
auto class_name = className; auto class_name = className;
if (pos != std::string::npos) if (pos != std::string::npos)
{ {
auto namespacename = className.substr(0, pos); auto namespacename = className.substr(0, pos);
file << "using namespace " << namespacename << ";\n"; file << "using namespace " << namespacename << ";\n";
file << "\n";
class_name = className.substr(pos + 2); class_name = className.substr(pos + 2);
} }
file << "void " << class_name file << "void " << class_name
@ -231,14 +220,12 @@ void create_controller::newWebsockControllerSourceFile(
file << "{\n"; file << "{\n";
file << " //write your application logic here\n"; file << " //write your application logic here\n";
file << "}\n"; file << "}\n";
file << "\n";
file << "void " << class_name file << "void " << class_name
<< "::handleNewConnection(const HttpRequestPtr &req,const " << "::handleNewConnection(const HttpRequestPtr &req,const "
"WebSocketConnectionPtr& wsConnPtr)\n"; "WebSocketConnectionPtr& wsConnPtr)\n";
file << "{\n"; file << "{\n";
file << " //write your application logic here\n"; file << " //write your application logic here\n";
file << "}\n"; file << "}\n";
file << "\n";
file << "void " << class_name file << "void " << class_name
<< "::handleConnectionClosed(const WebSocketConnectionPtr& " << "::handleConnectionClosed(const WebSocketConnectionPtr& "
"wsConnPtr)\n"; "wsConnPtr)\n";
@ -252,11 +239,8 @@ void create_controller::newHttpControllerHeaderFile(
const std::string &className) const std::string &className)
{ {
file << "#pragma once\n"; file << "#pragma once\n";
file << "\n";
file << "#include <drogon/HttpController.h>\n"; file << "#include <drogon/HttpController.h>\n";
file << "\n";
file << "using namespace drogon;\n"; file << "using namespace drogon;\n";
file << "\n";
std::string class_name = className; std::string class_name = className;
std::string namepace_path = "/"; std::string namepace_path = "/";
auto pos = class_name.find("::"); auto pos = class_name.find("::");
@ -305,25 +289,22 @@ void create_controller::newHttpControllerHeaderFile(
file << "}\n"; file << "}\n";
} }
} }
void create_controller::newHttpControllerSourceFile( void create_controller::newHttpControllerSourceFile(
std::ofstream &file, std::ofstream &file,
const std::string &className, const std::string &className,
const std::string &filename) const std::string &filename)
{ {
file << "#include \"" << filename << ".h\"\n"; file << "#include \"" << filename << ".h\"\n";
file << "\n";
auto pos = className.rfind("::"); auto pos = className.rfind("::");
auto class_name = className; auto class_name = className;
if (pos != std::string::npos) if (pos != std::string::npos)
{ {
auto namespacename = className.substr(0, pos); auto namespacename = className.substr(0, pos);
file << "using namespace " << namespacename << ";\n"; file << "using namespace " << namespacename << ";\n";
file << "\n";
class_name = className.substr(pos + 2); 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, void create_controller::createController(std::vector<std::string> &httpClasses,
@ -379,21 +360,20 @@ void create_controller::createController(const std::string &className,
} }
if (type == Http) if (type == Http)
{ {
std::cout << "Create a http controller: " << className << std::endl; std::cout << "create a http controller:" << className << std::endl;
newHttpControllerHeaderFile(oHeadFile, className); newHttpControllerHeaderFile(oHeadFile, className);
newHttpControllerSourceFile(oSourceFile, className, ctlName); newHttpControllerSourceFile(oSourceFile, className, ctlName);
} }
else if (type == Simple) else if (type == Simple)
{ {
std::cout << "Create a http simple controller: " << className std::cout << "create a http simple controller:" << className
<< std::endl; << std::endl;
newSimpleControllerHeaderFile(oHeadFile, className); newSimpleControllerHeaderFile(oHeadFile, className);
newSimpleControllerSourceFile(oSourceFile, className, ctlName); newSimpleControllerSourceFile(oSourceFile, className, ctlName);
} }
else if (type == WebSocket) else if (type == WebSocket)
{ {
std::cout << "Create a websocket controller: " << className std::cout << "create a websocket controller:" << className << std::endl;
<< std::endl;
newWebsockControllerHeaderFile(oHeadFile, className); newWebsockControllerHeaderFile(oHeadFile, className);
newWebsockControllerSourceFile(oSourceFile, className, ctlName); newWebsockControllerSourceFile(oSourceFile, className, ctlName);
} }
@ -464,8 +444,8 @@ void create_controller::createARestfulController(const std::string &className,
std::cerr << err.what() << std::endl; std::cerr << err.what() << std::endl;
exit(1); exit(1);
} }
std::cout << "Create a http restful API controller: " << className std::cout << "create a http restful API controller:" << className
<< std::endl; << std::endl;
std::cout << "File name: " << ctlName << ".h and " << ctlName << ".cc" std::cout << "file name: " << ctlName << ".h and " << ctlName << ".cc"
<< std::endl; << std::endl;
} }

View File

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

View File

@ -18,9 +18,7 @@
#include <string> #include <string>
#include <iostream> #include <iostream>
#ifndef _WIN32
#include <unistd.h> #include <unistd.h>
#endif
#include <fstream> #include <fstream>
#include <regex> #include <regex>
@ -69,7 +67,6 @@ static void createFilterSourceFile(std::ofstream &file,
data.insert("filename", fileName); data.insert("filename", fileName);
file << templ->genText(data); file << templ->genText(data);
} }
void create_filter::handleCommand(std::vector<std::string> &parameters) void create_filter::handleCommand(std::vector<std::string> &parameters)
{ {
if (parameters.size() < 1) if (parameters.size() < 1)

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -30,26 +30,24 @@ namespace drogon_ctl
{ {
struct ColumnInfo struct ColumnInfo
{ {
std::string colName_; std::string _colName;
std::string colValName_; std::string _colValName;
std::string colTypeName_; std::string _colTypeName;
std::string colType_; std::string _colType;
std::string colDatabaseType_; std::string _colDatabaseType;
std::string dbType_; std::string _dbType;
ssize_t colLength_{0}; ssize_t _colLength = 0;
size_t index_{0}; size_t _index = 0;
bool isAutoVal_{false}; bool _isAutoVal = false;
bool isPrimaryKey_{false}; bool _isPrimaryKey = false;
bool notNull_{false}; bool _notNull = false;
bool hasDefaultVal_{false}; bool _hasDefaultVal = false;
}; };
inline std::string nameTransform(const std::string &origName, bool isType) inline std::string nameTransform(const std::string &origName, bool isType)
{ {
auto str = origName; auto str = origName;
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { std::transform(str.begin(), str.end(), str.begin(), tolower);
return tolower(c);
});
std::string::size_type startPos = 0; std::string::size_type startPos = 0;
std::string::size_type pos; std::string::size_type pos;
std::string ret; std::string ret;
@ -68,7 +66,7 @@ inline std::string nameTransform(const std::string &origName, bool isType)
break; break;
} }
while (str[pos] == '_' || str[pos] == '.') while (str[pos] == '_' || str[pos] == '.')
++pos; pos++;
if (str[pos] >= 'a' && str[pos] <= 'z') if (str[pos] >= 'a' && str[pos] <= 'z')
str[pos] += ('A' - 'a'); str[pos] += ('A' - 'a');
startPos = pos; startPos = pos;
@ -77,137 +75,54 @@ inline std::string nameTransform(const std::string &origName, bool isType)
ret[0] += ('A' - 'a'); ret[0] += ('A' - 'a');
return ret; return ret;
} }
std::string escapeIdentifier(const std::string &identifier,
const std::string &rdbms);
class PivotTable class PivotTable
{ {
public: public:
PivotTable() = default; PivotTable() = default;
PivotTable(const Json::Value &json) PivotTable(const Json::Value &json)
: tableName_(json.get("table_name", "").asString())
{ {
if (tableName_.empty()) _tableName = json.get("table_name", "").asString();
if (_tableName.empty())
{ {
throw std::runtime_error("table_name can't be empty"); throw std::runtime_error("table_name can't be empty");
} }
originalKey_ = json.get("original_key", "").asString(); _originalKey = json.get("original_key", "").asString();
if (originalKey_.empty()) if (_originalKey.empty())
{ {
throw std::runtime_error("original_key can't be empty"); throw std::runtime_error("original_key can't be empty");
} }
targetKey_ = json.get("target_key", "").asString(); _targetKey = json.get("target_key", "").asString();
if (targetKey_.empty()) if (_targetKey.empty())
{ {
throw std::runtime_error("target_key can't be empty"); throw std::runtime_error("target_key can't be empty");
} }
} }
PivotTable reverse() const PivotTable reverse() const
{ {
PivotTable pivot; PivotTable pivot;
pivot.tableName_ = tableName_; pivot._tableName = _tableName;
pivot.originalKey_ = targetKey_; pivot._originalKey = _targetKey;
pivot.targetKey_ = originalKey_; pivot._targetKey = _originalKey;
return pivot; return pivot;
} }
const std::string &tableName() const const std::string &tableName() const
{ {
return tableName_; return _tableName;
} }
const std::string &originalKey() const const std::string &originalKey() const
{ {
return originalKey_; return _originalKey;
} }
const std::string &targetKey() const const std::string &targetKey() const
{ {
return targetKey_; return _targetKey;
} }
private: private:
std::string tableName_; std::string _tableName;
std::string originalKey_; std::string _originalKey;
std::string targetKey_; std::string _targetKey;
}; };
class ConvertMethod
{
public:
ConvertMethod(const Json::Value &convert)
{
tableName_ = convert.get("table", "*").asString();
colName_ = convert.get("column", "*").asString();
auto method = convert["method"];
if (method.isNull())
{
throw std::runtime_error("method - object is missing.");
} // endif
if (!method.isObject())
{
throw std::runtime_error("method is not an object.");
} // endif
methodBeforeDbWrite_ = method.get("before_db_write", "").asString();
methodAfterDbRead_ = method.get("after_db_read", "").asString();
auto includeFiles = convert["includes"];
if (includeFiles.isNull())
{
return;
} // endif
if (!includeFiles.isArray())
{
throw std::runtime_error("includes must be an array");
} // endif
for (auto &i : includeFiles)
{
includeFiles_.push_back(i.asString());
} // for
}
ConvertMethod() = default;
bool shouldConvert(const std::string &tableName,
const std::string &colName) const;
const std::string &tableName() const
{
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_;
}
private:
std::string tableName_{"*"};
std::string colName_{"*"};
std::string methodBeforeDbWrite_;
std::string methodAfterDbRead_;
std::vector<std::string> includeFiles_;
};
class Relationship class Relationship
{ {
public: public:
@ -217,58 +132,54 @@ class Relationship
HasMany, HasMany,
ManyToMany ManyToMany
}; };
Relationship(const Json::Value &relationship) Relationship(const Json::Value &relationship)
{ {
auto type = relationship.get("type", "has one").asString(); auto type = relationship.get("type", "has one").asString();
if (type == "has one") if (type == "has one")
{ {
type_ = Relationship::Type::HasOne; _type = Relationship::Type::HasOne;
} }
else if (type == "has many") else if (type == "has many")
{ {
type_ = Relationship::Type::HasMany; _type = Relationship::Type::HasMany;
} }
else if (type == "many to many") else if (type == "many to many")
{ {
type_ = Relationship::Type::ManyToMany; _type = Relationship::Type::ManyToMany;
} }
else else
{ {
char message[128]; char message[128];
snprintf(message, sprintf(message, "Invalid relationship type: %s", type.data());
sizeof(message),
"Invalid relationship type: %s",
type.data());
throw std::runtime_error(message); throw std::runtime_error(message);
} }
originalTableName_ = _originalTableName =
relationship.get("original_table_name", "").asString(); relationship.get("original_table_name", "").asString();
if (originalTableName_.empty()) if (_originalTableName.empty())
{ {
throw std::runtime_error("original_table_name can't be empty"); throw std::runtime_error("original_table_name can't be empty");
} }
originalKey_ = relationship.get("original_key", "").asString(); _originalKey = relationship.get("original_key", "").asString();
if (originalKey_.empty()) if (_originalKey.empty())
{ {
throw std::runtime_error("original_key can't be empty"); throw std::runtime_error("original_key can't be empty");
} }
originalTableAlias_ = _originalTableAlias =
relationship.get("original_table_alias", "").asString(); relationship.get("original_table_alias", "").asString();
targetTableName_ = relationship.get("target_table_name", "").asString(); _targetTableName = relationship.get("target_table_name", "").asString();
if (targetTableName_.empty()) if (_targetTableName.empty())
{ {
throw std::runtime_error("target_table_name can't be empty"); throw std::runtime_error("target_table_name can't be empty");
} }
targetKey_ = relationship.get("target_key", "").asString(); _targetKey = relationship.get("target_key", "").asString();
if (targetKey_.empty()) if (_targetKey.empty())
{ {
throw std::runtime_error("target_key can't be empty"); throw std::runtime_error("target_key can't be empty");
} }
targetTableAlias_ = _targetTableAlias =
relationship.get("target_table_alias", "").asString(); relationship.get("target_table_alias", "").asString();
enableReverse_ = relationship.get("enable_reverse", false).asBool(); _enableReverse = relationship.get("enable_reverse", false).asBool();
if (type_ == Type::ManyToMany) if (_type == Type::ManyToMany)
{ {
auto &pivot = relationship["pivot_table"]; auto &pivot = relationship["pivot_table"];
if (pivot.isNull()) if (pivot.isNull())
@ -276,97 +187,84 @@ class Relationship
throw std::runtime_error( throw std::runtime_error(
"ManyToMany relationship needs a pivot table"); "ManyToMany relationship needs a pivot table");
} }
pivotTable_ = PivotTable(pivot); _pivotTable = PivotTable(pivot);
} }
} }
Relationship() = default; Relationship() = default;
Relationship reverse() const Relationship reverse() const
{ {
Relationship r; Relationship r;
if (type_ == Type::HasMany) if (_type == Type::HasMany)
{ {
r.type_ = Type::HasOne; r._type = Type::HasOne;
} }
else else
{ {
r.type_ = type_; r._type = _type;
} }
r.originalTableName_ = targetTableName_; r._originalTableName = _targetTableName;
r.originalTableAlias_ = targetTableAlias_; r._originalTableAlias = _targetTableAlias;
r.originalKey_ = targetKey_; r._originalKey = _targetKey;
r.targetTableName_ = originalTableName_; r._targetTableName = _originalTableName;
r.targetTableAlias_ = originalTableAlias_; r._targetTableAlias = _originalTableAlias;
r.targetKey_ = originalKey_; r._targetKey = _originalKey;
r.enableReverse_ = enableReverse_; r._enableReverse = _enableReverse;
r.pivotTable_ = pivotTable_.reverse(); r._pivotTable = _pivotTable.reverse();
return r; return r;
} }
Type type() const Type type() const
{ {
return type_; return _type;
} }
bool enableReverse() const bool enableReverse() const
{ {
return enableReverse_; return _enableReverse;
} }
const std::string &originalTableName() const const std::string &originalTableName() const
{ {
return originalTableName_; return _originalTableName;
} }
const std::string &originalTableAlias() const const std::string &originalTableAlias() const
{ {
return originalTableAlias_; return _originalTableAlias;
} }
const std::string &originalKey() const const std::string &originalKey() const
{ {
return originalKey_; return _originalKey;
} }
const std::string &targetTableName() const const std::string &targetTableName() const
{ {
return targetTableName_; return _targetTableName;
} }
const std::string &targetTableAlias() const const std::string &targetTableAlias() const
{ {
return targetTableAlias_; return _targetTableAlias;
} }
const std::string &targetKey() const const std::string &targetKey() const
{ {
return targetKey_; return _targetKey;
} }
const PivotTable &pivotTable() const const PivotTable &pivotTable() const
{ {
return pivotTable_; return _pivotTable;
} }
private: private:
Type type_{Type::HasOne}; Type _type = Type::HasOne;
std::string originalTableName_; std::string _originalTableName;
std::string originalTableAlias_; std::string _originalTableAlias;
std::string targetTableName_; std::string _targetTableName;
std::string targetTableAlias_; std::string _targetTableAlias;
std::string originalKey_; std::string _originalKey;
std::string targetKey_; std::string _targetKey;
bool enableReverse_{false}; bool _enableReverse = false;
PivotTable pivotTable_; PivotTable _pivotTable;
}; };
class create_model : public DrObject<create_model>, public CommandHandler class create_model : public DrObject<create_model>, public CommandHandler
{ {
public: public:
void handleCommand(std::vector<std::string> &parameters) override; virtual void handleCommand(std::vector<std::string> &parameters) override;
virtual std::string script() override
std::string script() override
{ {
return "create Model classes files"; return "create Model classes files";
} }
@ -378,22 +276,18 @@ class create_model : public DrObject<create_model>, public CommandHandler
const Json::Value &config, const Json::Value &config,
const std::string &singleModelName); const std::string &singleModelName);
#if USE_POSTGRESQL #if USE_POSTGRESQL
void createModelClassFromPG( void createModelClassFromPG(const std::string &path,
const std::string &path,
const DbClientPtr &client, const DbClientPtr &client,
const std::string &tableName, const std::string &tableName,
const std::string &schema, const std::string &schema,
const Json::Value &restfulApiConfig, const Json::Value &restfulApiConfig,
const std::vector<Relationship> &relationships, const std::vector<Relationship> &relationships);
const std::vector<ConvertMethod> &convertMethods);
void createModelFromPG( void createModelFromPG(
const std::string &path, const std::string &path,
const DbClientPtr &client, const DbClientPtr &client,
const std::string &schema, const std::string &schema,
const Json::Value &restfulApiConfig, const Json::Value &restfulApiConfig,
std::map<std::string, std::vector<Relationship>> &relationships, std::map<std::string, std::vector<Relationship>> &relationships);
std::map<std::string, std::vector<ConvertMethod>> &convertMethods);
#endif #endif
#if USE_MYSQL #if USE_MYSQL
void createModelClassFromMysql( void createModelClassFromMysql(
@ -401,14 +295,12 @@ class create_model : public DrObject<create_model>, public CommandHandler
const DbClientPtr &client, const DbClientPtr &client,
const std::string &tableName, const std::string &tableName,
const Json::Value &restfulApiConfig, const Json::Value &restfulApiConfig,
const std::vector<Relationship> &relationships, const std::vector<Relationship> &relationships);
const std::vector<ConvertMethod> &convertMethods);
void createModelFromMysql( void createModelFromMysql(
const std::string &path, const std::string &path,
const DbClientPtr &client, const DbClientPtr &client,
const Json::Value &restfulApiConfig, const Json::Value &restfulApiConfig,
std::map<std::string, std::vector<Relationship>> &relationships, std::map<std::string, std::vector<Relationship>> &relationships);
std::map<std::string, std::vector<ConvertMethod>> &convertMethods);
#endif #endif
#if USE_SQLITE3 #if USE_SQLITE3
void createModelClassFromSqlite3( void createModelClassFromSqlite3(
@ -416,19 +308,16 @@ class create_model : public DrObject<create_model>, public CommandHandler
const DbClientPtr &client, const DbClientPtr &client,
const std::string &tableName, const std::string &tableName,
const Json::Value &restfulApiConfig, const Json::Value &restfulApiConfig,
const std::vector<Relationship> &relationships, const std::vector<Relationship> &relationships);
const std::vector<ConvertMethod> &convertMethod);
void createModelFromSqlite3( void createModelFromSqlite3(
const std::string &path, const std::string &path,
const DbClientPtr &client, const DbClientPtr &client,
const Json::Value &restfulApiConfig, const Json::Value &restfulApiConfig,
std::map<std::string, std::vector<Relationship>> &relationships, std::map<std::string, std::vector<Relationship>> &relationships);
std::map<std::string, std::vector<ConvertMethod>> &convertMethod);
#endif #endif
void createRestfulAPIController(const DrTemplateData &tableInfo, void createRestfulAPIController(const DrTemplateData &tableInfo,
const Json::Value &restfulApiConfig); const Json::Value &restfulApiConfig);
std::string dbname_; std::string _dbname;
bool forceOverwrite_{false}; bool _forceOverwrite = false;
std::string outputPath_;
}; };
} // namespace drogon_ctl } // namespace drogon_ctl

View File

@ -18,9 +18,7 @@
#include <string> #include <string>
#include <iostream> #include <iostream>
#ifndef _WIN32
#include <unistd.h> #include <unistd.h>
#endif
#include <fstream> #include <fstream>
#include <regex> #include <regex>
@ -69,7 +67,6 @@ static void createPluginSourceFile(std::ofstream &file,
data.insert("filename", fileName); data.insert("filename", fileName);
file << templ->genText(data); file << templ->genText(data);
} }
void create_plugin::handleCommand(std::vector<std::string> &parameters) void create_plugin::handleCommand(std::vector<std::string> &parameters)
{ {
if (parameters.size() < 1) if (parameters.size() < 1)

View File

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

View File

@ -14,16 +14,10 @@
#include "create_project.h" #include "create_project.h"
#include <drogon/DrTemplateBase.h> #include <drogon/DrTemplateBase.h>
#include <drogon/utils/Utilities.h>
#include <iostream> #include <iostream>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#ifndef _WIN32
#include <unistd.h> #include <unistd.h>
#else
#include <io.h>
#include <direct.h>
#endif
#include <fstream> #include <fstream>
using namespace drogon_ctl; using namespace drogon_ctl;
@ -38,7 +32,6 @@ void create_project::handleCommand(std::vector<std::string> &parameters)
auto pName = parameters[0]; auto pName = parameters[0];
createProject(pName); createProject(pName);
} }
static void newCmakeFile(std::ofstream &cmakeFile, static void newCmakeFile(std::ofstream &cmakeFile,
const std::string &projectName) const std::string &projectName)
{ {
@ -47,59 +40,30 @@ static void newCmakeFile(std::ofstream &cmakeFile,
auto templ = DrTemplateBase::newTemplate("cmake.csp"); auto templ = DrTemplateBase::newTemplate("cmake.csp");
cmakeFile << templ->genText(data); cmakeFile << templ->genText(data);
} }
static void newMainFile(std::ofstream &mainFile) static void newMainFile(std::ofstream &mainFile)
{ {
auto templ = DrTemplateBase::newTemplate("demoMain"); auto templ = DrTemplateBase::newTemplate("demoMain");
mainFile << templ->genText(); mainFile << templ->genText();
} }
static void newGitIgFile(std::ofstream &gitFile) static void newGitIgFile(std::ofstream &gitFile)
{ {
auto templ = DrTemplateBase::newTemplate("gitignore.csp"); auto templ = DrTemplateBase::newTemplate("gitignore.csp");
gitFile << templ->genText(); gitFile << templ->genText();
} }
static void newConfigJsonFile(std::ofstream &configJsonFile) static void newConfigFile(std::ofstream &configFile)
{ {
auto templ = DrTemplateBase::newTemplate("config_json"); auto templ = DrTemplateBase::newTemplate("config");
configJsonFile << templ->genText(); configFile << templ->genText();
} }
static void newConfigYamlFile(std::ofstream &configYamlFile)
{
auto templ = DrTemplateBase::newTemplate("config_yaml");
configYamlFile << templ->genText();
}
static void newModelConfigFile(std::ofstream &configFile) static void newModelConfigFile(std::ofstream &configFile)
{ {
auto templ = DrTemplateBase::newTemplate("model_json"); auto templ = DrTemplateBase::newTemplate("model_json");
configFile << templ->genText(); configFile << templ->genText();
} }
static void newTestMainFile(std::ofstream &mainFile)
{
auto templ = DrTemplateBase::newTemplate("test_main");
mainFile << templ->genText();
}
static void newTestCmakeFile(std::ofstream &testCmakeFile,
const std::string &projectName)
{
HttpViewData data;
data.insert("ProjectName", projectName);
auto templ = DrTemplateBase::newTemplate("test_cmake");
testCmakeFile << templ->genText(data);
}
void create_project::createProject(const std::string &projectName) void create_project::createProject(const std::string &projectName)
{ {
#ifdef _WIN32
if (_access(projectName.data(), 0) == 0)
#else
if (access(projectName.data(), 0) == 0) if (access(projectName.data(), 0) == 0)
#endif
{ {
std::cerr std::cerr
<< "The directory already exists, please use another project name!" << "The directory already exists, please use another project name!"
@ -107,37 +71,25 @@ void create_project::createProject(const std::string &projectName)
exit(1); exit(1);
} }
std::cout << "create a project named " << projectName << std::endl; std::cout << "create a project named " << projectName << std::endl;
mkdir(projectName.data(), 0755);
drogon::utils::createPath(projectName);
// 1.create CMakeLists.txt // 1.create CMakeLists.txt
#ifdef _WIN32
auto r = _chdir(projectName.data());
#else
auto r = chdir(projectName.data()); auto r = chdir(projectName.data());
#endif
(void)(r); (void)(r);
std::ofstream cmakeFile("CMakeLists.txt", std::ofstream::out); std::ofstream cmakeFile("CMakeLists.txt", std::ofstream::out);
newCmakeFile(cmakeFile, projectName); newCmakeFile(cmakeFile, projectName);
std::ofstream mainFile("main.cc", std::ofstream::out); std::ofstream mainFile("main.cc", std::ofstream::out);
newMainFile(mainFile); newMainFile(mainFile);
drogon::utils::createPath("views"); mkdir("views", 0755);
drogon::utils::createPath("controllers"); mkdir("controllers", 0755);
drogon::utils::createPath("filters"); mkdir("filters", 0755);
drogon::utils::createPath("plugins"); mkdir("plugins", 0755);
drogon::utils::createPath("build"); mkdir("build", 0755);
drogon::utils::createPath("models"); mkdir("models", 0755);
drogon::utils::createPath("test");
std::ofstream gitFile(".gitignore", std::ofstream::out); std::ofstream gitFile(".gitignore", std::ofstream::out);
newGitIgFile(gitFile); newGitIgFile(gitFile);
std::ofstream configJsonFile("config.json", std::ofstream::out); std::ofstream configFile("config.json", std::ofstream::out);
newConfigJsonFile(configJsonFile); newConfigFile(configFile);
std::ofstream configYamlFile("config.yaml", std::ofstream::out);
newConfigYamlFile(configYamlFile);
std::ofstream modelConfigFile("models/model.json", std::ofstream::out); std::ofstream modelConfigFile("models/model.json", std::ofstream::out);
newModelConfigFile(modelConfigFile); newModelConfigFile(modelConfigFile);
std::ofstream testMainFile("test/test_main.cc", std::ofstream::out);
newTestMainFile(testMainFile);
std::ofstream testCmakeFile("test/CMakeLists.txt", std::ofstream::out);
newTestCmakeFile(testCmakeFile, projectName);
} }

View File

@ -16,21 +16,19 @@
#include <drogon/DrObject.h> #include <drogon/DrObject.h>
#include "CommandHandler.h" #include "CommandHandler.h"
using namespace drogon; using namespace drogon;
namespace drogon_ctl namespace drogon_ctl
{ {
class create_project : public DrObject<create_project>, public CommandHandler class create_project : public DrObject<create_project>, public CommandHandler
{ {
public: public:
void handleCommand(std::vector<std::string> &parameters) override; virtual void handleCommand(std::vector<std::string> &parameters) override;
virtual std::string script() override
std::string script() override
{ {
return "create a project"; return "create a project";
} }
protected: protected:
std::string outputPath_{"."}; std::string _outputPath = ".";
void createProject(const std::string &projectName); void createProject(const std::string &projectName);
}; };
} // namespace drogon_ctl } // namespace drogon_ctl

View File

@ -1,7 +1,7 @@
/** /**
* *
* @file create_view.cc * create_view.cc
* @author An Tao * An Tao
* *
* Copyright 2018, An Tao. All rights reserved. * Copyright 2018, An Tao. All rights reserved.
* https://github.com/an-tao/drogon * https://github.com/an-tao/drogon
@ -14,7 +14,6 @@
#include "create_view.h" #include "create_view.h"
#include "cmd.h" #include "cmd.h"
#include <drogon/utils/Utilities.h>
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include <string> #include <string>
@ -46,14 +45,13 @@ static std::string &replace_all(std::string &str,
{ {
str = str.replace(pos, old_value.length(), new_value); str = str.replace(pos, old_value.length(), new_value);
pos += new_value.length() - old_value.length(); pos += new_value.length() - old_value.length();
++pos; pos++;
} }
else else
break; break;
} }
return str; return str;
} }
static void parseCxxLine(std::ofstream &oSrcFile, static void parseCxxLine(std::ofstream &oSrcFile,
const std::string &line, const std::string &line,
const std::string &streamName, const std::string &streamName,
@ -67,7 +65,6 @@ static void parseCxxLine(std::ofstream &oSrcFile,
oSrcFile << tmp << "\n"; oSrcFile << tmp << "\n";
} }
} }
static void outputVal(std::ofstream &oSrcFile, static void outputVal(std::ofstream &oSrcFile,
const std::string &streamName, const std::string &streamName,
const std::string &viewDataName, const std::string &viewDataName,
@ -78,12 +75,12 @@ static void outputVal(std::ofstream &oSrcFile,
<< "\"];\n"; << "\"];\n";
oSrcFile << " if(val.type()==typeid(const char *)){\n"; oSrcFile << " if(val.type()==typeid(const char *)){\n";
oSrcFile << " " << streamName oSrcFile << " " << streamName
<< "<<*(std::any_cast<const char *>(&val));\n"; << "<<*any_cast<const char *>(&val);\n";
oSrcFile << " }else " oSrcFile << " }else "
"if(val.type()==typeid(std::string)||val.type()==typeid(const " "if(val.type()==typeid(std::string)||val.type()==typeid(const "
"std::string)){\n"; "std::string)){\n";
oSrcFile << " " << streamName oSrcFile << " " << streamName
<< "<<*(std::any_cast<const std::string>(&val));\n"; << "<<*any_cast<const std::string>(&val);\n";
oSrcFile << " }\n"; oSrcFile << " }\n";
oSrcFile << "}\n"; oSrcFile << "}\n";
} }
@ -112,15 +109,11 @@ static void parseLine(std::ofstream &oSrcFile,
{ {
std::string::size_type pos(0); std::string::size_type pos(0);
// std::cout<<line<<"("<<line.length()<<")\n"; // std::cout<<line<<"("<<line.length()<<")\n";
if (line.length() > 0 && line[line.length() - 1] == '\r')
{
line.resize(line.length() - 1);
}
if (line.length() == 0) if (line.length() == 0)
{ {
// std::cout<<"blank line!"<<std::endl; // std::cout<<"blank line!"<<std::endl;
// std::cout<<streamName<<"<<\"\\n\";\n"; // std::cout<<streamName<<"<<\"\\n\";\n";
if (returnFlag && !cxx_flag) if (returnFlag)
oSrcFile << streamName << "<<\"\\n\";\n"; oSrcFile << streamName << "<<\"\\n\";\n";
return; return;
} }
@ -156,10 +149,10 @@ static void parseLine(std::ofstream &oSrcFile,
std::string keyName = newLine.substr(0, pos); std::string keyName = newLine.substr(0, pos);
auto iter = keyName.begin(); auto iter = keyName.begin();
while (iter != keyName.end() && *iter == ' ') while (iter != keyName.end() && *iter == ' ')
++iter; iter++;
auto iterEnd = iter; auto iterEnd = iter;
while (iterEnd != keyName.end() && *iterEnd != ' ') while (iterEnd != keyName.end() && *iterEnd != ' ')
++iterEnd; iterEnd++;
keyName = std::string(iter, iterEnd); keyName = std::string(iter, iterEnd);
outputVal(oSrcFile, streamName, viewDataName, keyName); outputVal(oSrcFile, streamName, viewDataName, keyName);
std::string tailLine = std::string tailLine =
@ -189,10 +182,10 @@ static void parseLine(std::ofstream &oSrcFile,
std::string keyName = newLine.substr(0, pos); std::string keyName = newLine.substr(0, pos);
auto iter = keyName.begin(); auto iter = keyName.begin();
while (iter != keyName.end() && *iter == ' ') while (iter != keyName.end() && *iter == ' ')
++iter; iter++;
auto iterEnd = iter; auto iterEnd = iter;
while (iterEnd != keyName.end() && *iterEnd != ' ') while (iterEnd != keyName.end() && *iterEnd != ' ')
++iterEnd; iterEnd++;
keyName = std::string(iter, iterEnd); keyName = std::string(iter, iterEnd);
outputSubView(oSrcFile, streamName, viewDataName, keyName); outputSubView(oSrcFile, streamName, viewDataName, keyName);
std::string tailLine = std::string tailLine =
@ -250,95 +243,39 @@ static void parseLine(std::ofstream &oSrcFile,
void create_view::handleCommand(std::vector<std::string> &parameters) void create_view::handleCommand(std::vector<std::string> &parameters)
{ {
for (auto iter = parameters.begin(); iter != parameters.end();) for (auto iter = parameters.begin(); iter != parameters.end(); iter++)
{ {
auto &file = *iter; auto file = *iter;
if (file == "-o" || file == "--output") if (file == "-o" || file == "--output")
{ {
iter = parameters.erase(iter); iter = parameters.erase(iter);
if (iter != parameters.end()) if (iter != parameters.end())
{ {
outputPath_ = *iter; _outputPath = *iter;
iter = parameters.erase(iter); iter = parameters.erase(iter);
} }
continue; break;
}
else if (file == "-n" || file == "--namespace")
{
iter = parameters.erase(iter);
if (iter != parameters.end())
{
namespaces_ = utils::splitString(*iter, "::");
iter = parameters.erase(iter);
}
continue;
}
else if (file == "--path-to-namespace")
{
iter = parameters.erase(iter);
pathToNamespaceFlag_ = true;
continue;
} }
else if (file[0] == '-') else if (file[0] == '-')
{ {
std::cout << ARGS_ERROR_STR << std::endl; std::cout << ARGS_ERROR_STR << std::endl;
return; return;
} }
++iter;
} }
createViewFiles(parameters); createViewFiles(parameters);
} }
void create_view::createViewFiles(std::vector<std::string> &cspFileNames) void create_view::createViewFiles(std::vector<std::string> &cspFileNames)
{ {
for (auto const &file : cspFileNames) for (auto const &file : cspFileNames)
{ {
std::cout << "create view:" << file << std::endl; std::cout << "create view:" << file << std::endl;
if (createViewFile(file) != 0) createViewFile(file);
exit(1);
} }
} }
int create_view::createViewFile(const std::string &script_filename) int create_view::createViewFile(const std::string &script_filename)
{ {
std::cout << "create HttpView Class file by " << script_filename std::cout << "create HttpView Class file by " << script_filename
<< std::endl; << std::endl;
if (pathToNamespaceFlag_)
{
std::string::size_type pos1 = 0, pos2 = 0;
if (script_filename.length() >= 2 && script_filename[0] == '.' &&
(script_filename[1] == '/' || script_filename[1] == '\\'))
{
pos1 = pos2 = 2;
}
else if (script_filename.length() >= 1 &&
(script_filename[0] == '/' || script_filename[0] == '\\'))
{
pos1 = pos2 = 1;
}
while (pos2 < script_filename.length() - 1)
{
if (script_filename[pos2] == '/' || script_filename[pos2] == '\\')
{
if (pos2 > pos1)
{
namespaces_.push_back(
script_filename.substr(pos1, pos2 - pos1));
}
pos1 = ++pos2;
}
else
{
++pos2;
}
}
}
std::string npPrefix;
for (auto &np : namespaces_)
{
npPrefix += np;
npPrefix += "_";
}
std::ifstream infile(script_filename.c_str(), std::ifstream::in); std::ifstream infile(script_filename.c_str(), std::ifstream::in);
if (infile) if (infile)
{ {
@ -351,22 +288,16 @@ int create_view::createViewFile(const std::string &script_filename)
className = className.substr(pos + 1); className = className.substr(pos + 1);
} }
std::cout << "className=" << className << std::endl; std::cout << "className=" << className << std::endl;
std::string headFileName = std::string headFileName = _outputPath + "/" + className + ".h";
outputPath_ + "/" + npPrefix + className + ".h"; std::string sourceFilename = _outputPath + "/" + className + ".cc";
std::string sourceFilename =
outputPath_ + "/" + npPrefix + className + ".cc";
std::ofstream oHeadFile(headFileName.c_str(), std::ofstream::out); std::ofstream oHeadFile(headFileName.c_str(), std::ofstream::out);
std::ofstream oSourceFile(sourceFilename.c_str(), std::ofstream oSourceFile(sourceFilename.c_str(),
std::ofstream::out); std::ofstream::out);
if (!oHeadFile || !oSourceFile) if (!oHeadFile || !oSourceFile)
{
std::cerr << "Can't open " << headFileName << " or "
<< sourceFilename << "\n";
return -1; return -1;
}
newViewHeaderFile(oHeadFile, className); newViewHeaderFile(oHeadFile, className);
newViewSourceFile(oSourceFile, className, npPrefix, infile); newViewSourceFile(oSourceFile, className, infile);
} }
else else
return -1; return -1;
@ -378,41 +309,29 @@ int create_view::createViewFile(const std::string &script_filename)
} }
return 0; return 0;
} }
void create_view::newViewHeaderFile(std::ofstream &file, void create_view::newViewHeaderFile(std::ofstream &file,
const std::string &className) const std::string &className)
{ {
file << "//this file is generated by program automatically,don't modify " file << "//this file is generated by program automatically,don't modify "
"it!\n"; "it!\n";
file << "#include <drogon/DrTemplate.h>\n"; file << "#include <drogon/DrTemplate.h>\n";
for (auto &np : namespaces_) file << "using namespace drogon;\n";
{ file << "class " << className << ":public DrTemplate<" << className
file << "namespace " << np << "\n";
file << "{\n";
}
file << "class " << className << ":public drogon::DrTemplate<" << className
<< ">\n"; << ">\n";
file << "{\npublic:\n\t" << className << "(){};\n\tvirtual ~" << className file << "{\npublic:\n\t" << className << "(){};\n\tvirtual ~" << className
<< "(){};\n\t" << "(){};\n\t"
"virtual std::string genText(const drogon::DrTemplateData &) " "virtual std::string genText(const DrTemplateData &) override;\n};";
"override;\n};\n";
for (std::size_t i = 0; i < namespaces_.size(); ++i)
{
file << "}\n";
}
} }
void create_view::newViewSourceFile(std::ofstream &file, void create_view::newViewSourceFile(std::ofstream &file,
const std::string &className, const std::string &className,
const std::string &namespacePrefix,
std::ifstream &infile) std::ifstream &infile)
{ {
file << "//this file is generated by program(drogon_ctl) " file << "//this file is generated by program(drogon_ctl) "
"automatically,don't modify it!\n"; "automatically,don't modify it!\n";
file << "#include \"" << namespacePrefix << className << ".h\"\n"; file << "#include \"" << className << ".h\"\n";
file << "#include <drogon/utils/OStringStream.h>\n";
file << "#include <drogon/utils/Utilities.h>\n";
file << "#include <string>\n"; file << "#include <string>\n";
file << "#include <sstream>\n";
file << "#include <map>\n"; file << "#include <map>\n";
file << "#include <vector>\n"; file << "#include <vector>\n";
file << "#include <set>\n"; file << "#include <set>\n";
@ -424,26 +343,19 @@ void create_view::newViewSourceFile(std::ofstream &file,
file << "#include <deque>\n"; file << "#include <deque>\n";
file << "#include <queue>\n"; file << "#include <queue>\n";
// Find layout tag // file << "using namespace std;\n";
std::string layoutName; // file <<"void __attribute__((constructor)) startup()\n";
std::regex layoutReg("<%layout[ \\t]+(((?!%\\}).)*[^ \\t])[ \\t]*%>"); // file <<"{std::cout<<\"dynamic lib start to load!\"<<std::endl;}\n";
for (std::string buffer; std::getline(infile, buffer);) // file <<"void __attribute__((destructor)) shutdown()\n";
{ // file <<"{std::cout<<\"dynamic lib start to unload!\"<<std::endl;}\n";
std::smatch results; std::string buffer;
if (std::regex_search(buffer, results, layoutReg)) char line[8192];
{ int import_flag = 0;
if (results.size() > 1)
{ while (infile.getline(line, sizeof(line)))
layoutName = results[1].str();
break;
}
}
}
infile.clear();
infile.seekg(0, std::ifstream::beg);
bool import_flag{false};
for (std::string buffer; std::getline(infile, buffer);)
{ {
buffer = line;
std::string::size_type pos(0); std::string::size_type pos(0);
if (!import_flag) if (!import_flag)
@ -452,12 +364,12 @@ void create_view::newViewSourceFile(std::ofstream &file,
std::transform(lowerBuffer.begin(), std::transform(lowerBuffer.begin(),
lowerBuffer.end(), lowerBuffer.end(),
lowerBuffer.begin(), lowerBuffer.begin(),
[](unsigned char c) { return tolower(c); }); ::tolower);
if ((pos = lowerBuffer.find(cxx_include)) != std::string::npos) if ((pos = lowerBuffer.find(cxx_include)) != std::string::npos)
{ {
// std::cout<<"haha find it!"<<endl; // std::cout<<"haha find it!"<<endl;
std::string newLine = buffer.substr(pos + cxx_include.length()); std::string newLine = buffer.substr(pos + cxx_include.length());
import_flag = true; import_flag = 1;
if ((pos = newLine.find(cxx_end)) != std::string::npos) if ((pos = newLine.find(cxx_end)) != std::string::npos)
{ {
newLine = newLine.substr(0, pos); newLine = newLine.substr(0, pos);
@ -477,6 +389,7 @@ void create_view::newViewSourceFile(std::ofstream &file,
{ {
std::string newLine = buffer.substr(0, pos); std::string newLine = buffer.substr(0, pos);
file << newLine << "\n"; file << newLine << "\n";
break; break;
} }
else else
@ -487,29 +400,14 @@ void create_view::newViewSourceFile(std::ofstream &file,
} }
} }
// std::cout<<"import_flag="<<import_flag<<std::endl; // std::cout<<"import_flag="<<import_flag<<std::endl;
if (!import_flag) if (import_flag == 0)
{ {
infile.clear(); infile.clear();
infile.seekg(0, std::ifstream::beg); infile.seekg(0, std::ifstream::beg);
} }
if (!namespaces_.empty()) // std::cout<<"file pos:"<<infile.tellg()<<std::endl;
{
file << "using namespace ";
for (std::size_t i = 0; i < namespaces_.size(); ++i)
{
if (i != namespaces_.size() - 1)
{
file << namespaces_[i] << "::";
}
else
{
file << namespaces_[i] << ";";
}
}
file << "\n";
}
file << "using namespace drogon;\n";
std::string viewDataName = className + "_view_data"; std::string viewDataName = className + "_view_data";
// virtual std::string genText(const DrTemplateData &) // virtual std::string genText(const DrTemplateData &)
file << "std::string " << className << "::genText(const DrTemplateData& " file << "std::string " << className << "::genText(const DrTemplateData& "
@ -518,37 +416,18 @@ void create_view::newViewSourceFile(std::ofstream &file,
std::string streamName = className + "_tmp_stream"; std::string streamName = className + "_tmp_stream";
// oSrcFile <<"\tstd::string "<<bodyName<<";\n"; // oSrcFile <<"\tstd::string "<<bodyName<<";\n";
file << "\tdrogon::OStringStream " << streamName << ";\n"; file << "\tstd::stringstream " << streamName << ";\n";
file << "\tstd::string layoutName{\"" << layoutName << "\"};\n";
int cxx_flag = 0; int cxx_flag = 0;
for (std::string buffer; std::getline(infile, buffer);) while (infile.getline(line, sizeof(line)))
{ {
buffer = line;
if (buffer.length() > 0) if (buffer.length() > 0)
{ {
std::smatch results;
if (std::regex_search(buffer, results, layoutReg))
{
if (results.size() > 1)
{
continue;
}
}
std::regex re("\\{%[ \\t]*(((?!%\\}).)*[^ \\t])[ \\t]*%\\}"); std::regex re("\\{%[ \\t]*(((?!%\\}).)*[^ \\t])[ \\t]*%\\}");
buffer = std::regex_replace(buffer, re, "<%c++$$$$<<$1;%>"); buffer = std::regex_replace(buffer, re, "<%c++$$$$<<$1;%>");
} }
parseLine(file, buffer, streamName, viewDataName, cxx_flag); parseLine(file, buffer, streamName, viewDataName, cxx_flag);
} }
file << "if(layoutName.empty())\n{\n";
file << "std::string ret{std::move(" << streamName << ".str())};\n"; file << "return " << streamName << ".str();\n}\n";
file << "return ret;\n}else\n{\n";
file << "auto templ = DrTemplateBase::newTemplate(layoutName);\n";
file << "if(!templ) return \"\";\n";
file << "HttpViewData data = " << viewDataName << ";\n";
file << "auto str = std::move(" << streamName << ".str());\n";
file << "if(!str.empty() && str[str.length()-1] == '\\n') "
"str.resize(str.length()-1);\n";
file << "data[\"\"] = std::move(str);\n";
file << "return templ->genText(data);\n";
file << "}\n}\n";
} }

View File

@ -1,7 +1,7 @@
/** /**
* *
* @file create_view.h * create_view.h
* @author An Tao * An Tao
* *
* Copyright 2018, An Tao. All rights reserved. * Copyright 2018, An Tao. All rights reserved.
* https://github.com/an-tao/drogon * https://github.com/an-tao/drogon
@ -17,29 +17,24 @@
#include <drogon/DrObject.h> #include <drogon/DrObject.h>
#include "CommandHandler.h" #include "CommandHandler.h"
using namespace drogon; using namespace drogon;
namespace drogon_ctl namespace drogon_ctl
{ {
class create_view : public DrObject<create_view>, public CommandHandler class create_view : public DrObject<create_view>, public CommandHandler
{ {
public: public:
void handleCommand(std::vector<std::string> &parameters) override; virtual void handleCommand(std::vector<std::string> &parameters) override;
virtual std::string script() override
std::string script() override
{ {
return "create view class files"; return "create view class files";
} }
protected: protected:
std::string outputPath_{"."}; std::string _outputPath = ".";
std::vector<std::string> namespaces_;
bool pathToNamespaceFlag_{false};
void createViewFiles(std::vector<std::string> &cspFileNames); void createViewFiles(std::vector<std::string> &cspFileNames);
int createViewFile(const std::string &script_filename); int createViewFile(const std::string &script_filename);
void newViewHeaderFile(std::ofstream &file, const std::string &className); void newViewHeaderFile(std::ofstream &file, const std::string &className);
void newViewSourceFile(std::ofstream &file, void newViewSourceFile(std::ofstream &file,
const std::string &className, const std::string &className,
const std::string &namespacePrefix,
std::ifstream &infile); std::ifstream &infile);
}; };
} // namespace drogon_ctl } // namespace drogon_ctl

View File

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

View File

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

View File

@ -1,7 +1,7 @@
/** /**
* *
* @file main.cc * main.cc
* @author An Tao * An Tao
* *
* Copyright 2018, An Tao. All rights reserved. * Copyright 2018, An Tao. All rights reserved.
* https://github.com/an-tao/drogon * https://github.com/an-tao/drogon
@ -13,20 +13,21 @@
*/ */
#include "cmd.h" #include "cmd.h"
#include <string> #include <string>
#include <vector> #include <vector>
#include <iostream> #include <iostream>
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
std::vector<std::string> args;
if (argc < 2) if (argc < 2)
{ {
args = {"help"}; std::vector<std::string> args = {"help"};
exeCommand(args); exeCommand(args);
return 0; return 0;
} }
for (int i = 1; i < argc; ++i) std::vector<std::string> args;
for (int i = 1; i < argc; i++)
{ {
args.push_back(argv[i]); args.push_back(argv[i]);
} }

View File

@ -18,17 +18,10 @@
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <iomanip> #include <iomanip>
#include <cstdlib> #include <stdlib.h>
#include <json/json.h>
#include <fstream>
#include <string>
#include <unordered_map>
#ifndef _WIN32
#include <unistd.h> #include <unistd.h>
#endif
using namespace drogon_ctl; using namespace drogon_ctl;
std::string press::detail() std::string press::detail()
{ {
return "Use press command to do stress testing\n" return "Use press command to do stress testing\n"
@ -36,19 +29,17 @@ std::string press::detail()
" -n num number of requests(default : 1)\n" " -n num number of requests(default : 1)\n"
" -t num number of threads(default : 1)\n" " -t num number of threads(default : 1)\n"
" -c num concurrent connections(default : 1)\n" " -c num concurrent connections(default : 1)\n"
" -k disable SSL certificate validation(default: enable)\n" // " -k keep alive(default: no)\n"
" -f customize http request json file(default: disenable)\n" " -q no progress indication(default: no)\n\n"
" -q no progress indication(default: show)\n\n"
"example: drogon_ctl press -n 10000 -c 100 -t 4 -q " "example: drogon_ctl press -n 10000 -c 100 -t 4 -q "
"http://localhost:8080/index.html -f ./http_request.json\n"; "http://localhost:8080/index.html\n";
} }
void outputErrorAndExit(const std::string_view &err) void outputErrorAndExit(const string_view &err)
{ {
std::cout << err << std::endl; std::cout << err << std::endl;
exit(1); exit(1);
} }
void press::handleCommand(std::vector<std::string> &parameters) void press::handleCommand(std::vector<std::string> &parameters)
{ {
for (auto iter = parameters.begin(); iter != parameters.end(); iter++) for (auto iter = parameters.begin(); iter != parameters.end(); iter++)
@ -58,7 +49,7 @@ void press::handleCommand(std::vector<std::string> &parameters)
{ {
if (param == "-n") if (param == "-n")
{ {
++iter; iter++;
if (iter == parameters.end()) if (iter == parameters.end())
{ {
outputErrorAndExit("No number of requests!"); outputErrorAndExit("No number of requests!");
@ -66,7 +57,7 @@ void press::handleCommand(std::vector<std::string> &parameters)
auto &num = *iter; auto &num = *iter;
try try
{ {
numOfRequests_ = std::stoll(num); _numOfRequests = std::stoll(num);
} }
catch (...) catch (...)
{ {
@ -79,7 +70,7 @@ void press::handleCommand(std::vector<std::string> &parameters)
auto num = param.substr(2); auto num = param.substr(2);
try try
{ {
numOfRequests_ = std::stoll(num); _numOfRequests = std::stoll(num);
} }
catch (...) catch (...)
{ {
@ -92,7 +83,7 @@ void press::handleCommand(std::vector<std::string> &parameters)
{ {
if (param == "-t") if (param == "-t")
{ {
++iter; iter++;
if (iter == parameters.end()) if (iter == parameters.end())
{ {
outputErrorAndExit("No number of threads!"); outputErrorAndExit("No number of threads!");
@ -100,7 +91,7 @@ void press::handleCommand(std::vector<std::string> &parameters)
auto &num = *iter; auto &num = *iter;
try try
{ {
numOfThreads_ = std::stoll(num); _numOfThreads = std::stoll(num);
} }
catch (...) catch (...)
{ {
@ -113,7 +104,7 @@ void press::handleCommand(std::vector<std::string> &parameters)
auto num = param.substr(2); auto num = param.substr(2);
try try
{ {
numOfThreads_ = std::stoll(num); _numOfThreads = std::stoll(num);
} }
catch (...) catch (...)
{ {
@ -126,7 +117,7 @@ void press::handleCommand(std::vector<std::string> &parameters)
{ {
if (param == "-c") if (param == "-c")
{ {
++iter; iter++;
if (iter == parameters.end()) if (iter == parameters.end())
{ {
outputErrorAndExit("No number of connections!"); outputErrorAndExit("No number of connections!");
@ -134,7 +125,7 @@ void press::handleCommand(std::vector<std::string> &parameters)
auto &num = *iter; auto &num = *iter;
try try
{ {
numOfConnections_ = std::stoll(num); _numOfConnections = std::stoll(num);
} }
catch (...) catch (...)
{ {
@ -147,7 +138,7 @@ void press::handleCommand(std::vector<std::string> &parameters)
auto num = param.substr(2); auto num = param.substr(2);
try try
{ {
numOfConnections_ = std::stoll(num); _numOfConnections = std::stoll(num);
} }
catch (...) catch (...)
{ {
@ -156,230 +147,88 @@ void press::handleCommand(std::vector<std::string> &parameters)
continue; continue;
} }
} }
else if (param.find("-f") == 0) // else if (param == "-k")
{ // {
if (param == "-f") // _keepAlive = true;
{ // continue;
++iter; // }
if (iter == parameters.end())
{
outputErrorAndExit("No http request json file!");
}
httpRequestJsonFile_ = *iter;
continue;
}
else
{
httpRequestJsonFile_ = param.substr(2);
continue;
}
}
else if (param == "-k")
{
certValidation_ = false;
continue;
}
else if (param == "-q") else if (param == "-q")
{ {
processIndication_ = false; _processIndication = false;
} }
else if (param[0] != '-') else if (param[0] != '-')
{ {
url_ = param; _url = param;
} }
} }
// std::cout << "n=" << numOfRequests_ << std::endl; // std::cout << "n=" << _numOfRequests << std::endl;
// std::cout << "t=" << numOfThreads_ << std::endl; // std::cout << "t=" << _numOfThreads << std::endl;
// std::cout << "c=" << numOfConnections_ << std::endl; // std::cout << "c=" << _numOfConnections << std::endl;
// std::cout << "q=" << processIndication_ << std::endl; // std::cout << "q=" << _processIndication << std::endl;
// std::cout << "url=" << url_ << std::endl; // std::cout << "url=" << _url << std::endl;
if (url_.empty() || url_.compare(0, 4, "http") != 0 || if (_url.empty() || _url.find("http") != 0 ||
(url_.compare(4, 3, "://") != 0 && url_.compare(4, 4, "s://") != 0)) _url.find("://") == std::string::npos)
{ {
outputErrorAndExit("Invalid URL"); outputErrorAndExit("Invalid URL");
} }
else else
{ {
auto pos = url_.find("://"); auto pos = _url.find("://");
auto posOfPath = url_.find('/', pos + 3); auto posOfPath = _url.find("/", pos + 3);
if (posOfPath == std::string::npos) if (posOfPath == std::string::npos)
{ {
host_ = url_; _host = _url;
path_ = "/"; _path = "/";
} }
else else
{ {
host_ = url_.substr(0, posOfPath); _host = _url.substr(0, posOfPath);
path_ = url_.substr(posOfPath); _path = _url.substr(posOfPath);
} }
} }
// std::cout << "host=" << _host << std::endl;
/* // std::cout << "path=" << _path << std::endl;
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(); doTesting();
} }
void press::doTesting() void press::doTesting()
{ {
createRequestAndClients(); createRequestAndClients();
if (clients_.empty()) if (_clients.empty())
{ {
outputErrorAndExit("No connection!"); outputErrorAndExit("No connection!");
} }
statistics_.startDate_ = trantor::Date::now(); _stat._startDate = trantor::Date::now();
for (auto &client : clients_) for (auto &client : _clients)
{ {
sendRequest(client); sendRequest(client);
} }
loopPool_->wait(); _loopPool->wait();
} }
void press::createRequestAndClients() void press::createRequestAndClients()
{ {
loopPool_ = std::make_unique<trantor::EventLoopThreadPool>(numOfThreads_); _loopPool = std::make_unique<trantor::EventLoopThreadPool>(_numOfThreads);
loopPool_->start(); _loopPool->start();
for (size_t i = 0; i < numOfConnections_; ++i) for (size_t i = 0; i < _numOfConnections; i++)
{ {
auto client = HttpClient::newHttpClient(host_, auto client =
loopPool_->getNextLoop(), HttpClient::newHttpClient(_host, _loopPool->getNextLoop());
false,
certValidation_);
client->enableCookies(); client->enableCookies();
clients_.push_back(client); _clients.push_back(client);
} }
} }
void press::sendRequest(const HttpClientPtr &client) void press::sendRequest(const HttpClientPtr &client)
{ {
auto numOfRequest = statistics_.numOfRequestsSent_++; auto numOfRequest = _stat._numOfRequestsSent++;
if (numOfRequest >= numOfRequests_) if (numOfRequest >= _numOfRequests)
{ {
return; return;
} }
auto request = HttpRequest::newHttpRequest();
HttpRequestPtr request; request->setPath(_path);
if (createHttpRequestFunc_)
{
request = createHttpRequestFunc_();
}
else
{
request = HttpRequest::newHttpRequest();
request->setPath(path_);
request->setMethod(Get); request->setMethod(Get);
}
// std::cout << "send!" << std::endl; // std::cout << "send!" << std::endl;
client->sendRequest( client->sendRequest(
request, request,
@ -388,23 +237,23 @@ void press::sendRequest(const HttpClientPtr &client)
if (r == ReqResult::Ok) if (r == ReqResult::Ok)
{ {
// std::cout << "OK" << std::endl; // std::cout << "OK" << std::endl;
goodNum = ++statistics_.numOfGoodResponse_; goodNum = ++_stat._numOfGoodResponse;
badNum = statistics_.numOfBadResponse_; badNum = _stat._numOfBadResponse;
statistics_.bytesRecieved_ += resp->body().length(); _stat._bytesRecieved += resp->body().length();
auto delay = trantor::Date::now().microSecondsSinceEpoch() - auto delay = trantor::Date::now().microSecondsSinceEpoch() -
request->creationDate().microSecondsSinceEpoch(); request->creationDate().microSecondsSinceEpoch();
statistics_.totalDelay_ += delay; _stat._totalDelay += delay;
} }
else else
{ {
goodNum = statistics_.numOfGoodResponse_; goodNum = _stat._numOfGoodResponse;
badNum = ++statistics_.numOfBadResponse_; badNum = ++_stat._numOfBadResponse;
if (badNum > numOfRequests_ / 10) if (badNum > _numOfRequests / 10)
{ {
outputErrorAndExit("Too many errors"); outputErrorAndExit("Too many errors");
} }
} }
if (goodNum + badNum >= numOfRequests_) if (goodNum + badNum >= _numOfRequests)
{ {
outputResults(); outputResults();
} }
@ -417,7 +266,7 @@ void press::sendRequest(const HttpClientPtr &client)
}); });
} }
if (processIndication_) if (_processIndication)
{ {
auto rec = goodNum + badNum; auto rec = goodNum + badNum;
if (rec % 100000 == 0) if (rec % 100000 == 0)
@ -431,37 +280,34 @@ void press::sendRequest(const HttpClientPtr &client)
void press::outputResults() void press::outputResults()
{ {
static std::mutex mtx;
size_t totalSent = 0; size_t totalSent = 0;
size_t totalRecv = 0; size_t totalRecv = 0;
for (auto &client : clients_) for (auto &client : _clients)
{ {
totalSent += client->bytesSent(); totalSent += client->bytesSent();
totalRecv += client->bytesReceived(); totalRecv += client->bytesReceived();
} }
auto now = trantor::Date::now(); auto now = trantor::Date::now();
auto microSecs = now.microSecondsSinceEpoch() - auto microSecs = now.microSecondsSinceEpoch() -
statistics_.startDate_.microSecondsSinceEpoch(); _stat._startDate.microSecondsSinceEpoch();
double seconds = (double)microSecs / 1000000.0; double seconds = (double)microSecs / 1000000.0;
auto rps = static_cast<size_t>(statistics_.numOfGoodResponse_ / seconds); size_t rps = _stat._numOfGoodResponse / seconds;
std::cout << std::endl; std::cout << std::endl;
std::cout << "TOTALS: " << numOfConnections_ << " connect, " std::cout << "TOTALS: " << _numOfConnections << " connect, "
<< numOfRequests_ << " requests, " << _numOfRequests << " requests, " << _stat._numOfGoodResponse
<< statistics_.numOfGoodResponse_ << " success, " << " success, " << _stat._numOfBadResponse << " fail"
<< statistics_.numOfBadResponse_ << " fail" << std::endl;
std::cout << "TRAFFIC: "
<< statistics_.bytesRecieved_ / statistics_.numOfGoodResponse_
<< " avg bytes, "
<< (totalRecv - statistics_.bytesRecieved_) /
statistics_.numOfGoodResponse_
<< " avg overhead, " << statistics_.bytesRecieved_ << " bytes, "
<< totalRecv - statistics_.bytesRecieved_ << " overhead"
<< std::endl; << std::endl;
std::cout << "TRAFFIC: " << _stat._bytesRecieved / _stat._numOfGoodResponse
<< " avg bytes, "
<< (totalRecv - _stat._bytesRecieved) / _stat._numOfGoodResponse
<< " avg overhead, " << _stat._bytesRecieved << " bytes, "
<< totalRecv - _stat._bytesRecieved << " overhead" << std::endl;
std::cout << std::setiosflags(std::ios::fixed) << std::setprecision(3) std::cout << std::setiosflags(std::ios::fixed) << std::setprecision(3)
<< "TIMING: " << seconds << " seconds, " << rps << " rps, " << "TIMING: " << seconds << " seconds, " << rps << " rps, "
<< (double)(statistics_.totalDelay_) / << (double)(_stat._totalDelay) / _stat._numOfGoodResponse / 1000
statistics_.numOfGoodResponse_ / 1000
<< " ms avg req time" << std::endl; << " ms avg req time" << std::endl;
std::cout << "SPEED: download " << totalRecv / seconds / 1000 std::cout << "SPEED: download " << totalRecv / seconds / 1000

View File

@ -20,7 +20,6 @@
#include <drogon/HttpClient.h> #include <drogon/HttpClient.h>
#include <trantor/utils/Date.h> #include <trantor/utils/Date.h>
#include <trantor/net/EventLoopThreadPool.h> #include <trantor/net/EventLoopThreadPool.h>
#include <functional>
#include <string> #include <string>
#include <atomic> #include <atomic>
#include <memory> #include <memory>
@ -32,50 +31,44 @@ namespace drogon_ctl
{ {
struct Statistics struct Statistics
{ {
std::atomic_size_t numOfRequestsSent_{0}; std::atomic_size_t _numOfRequestsSent = ATOMIC_VAR_INIT(0);
std::atomic_size_t bytesRecieved_{0}; std::atomic_size_t _bytesRecieved = ATOMIC_VAR_INIT(0);
std::atomic_size_t numOfGoodResponse_{0}; std::atomic_size_t _numOfGoodResponse = ATOMIC_VAR_INIT(0);
std::atomic_size_t numOfBadResponse_{0}; std::atomic_size_t _numOfBadResponse = ATOMIC_VAR_INIT(0);
std::atomic_size_t totalDelay_{0}; std::atomic_size_t _totalDelay = ATOMIC_VAR_INIT(0);
trantor::Date startDate_; trantor::Date _startDate;
trantor::Date endDate_; trantor::Date _endDate;
}; };
class press : public DrObject<press>, public CommandHandler class press : public DrObject<press>, public CommandHandler
{ {
public: public:
void handleCommand(std::vector<std::string> &parameters) override; virtual void handleCommand(std::vector<std::string> &parameters) override;
virtual std::string script() override
std::string script() override
{ {
return "Do stress testing(Use 'drogon_ctl help press' for more " return "Do stress testing(Use 'drogon_ctl help press' for more "
"information)"; "information)";
} }
virtual bool isTopCommand() override
bool isTopCommand() override
{ {
return true; return true;
} }
virtual std::string detail() override;
std::string detail() override;
private: private:
size_t numOfThreads_{1}; size_t _numOfThreads = 1;
size_t numOfRequests_{1}; size_t _numOfRequests = 1;
size_t numOfConnections_{1}; size_t _numOfConnections = 1;
std::string httpRequestJsonFile_; // bool _keepAlive = false;
std::function<HttpRequestPtr()> createHttpRequestFunc_; bool _processIndication = true;
bool certValidation_{true}; std::string _url;
bool processIndication_{true}; std::string _host;
std::string url_; std::string _path;
std::string host_;
std::string path_;
void doTesting(); void doTesting();
void createRequestAndClients(); void createRequestAndClients();
void sendRequest(const HttpClientPtr &client); void sendRequest(const HttpClientPtr &client);
void outputResults(); void outputResults();
std::unique_ptr<trantor::EventLoopThreadPool> loopPool_; std::unique_ptr<trantor::EventLoopThreadPool> _loopPool;
std::vector<HttpClientPtr> clients_; std::vector<HttpClientPtr> _clients;
Statistics statistics_; Statistics _stat;
}; };
} // namespace drogon_ctl } // namespace drogon_ctl

View File

@ -5,12 +5,7 @@ include(CheckIncludeFileCXX)
check_include_file_cxx(any HAS_ANY) check_include_file_cxx(any HAS_ANY)
check_include_file_cxx(string_view HAS_STRING_VIEW) check_include_file_cxx(string_view HAS_STRING_VIEW)
check_include_file_cxx(coroutine HAS_COROUTINE) if(HAS_ANY AND HAS_STRING_VIEW)
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) set(CMAKE_CXX_STANDARD 17)
else() else()
set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD 14)
@ -19,26 +14,24 @@ endif ()
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_EXTENSIONS OFF)
add_executable(${PROJECT_NAME} main.cc) add_executable([[ProjectName]] main.cc)
# ############################################################################## ##########
# If you include the drogon source code locally in your project, use this method # If you include the drogon source code locally in your project, use this method to add drogon
# to add drogon
# add_subdirectory(drogon) # add_subdirectory(drogon)
# target_link_libraries(${PROJECT_NAME} PRIVATE drogon) # target_link_libraries([[ProjectName]] PRIVATE drogon)
# ##########
# and comment out the following lines
find_package(Drogon CONFIG REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)
# ############################################################################## find_package(Drogon CONFIG REQUIRED)
target_link_libraries([[ProjectName]] PRIVATE Drogon::Drogon)
if(CMAKE_CXX_STANDARD LESS 17) if(CMAKE_CXX_STANDARD LESS 17)
message(FATAL_ERROR "c++17 or higher is required") #With C++14, use boost to support any and string_view
elseif (CMAKE_CXX_STANDARD LESS 20) message(STATUS "use c++14")
message(STATUS "use c++17") find_package(Boost 1.61.0 REQUIRED)
target_include_directories([[ProjectName]] PRIVATE ${Boost_INCLUDE_DIRS})
else() else()
message(STATUS "use c++20") message(STATUS "use c++17")
endif() endif()
aux_source_directory(controllers CTL_SRC) aux_source_directory(controllers CTL_SRC)
@ -46,30 +39,18 @@ aux_source_directory(filters FILTER_SRC)
aux_source_directory(plugins PLUGIN_SRC) aux_source_directory(plugins PLUGIN_SRC)
aux_source_directory(models MODEL_SRC) aux_source_directory(models MODEL_SRC)
drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views file(GLOB SCP_LIST ${CMAKE_CURRENT_SOURCE_DIR}/views/*.csp)
${CMAKE_CURRENT_BINARY_DIR}) foreach(cspFile ${SCP_LIST})
# use the following line to create views with namespaces. message(STATUS "cspFile:" ${cspFile})
# drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views EXEC_PROGRAM(basename ARGS "${cspFile} .csp" OUTPUT_VARIABLE classname)
# ${CMAKE_CURRENT_BINARY_DIR} TRUE) message(STATUS "view classname:" ${classname})
# use the following line to create views with namespace CHANGE_ME prefixed ADD_CUSTOM_COMMAND(OUTPUT ${classname}.h ${classname}.cc
# and path namespaces. COMMAND drogon_ctl
# drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views ARGS create view ${cspFile}
# ${CMAKE_CURRENT_BINARY_DIR} TRUE CHANGE_ME) DEPENDS ${cspFile}
VERBATIM )
set(VIEWSRC ${VIEWSRC} ${classname}.cc)
endforeach()
target_include_directories(${PROJECT_NAME} target_include_directories([[ProjectName]] PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models)
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} target_sources([[ProjectName]] PRIVATE ${SRC_DIR} ${CTL_SRC} ${FILTER_SRC} ${VIEWSRC} ${PLUGIN_SRC} ${MODEL_SRC})
${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

@ -4,47 +4,73 @@
/* /*
//ssl:The global ssl files setting //ssl:The global ssl files setting
"ssl": { "ssl": {
"cert": "../../trantor/trantor/tests/server.crt", "cert": "../../trantor/trantor/tests/server.pem",
"key": "../../trantor/trantor/tests/server.key" "key": "../../trantor/trantor/tests/server.pem"
},*/ },
"listeners": [{ "listeners": [
{
//address: Ip address,0.0.0.0 by default //address: Ip address,0.0.0.0 by default
"address": "0.0.0.0", "address": "0.0.0.0",
//port: Port number //port: Port number
"port": 8088, "port": 80,
//https: If true, use https for security,false by default //https: If true, use https for security,false by default
"https": false "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": ""
}
],
"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,
//connection_number: 1 by default, if the 'is_fast' is true, the number is the number of
//connections per IO thread, otherwise it is the total number of all connections.
"connection_number": 1
}
],*/
"app": { "app": {
//threads_num: The number of IO threads, 1 by default, if the value is set to 0, the number of threads //threads_num: 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 //is the number of CPU cores
"threads_num": 2, "threads_num": 1,
//enable_session: False by default //enable_session: False by default
"enable_session": false, "enable_session": false,
"session_timeout": 0, "session_timeout": 0,
//session_cookie_key: The cookie key of the session, "JSESSIONID" by default //document_root: Root path of HTTP document, defaut path is ./
"session_cookie_key": "JSESSIONID",
//session_max_age: The max age of the session cookie, -1 by default
"session_max_age": -1,
//document_root: Root path of HTTP document, default path is ./
"document_root": "./", "document_root": "./",
//home_page: Set the HTML file of the home page, the default value is "index.html" //home_page: Set the HTML file of the home page, the default value is "index.html"
//If there isn't any handler registered to the path "/", the home page file in the "document_root" is send to clients as a response //If there isn't any handler registered to the path "/", the home page file in the "document_root" is send to clients as a response
//to the request for "/". //to the request for "/".
"home_page": "index.html", "home_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. //upload_path: The path to save the uploaded file. "uploads" by default.
//If the path isn't prefixed with /, ./ or ../, //If the path isn't prefixed with /, ./ or ../,
//it is relative path of document_root path //it is relative path of document_root path
"upload_path": "uploads", "upload_path": "uploads",
/* file_types: /* file_types:
* HTTP download file types, the file types supported by drogon * HTTP download file types,The file types supported by drogon
* by default are "html", "js", "css", "xml", "xsl", "txt", "svg", * by default are "html", "js", "css", "xml", "xsl", "txt", "svg",
* "ttf", "otf", "woff2", "woff" , "eot", "png", "jpg", "jpeg", * "ttf", "otf", "woff2", "woff" , "eot", "png", "jpg", "jpeg",
* "gif", "bmp", "ico", "icns", etc. */ * "gif", "bmp", "ico", "icns", etc. */
@ -64,7 +90,7 @@
], ],
//max_connections: maximum connections number,100000 by default //max_connections: maximum connections number,100000 by default
"max_connections": 100000, "max_connections": 100000,
//max_connections_per_ip: maximum connections number per client,0 by default which means no limit //max_connections_per_ip: maximum connections number per clinet,0 by default which means no limit
"max_connections_per_ip": 0, "max_connections_per_ip": 0,
//Load_dynamic_views: False by default, when set to true, drogon //Load_dynamic_views: False by default, when set to true, drogon
//compiles and loads dynamically "CSP View Files" in directories defined //compiles and loads dynamically "CSP View Files" in directories defined
@ -91,11 +117,9 @@
}, },
//run_as_daemon: False by default //run_as_daemon: False by default
"run_as_daemon": false, "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 by default, if true, the program will be restart by the parent after exiting;
"relaunch_on_error": false, "relaunch_on_error": false,
//use_sendfile: True by default, if true, the program //use_sendfile: True by default, if ture, the program
//uses sendfile() system-call to send static files to clients; //uses sendfile() system-call to send static files to clients;
"use_sendfile": true, "use_sendfile": true,
//use_gzip: True by default, use gzip to compress the response body's content; //use_gzip: True by default, use gzip to compress the response body's content;
@ -103,6 +127,18 @@
//static_files_cache_time: 5 (seconds) by default, the time in which the static file response is cached, //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 //0 means cache forever, the negative value means no cache
"static_files_cache_time": 5, "static_files_cache_time": 5,
//simple_controllers_map: Used to configure mapping from path to simple controller
"simple_controllers_map": [{
"path": "/path/name",
"controller": "controllerClassName",
"http_methods": [
"get",
"post"
],
"filters": [
"FilterClassName"
]
}],
//idle_connection_timeout: Defaults to 60 seconds, the lifetime //idle_connection_timeout: Defaults to 60 seconds, the lifetime
//of the connection without read or write //of the connection without read or write
"idle_connection_timeout": 60, "idle_connection_timeout": 60,
@ -111,10 +147,10 @@
"server_header_field": "", "server_header_field": "",
//enable_server_header: Set true to force drogon to add a 'Server' header to each HTTP response. The default //enable_server_header: Set true to force drogon to add a 'Server' header to each HTTP response. The default
//value is true. //value is true.
"enable_server_header": false, "enable_server_header": true,
//enable_date_header: Set true to force drogon to add a 'Date' header to each HTTP response. The default //enable_date_header: Set true to force drogon to add a 'Date' header to each HTTP response. The default
//value is true. //value is true.
"enable_date_header": false, "enable_date_header": true,
//keepalive_requests: Set the maximum number of requests that can be served through one keep-alive connection. //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. //After the maximum number of requests are made, the connection is closed.
//The default value of 0 means no limit. //The default value of 0 means no limit.
@ -141,18 +177,13 @@
//plugins: Define all plugins running in the application //plugins: Define all plugins running in the application
"plugins": [{ "plugins": [{
//name: The class name of the plugin //name: The class name of the plugin
"name": "my_plugin::SimpleReverseProxy", //"name": "TestPlugin",
//dependencies: Plugins that the plugin depends on. It can be commented out //dependencies: Plugins that the plugin depends on. It can be commented out
"dependencies": [], "dependencies": [],
//config: The configuration of the plugin. This json object is the parameter to initialize the plugin. //config: The configuration of the plugin. This json object is the parameter to initialize the plugin.
//It can be commented out //It can be commented out
"config": { "config": {
// The pipelining depth of HTTP clients. "heartbeat_interval": 2
"pipelining": 16,
"backends": ["http://127.0.0.1:8848"],
"same_client_to_same_backend": false,
//The number of connections created by proxy for each backend in very event loop (IO thread).
"connection_factor": 1
} }
}], }],
//custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method. //custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method.

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,10 +1,9 @@
#include <drogon/drogon.h> #include <drogon/drogon.h>
int main() { int main() {
//Set HTTP listener address and port //Set HTTP listener address and port
drogon::app().addListener("0.0.0.0", 5555); drogon::app().addListener("0.0.0.0",80);
//Load config file //Load config file
//drogon::app().loadConfigFile("../config.json"); //drogon::app().loadConfigFile("../config.json");
//drogon::app().loadConfigFile("../config.yaml");
//Run HTTP framework,the method will block in the internal event loop //Run HTTP framework,the method will block in the internal event loop
drogon::app().run(); drogon::app().run();
return 0; return 0;

View File

@ -22,7 +22,7 @@ class [[className]] : public HttpFilter<[[className]]>
{ {
public: public:
[[className]]() {} [[className]]() {}
void doFilter(const HttpRequestPtr &req, virtual void doFilter(const HttpRequestPtr &req,
FilterCallback &&fcb, FilterCallback &&fcb,
FilterChainCallback &&fccb) override; FilterChainCallback &&fccb) override;
}; };

View File

@ -1,73 +1,20 @@
# 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 # Prerequisites
*.d *.d
# Object files # Compiled Object files
*.slo
*.lo
*.o *.o
*.ko
*.obj *.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers # Precompiled Headers
*.gch *.gch
*.pch *.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 # Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files # Fortran module files
*.mod *.mod
@ -75,487 +22,15 @@ dkms.conf
# Compiled Static libraries # Compiled Static libraries
*.lai *.lai
*.la
*.a
*.lib
# Executables # Executables
*.exe
*.out
*.app
### CMake ### build
CMakeLists.txt.user cmake-build-debug
CMakeCache.txt .idea
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++

File diff suppressed because it is too large Load Diff

View File

@ -14,27 +14,23 @@ using namespace drogon_ctl;
#include <drogon/orm/Field.h> #include <drogon/orm/Field.h>
#include <drogon/orm/SqlBinder.h> #include <drogon/orm/SqlBinder.h>
#include <drogon/orm/Mapper.h> #include <drogon/orm/Mapper.h>
#include <drogon/orm/BaseBuilder.h>
#ifdef __cpp_impl_coroutine
#include <drogon/orm/CoroMapper.h>
#endif
#include <trantor/utils/Date.h> #include <trantor/utils/Date.h>
#include <trantor/utils/Logger.h> #include <trantor/utils/Logger.h>
#include <json/json.h> #include <json/json.h>
#include <string> #include <string>
#include <string_view>
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <tuple> #include <tuple>
#include <stdint.h> #include <stdint.h>
#include <iostream> #include <iostream>
using namespace drogon::orm;
namespace drogon namespace drogon
{ {
namespace orm namespace orm
{ {
class DbClient; class DbClient;
using DbClientPtr = std::shared_ptr<DbClient>; typedef std::shared_ptr<DbClient> DbClientPtr;
} }
} }
namespace drogon_model namespace drogon_model
@ -48,28 +44,23 @@ auto &schema=@@.get<std::string>("schema");
$$<<"namespace "<<schema<<"\n"; $$<<"namespace "<<schema<<"\n";
$$<<"{\n"; $$<<"{\n";
} }
std::vector<std::string> relationshipClassNames;
auto &relationships=@@.get<std::vector<Relationship>>("relationships"); auto &relationships=@@.get<std::vector<Relationship>>("relationships");
for(auto &relationship : relationships) for(auto &relationship : relationships)
{ {
auto &name=relationship.targetTableName(); auto &name=relationship.targetTableName();
auto relationshipClassName=nameTransform(name, true); auto relationshipClassName=nameTransform(name, true);
relationshipClassNames.push_back(relationshipClassName); %>
class {%relationshipClassName%};
<%c++
if(relationship.type() == Relationship::Type::ManyToMany) if(relationship.type() == Relationship::Type::ManyToMany)
{ {
auto &pivotTableName=relationship.pivotTable().tableName(); auto &pivotTableName=relationship.pivotTable().tableName();
auto pivotTableClassName=nameTransform(pivotTableName, true); auto pivotTableClassName=nameTransform(pivotTableName, true);
relationshipClassNames.push_back(pivotTableClassName);
}
}
std::sort(relationshipClassNames.begin(), relationshipClassNames.end());
relationshipClassNames.erase(std::unique(relationshipClassNames.begin(), relationshipClassNames.end()), relationshipClassNames.end());
for(std::string &relationshipClassName : relationshipClassNames)
{
%> %>
class {%relationshipClassName%}; class {%pivotTableClassName%};
<%c++ <%c++
} }
}
%> %>
class [[className]] class [[className]]
@ -81,21 +72,21 @@ class [[className]]
auto cols=@@.get<std::vector<ColumnInfo>>("columns"); auto cols=@@.get<std::vector<ColumnInfo>>("columns");
for(size_t i=0;i<cols.size();i++) for(size_t i=0;i<cols.size();i++)
{ {
$$<<" static const std::string _"<<cols[i].colName_<<";\n"; $$<<" static const std::string _"<<cols[i]._colName<<";\n";
} }
%> %>
}; };
static const int primaryKeyNumber; const static int primaryKeyNumber;
static const std::string tableName; const static std::string tableName;
static const bool hasPrimaryKey; const static bool hasPrimaryKey;
<%c++if(@@.get<int>("hasPrimaryKey")<=1){%> <%c++if(@@.get<int>("hasPrimaryKey")<=1){%>
static const std::string primaryKeyName; const static std::string primaryKeyName;
<%c++if(!@@.get<std::string>("primaryKeyType").empty()){%> <%c++if(!@@.get<std::string>("primaryKeyType").empty()){%>
using PrimaryKeyType = [[primaryKeyType]]; typedef [[primaryKeyType]] PrimaryKeyType;
const PrimaryKeyType &getPrimaryKey() const; const PrimaryKeyType &getPrimaryKey() const;
<%c++}else{%> <%c++}else{%>
using PrimaryKeyType = void; typedef void PrimaryKeyType;
int getPrimaryKey() const { assert(false); return 0; } int getPrimaryKey() const { assert(false); return 0; }
<%c++}%> <%c++}%>
<%c++}else{ <%c++}else{
@ -108,8 +99,8 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
typelist += ","; typelist += ",";
} }
%> %>
static const std::vector<std::string> primaryKeyName; const static std::vector<std::string> primaryKeyName;
using PrimaryKeyType = std::tuple<{%typelist%}>;//<%c++ typedef std::tuple<{%typelist%}> PrimaryKeyType;//<%c++
auto pkName=@@.get<std::vector<std::string>>("primaryKeyName"); auto pkName=@@.get<std::vector<std::string>>("primaryKeyName");
for(size_t i=0;i<pkName.size();i++) for(size_t i=0;i<pkName.size();i++)
{ {
@ -130,7 +121,7 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
* @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. * 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 * @brief constructor
@ -167,35 +158,36 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
<%c++ <%c++
for(const auto &col:cols) for(const auto &col:cols)
{ {
$$<<" /** For column "<<col.colName_<<" */\n"; $$<<" /** For column "<<col._colName<<" */\n";
if(!col.colType_.empty()) if(!col._colType.empty())
{ {
$$<<" ///Get the value of the column "<<col.colName_<<", returns the default value if the column is null\n"; $$<<" ///Get the value of the column "<<col._colName<<", returns the default value if the column is null\n";
$$<<" const "<<col.colType_<<" &getValueOf"<<col.colTypeName_<<"() const noexcept;\n"; $$<<" const "<<col._colType<<" &getValueOf"<<col._colTypeName<<"() const noexcept;\n";
if(col.colType_=="std::vector<char>") if(col._colType=="std::vector<char>")
{ {
$$<<" ///Return the column value by std::string with binary data\n"; $$<<" ///Return the column value by std::string with binary data\n";
$$<<" std::string getValueOf"<<col.colTypeName_<<"AsString() const noexcept;\n"; $$<<" std::string getValueOf"<<col._colTypeName<<"AsString() const noexcept;\n";
} }
$$<<" ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null\n"; $$<<" ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null\n";
$$<<" const std::shared_ptr<"<<col.colType_<<"> &get"<<col.colTypeName_<<"() const noexcept;\n"; $$<<" const std::shared_ptr<"<<col._colType<<"> &get"<<col._colTypeName<<"() const noexcept;\n";
if(!col._isAutoVal)
$$<<" ///Set the value of the column "<<col.colName_<<"\n";
$$<<" void set"<<col.colTypeName_<<"(const "<<col.colType_<<" &p"<<col.colTypeName_<<") noexcept;\n";
if(col.colType_=="std::string")
$$<<" void set"<<col.colTypeName_<<"("<<col.colType_<<" &&p"<<col.colTypeName_<<") noexcept;\n";
if(col.colType_=="std::vector<char>")
{ {
$$<<" void set"<<col.colTypeName_<<"(const std::string &p"<<col.colTypeName_<<") noexcept;\n"; $$<<" ///Set the value of the column "<<col._colName<<"\n";
} $$<<" void set"<<col._colTypeName<<"(const "<<col._colType<<" &p"<<col._colTypeName<<") noexcept;\n";
if(!col.notNull_) if(col._colType=="std::string")
$$<<" void set"<<col._colTypeName<<"("<<col._colType<<" &&p"<<col._colTypeName<<") noexcept;\n";
if(col._colType=="std::vector<char>")
{ {
$$<<" void set"<<col.colTypeName_<<"ToNull() noexcept;\n"; $$<<" void set"<<col._colTypeName<<"(const std::string &p"<<col._colTypeName<<") noexcept;\n";
}
if(!col._notNull)
{
$$<<" void set"<<col._colTypeName<<"ToNull() noexcept;\n";
}
} }
} }
else else
$$<<" //FIXME!!"<<" getValueOf"<<col.colTypeName_<<"() const noexcept;\n"; $$<<" //FIXME!!"<<" getValueOf"<<col._colTypeName<<"() const noexcept;\n";
$$<<"\n"; $$<<"\n";
} }
%> %>
@ -228,20 +220,18 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
} }
std::string alind(alias.length(), ' '); std::string alind(alias.length(), ' ');
%> %>
{%relationshipClassName%} get{%alias%}(const drogon::orm::DbClientPtr &clientPtr) const; void get{%alias%}(const DbClientPtr &clientPtr,
void get{%alias%}(const drogon::orm::DbClientPtr &clientPtr,
{%alind%} const std::function<void({%relationshipClassName%})> &rcb, {%alind%} const std::function<void({%relationshipClassName%})> &rcb,
{%alind%} const drogon::orm::ExceptionCallback &ecb) const; {%alind%} const ExceptionCallback &ecb) const;
<%c++ <%c++
} }
else else
{ {
std::string relationshipClassInde(relationshipClassName.length(), ' '); std::string relationshipClassInde(relationshipClassName.length(), ' ');
%> %>
{%relationshipClassName%} get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr) const; void get{%relationshipClassName%}(const DbClientPtr &clientPtr,
void get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr,
{%relationshipClassInde%} const std::function<void({%relationshipClassName%})> &rcb, {%relationshipClassInde%} const std::function<void({%relationshipClassName%})> &rcb,
{%relationshipClassInde%} const drogon::orm::ExceptionCallback &ecb) const; {%relationshipClassInde%} const ExceptionCallback &ecb) const;
<%c++ <%c++
} }
} }
@ -255,20 +245,18 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
} }
std::string alind(alias.length(), ' '); std::string alind(alias.length(), ' ');
%> %>
std::vector<{%relationshipClassName%}> get{%alias%}(const drogon::orm::DbClientPtr &clientPtr) const; void get{%alias%}(const DbClientPtr &clientPtr,
void get{%alias%}(const drogon::orm::DbClientPtr &clientPtr,
{%alind%} const std::function<void(std::vector<{%relationshipClassName%}>)> &rcb, {%alind%} const std::function<void(std::vector<{%relationshipClassName%}>)> &rcb,
{%alind%} const drogon::orm::ExceptionCallback &ecb) const; {%alind%} const ExceptionCallback &ecb) const;
<%c++ <%c++
} }
else else
{ {
std::string relationshipClassInde(relationshipClassName.length(), ' '); std::string relationshipClassInde(relationshipClassName.length(), ' ');
%> %>
std::vector<{%relationshipClassName%}> get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr) const; void get{%relationshipClassName%}(const DbClientPtr &clientPtr,
void get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr,
{%relationshipClassInde%} const std::function<void(std::vector<{%relationshipClassName%}>)> &rcb, {%relationshipClassInde%} const std::function<void(std::vector<{%relationshipClassName%}>)> &rcb,
{%relationshipClassInde%} const drogon::orm::ExceptionCallback &ecb) const; {%relationshipClassInde%} const ExceptionCallback &ecb) const;
<%c++ <%c++
} }
} }
@ -284,34 +272,25 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
} }
std::string alind(alias.length(), ' '); std::string alind(alias.length(), ' ');
%> %>
std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>> get{%alias%}(const drogon::orm::DbClientPtr &clientPtr) const; void get{%alias%}(const DbClientPtr &clientPtr,
void get{%alias%}(const drogon::orm::DbClientPtr &clientPtr,
{%alind%} const std::function<void(std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>)> &rcb, {%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++ <%c++
} }
else else
{ {
std::string relationshipClassInde(relationshipClassName.length(), ' '); std::string relationshipClassInde(relationshipClassName.length(), ' ');
%> %>
std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>> get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr) const; void get{%relationshipClassName%}(const DbClientPtr &clientPtr,
void get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr,
{%relationshipClassInde%} const std::function<void(std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>)> &rcb, {%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++ <%c++
} }
} }
} }
%> %>
private: private:
friend drogon::orm::Mapper<[[className]]>; friend 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>;
#ifdef __cpp_impl_coroutine
friend drogon::orm::CoroMapper<[[className]]>;
#endif
static const std::vector<std::string> &insertColumns() noexcept; static const std::vector<std::string> &insertColumns() noexcept;
void outputArgs(drogon::orm::internal::SqlBinder &binder) const; void outputArgs(drogon::orm::internal::SqlBinder &binder) const;
const std::vector<std::string> updateColumns() const; const std::vector<std::string> updateColumns() const;
@ -321,22 +300,22 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
<%c++ <%c++
for(auto col:cols) for(auto col:cols)
{ {
if(!col.colType_.empty()) if(!col._colType.empty())
$$<<" std::shared_ptr<"<<col.colType_<<"> "<<col.colValName_<<"_;\n"; $$<<" std::shared_ptr<"<<col._colType<<"> _"<<col._colValName<<";\n";
} }
%> %>
struct MetaData struct MetaData
{ {
const std::string colName_; const std::string _colName;
const std::string colType_; const std::string _colType;
const std::string colDatabaseType_; const std::string _colDatabaseType;
const ssize_t colLength_; const ssize_t _colLength;
const bool isAutoVal_; const bool _isAutoVal;
const bool isPrimaryKey_; const bool _isPrimaryKey;
const bool notNull_; const bool _notNull;
}; };
static const std::vector<MetaData> metaData_; static const std::vector<MetaData> _metaData;
bool dirtyFlag_[{%cols.size()%}]={ false }; bool _dirtyFlag[{%cols.size()%}]={ false };
public: public:
static const std::string &sqlForFindingByPrimaryKey() static const std::string &sqlForFindingByPrimaryKey()
{ {
@ -411,43 +390,26 @@ if(@@.get<int>("hasPrimaryKey")<=1){
bool selFlag=false; bool selFlag=false;
for(size_t i=0;i<cols.size();i++) for(size_t i=0;i<cols.size();i++)
{ {
if(cols[i].isAutoVal_) if(cols[i]._isAutoVal)
{ {
if(@@.get<std::string>("rdbms")=="sqlite3")
{
continue;
}
if(@@.get<int>("hasPrimaryKey")>0) if(@@.get<int>("hasPrimaryKey")>0)
{ {
selFlag = true; selFlag = true;
} }
$$<<" sql += \""<<cols[i].colName_<<",\";\n"; $$<<" sql += \""<<cols[i]._colName<<",\";\n";
$$<<" ++parametersCount;\n"; $$<<" ++parametersCount;\n";
continue; continue;
} }
if(cols[i].colType_.empty()) if(cols[i]._colType.empty())
continue; continue;
if(cols[i].hasDefaultVal_) if(cols[i]._hasDefaultVal)
{ {
if(@@.get<std::string>("rdbms")!="sqlite3") $$<<" sql += \""<<cols[i]._colName<<",\";\n";
{
$$<<" sql += \""<<cols[i].colName_<<",\";\n";
$$<<" ++parametersCount;\n"; $$<<" ++parametersCount;\n";
}
else
{
%>
if(dirtyFlag_[{%i%}])
{
sql += "{%cols[i].colName_%},";
++parametersCount;
}
<%c++
}
if(@@.get<int>("hasPrimaryKey")>0||@@.get<std::string>("rdbms")=="postgresql") if(@@.get<int>("hasPrimaryKey")>0||@@.get<std::string>("rdbms")=="postgresql")
{ {
%> %>
if(!dirtyFlag_[{%i%}]) if(!_dirtyFlag[{%i%}])
{ {
needSelection=true; needSelection=true;
} }
@ -457,9 +419,9 @@ if(@@.get<int>("hasPrimaryKey")<=1){
else else
{ {
%> %>
if(dirtyFlag_[{%i%}]) if(_dirtyFlag[{%i%}])
{ {
sql += "{%cols[i].colName_%},"; sql += "{%cols[i]._colName%},";
++parametersCount; ++parametersCount;
} }
<%c++ <%c++
@ -489,26 +451,23 @@ if(@@.get<std::string>("rdbms")=="postgresql")
} }
for(size_t i=0;i<cols.size();i++) for(size_t i=0;i<cols.size();i++)
{ {
if(cols[i].isAutoVal_) if(cols[i]._isAutoVal)
{
if(@@.get<std::string>("rdbms")!="sqlite3")
{ {
%> %>
sql +="default,"; sql +="default,";
<%c++ <%c++
}
continue; continue;
} }
if(cols[i].colType_.empty()) if(cols[i]._colType.empty())
continue; continue;
%> %>
if(dirtyFlag_[{%i%}]) if(_dirtyFlag[{%i%}])
{ {
<%c++ <%c++
if(@@.get<std::string>("rdbms")=="postgresql") if(@@.get<std::string>("rdbms")=="postgresql")
{ {
%> %>
n = snprintf(placeholderStr,sizeof(placeholderStr),"$%d,",placeholder++); n = sprintf(placeholderStr,"$%d,",placeholder++);
sql.append(placeholderStr, n); sql.append(placeholderStr, n);
<%c++ <%c++
}else }else
@ -521,7 +480,7 @@ if(@@.get<std::string>("rdbms")=="postgresql")
%> %>
} }
<%c++ <%c++
if(cols[i].hasDefaultVal_&&@@.get<std::string>("rdbms")!="sqlite3") if(cols[i]._hasDefaultVal)
{ {
%> %>
else else

View File

@ -13,31 +13,10 @@
"schema": "public", "schema": "public",
//user: User name //user: User name
"user": "", "user": "",
//password or passwd: Password //password: Password
"password": "", "passwd": "",
//client_encoding: The character set used by drogon_ctl. it is empty string by default which
//means use the default character set.
//"client_encoding": "",
//table: An array of tables to be modelized. if the array is empty, all revealed tables are modelized. //table: An array of tables to be modelized. if the array is empty, all revealed tables are modelized.
"tables": [], "tables": [],
//convert: the value can be changed by a function call before it is stored into database or
//after it is read from database
"convert": {
"enabled": false,
"items":[{
"table": "user",
"column": "password",
"method": {
//after_db_read: name of the method which is called after reading from database, signature: void([const] std::shared_ptr [&])
"after_db_read": "decrypt_password",
//before_db_write: name of the method which is called before writing to database, signature: void([const] std::shared_ptr [&])
"before_db_write": "encrypt_password"
},
"includes": [
"\"file_local_search_path.h\"","<file_in_global_search_path.h>"
]
}]
},
"relationships": { "relationships": {
"enabled": false, "enabled": false,
"items": [{ "items": [{

View File

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

View File

@ -451,17 +451,17 @@ void [[className]]Base::update(const HttpRequestPtr &req,
<%c++ <%c++
tableInfo = @@.get<DrTemplateData>("tableInfo"); tableInfo = @@.get<DrTemplateData>("tableInfo");
const auto &cols=tableInfo.get<std::vector<ColumnInfo>>("columns"); const auto &cols=tableInfo.get<std::vector<ColumnInfo>>("columns");
for(size_t i=0; i<cols.size(); ++i) for(size_t i=0; i<cols.size(); i++)
{ {
auto &col = cols[i]; auto &col = cols[i];
if(i < (cols.size()-1)) if(i < (cols.size()-1))
{ {
%> %>
"{%col.colName_%}", "{%col._colName%}",
<%c++ <%c++
}else{ }else{
%> %>
"{%col.colName_%}" "{%col._colName%}"
<%c++ <%c++
} }
} }
@ -475,17 +475,17 @@ for(size_t i=0; i<cols.size(); ++i)
*/ */
enableMasquerading({ enableMasquerading({
<%c++ <%c++
for(size_t i=0; i<cols.size(); ++i) for(size_t i=0; i<cols.size(); i++)
{ {
auto &col = cols[i]; auto &col = cols[i];
if(i < (cols.size()-1)) if(i < (cols.size()-1))
{ {
%> %>
"{%col.colName_%}", // the alias for the {%col.colName_%} column. "{%col._colName%}", // the alias for the {%col._colName%} column.
<%c++ <%c++
}else{ }else{
%> %>
"{%col.colName_%}" // the alias for the {%col.colName_%} column. "{%col._colName%}" // the alias for the {%col._colName%} column.
<%c++ <%c++
} }
} }

View File

@ -19,7 +19,6 @@ auto modelName = tableInfo.get<std::string>("className");
$$<<"#include \""<<modelName<<".h\"\n"; $$<<"#include \""<<modelName<<".h\"\n";
bool hasPrimaryKey = (tableInfo.get<int>("hasPrimaryKey")==1); bool hasPrimaryKey = (tableInfo.get<int>("hasPrimaryKey")==1);
$$<<"using namespace drogon;\n"; $$<<"using namespace drogon;\n";
$$<<"using namespace drogon::orm;\n";
$$<<"using namespace drogon_model::"<<tableInfo.get<std::string>("dbName"); $$<<"using namespace drogon_model::"<<tableInfo.get<std::string>("dbName");
auto &schema=tableInfo.get<std::string>("schema"); auto &schema=tableInfo.get<std::string>("schema");
@ -72,13 +71,13 @@ class [[className]]Base : public RestfulController
orm::DbClientPtr getDbClient() orm::DbClientPtr getDbClient()
{ {
return drogon::app().get{%(@@.get<bool>("isFastDbClient")?"Fast":"")%}DbClient(dbClientName_); return drogon::app().get{%(@@.get<bool>("isFastDbClient")?"Fast":"")%}DbClient(_dbClientName);
} }
protected: protected:
/// Ensure that subclasses inherited from this class are instantiated. /// Ensure that subclasses inherited from this class are instantiated.
[[className]]Base(); [[className]]Base();
const std::string dbClientName_{"[[dbClientName]]"}; const std::string _dbClientName = "[[dbClientName]]";
}; };
<%c++ for(size_t i=0;i<namespaceVector.size();++i) <%c++ for(size_t i=0;i<namespaceVector.size();++i)
{ {

View File

@ -1,14 +0,0 @@
cmake_minimum_required(VERSION 3.5)
project([[ProjectName]]_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

@ -15,54 +15,23 @@
#include "version.h" #include "version.h"
#include <drogon/config.h> #include <drogon/config.h>
#include <drogon/version.h> #include <drogon/version.h>
#include <drogon/utils/Utilities.h>
#include <trantor/net/Resolver.h>
#include <trantor/utils/Utilities.h>
#include <iostream> #include <iostream>
using namespace drogon_ctl; using namespace drogon_ctl;
static const char banner[] = static const char banner[] =
R"( _ " _ \n"
__| |_ __ ___ __ _ ___ _ __ " __| |_ __ ___ __ _ ___ _ __ \n"
/ _` | '__/ _ \ / _` |/ _ \| '_ \ " / _` | '__/ _ \\ / _` |/ _ \\| '_ \\ \n"
| (_| | | | (_) | (_| | (_) | | | | "| (_| | | | (_) | (_| | (_) | | | |\n"
\__,_|_| \___/ \__, |\___/|_| |_| " \\__,_|_| \\___/ \\__, |\\___/|_| |_|\n"
|___/ " |___/ \n";
)";
void version::handleCommand(std::vector<std::string> &parameters) 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 << banner << std::endl;
std::cout << "A utility for drogon" << std::endl; std::cout << "A utility for drogon" << std::endl;
std::cout << "Version: " << DROGON_VERSION << std::endl; std::cout << "Version:" << VERSION << std::endl;
std::cout << "Git commit: " << DROGON_VERSION_SHA1 << std::endl; std::cout << "Git commit:" << VERSION_MD5 << std::endl;
std::cout << "Compilation: \n Compiler: " << COMPILER_COMMAND std::cout << "Compile config:" << COMPILATION_FLAGS << " " << INCLUDING_DIRS
<< "\n Compiler ID: " << COMPILER_ID << std::endl;
<< "\n Compilation flags: " << COMPILATION_FLAGS
<< INCLUDING_DIRS << std::endl;
std::cout << "Libraries: \n postgresql: "
<< (USE_POSTGRESQL ? "yes" : "no") << " (pipeline 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 USE_BROTLI
std::cout << " brotli: yes\n";
#else
std::cout << " brotli: no\n";
#endif
#ifdef USE_REDIS
std::cout << " hiredis: yes\n";
#else
std::cout << " hiredis: no\n";
#endif
std::cout << " c-ares: "
<< (trantor::Resolver::isCAresUsed() ? "yes\n" : "no\n");
#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 <drogon/DrObject.h>
#include "CommandHandler.h" #include "CommandHandler.h"
using namespace drogon; using namespace drogon;
namespace drogon_ctl namespace drogon_ctl
{ {
class version : public DrObject<version>, public CommandHandler class version : public DrObject<version>, public CommandHandler
{ {
public: public:
void handleCommand(std::vector<std::string> &parameters) override; virtual void handleCommand(std::vector<std::string> &parameters) override;
virtual std::string script() override
std::string script() override
{ {
return "display version of this tool"; return "display version of this tool";
} }
virtual bool isTopCommand() override
bool isTopCommand() override
{ {
return true; return true;
} }
version() version()
{ {
} }

View File

@ -1,62 +1,84 @@
link_libraries(${PROJECT_NAME}) link_libraries(${PROJECT_NAME})
file(GLOB SCP_LIST ${CMAKE_CURRENT_SOURCE_DIR}/simple_example/*.csp)
foreach(cspFile ${SCP_LIST})
message(STATUS "cspFile:" ${cspFile})
exec_program(basename
ARGS
"${cspFile} .csp"
OUTPUT_VARIABLE
classname)
message(STATUS "view classname:" ${classname})
add_custom_command(OUTPUT ${classname}.h ${classname}.cc
COMMAND drogon_ctl
ARGS
create
view
${cspFile}
DEPENDS ${cspFile}
VERBATIM)
set(VIEWSRC ${VIEWSRC} ${classname}.cc)
endforeach()
set(simple_example_sources
simple_example/CustomCtrl.cc
simple_example/CustomHeaderFilter.cc
simple_example/DoNothingPlugin.cc
simple_example/ForwardCtrl.cc
simple_example/JsonTestController.cc
simple_example/ListParaCtl.cc
simple_example/PipeliningTest.cc
simple_example/TestController.cc
simple_example/TestPlugin.cc
simple_example/TestViewCtl.cc
simple_example/WebSocketTest.cc
simple_example/api_Attachment.cc
simple_example/api_v1_ApiTest.cc
simple_example/TimeFilter.cc
simple_example/main.cc)
add_executable(webapp ${simple_example_sources} ${VIEWSRC})
add_dependencies(webapp drogon_ctl)
set(client_example_sources client_example/main.cc)
set(benchmark_sources benchmark/BenchmarkCtrl.cc benchmark/JsonCtrl.cc set(benchmark_sources benchmark/BenchmarkCtrl.cc benchmark/JsonCtrl.cc
benchmark/main.cc) benchmark/main.cc)
# AUX_SOURCE_DIRECTORY(simple_example_test DIR_TEST)
add_executable(client client_example/main.cc) add_executable(client ${client_example_sources})
add_executable(websocket_client websocket_client/WebSocketClient.cc)
add_executable(websocket_server websocket_server/WebSocketServer.cc)
add_executable(benchmark ${benchmark_sources}) add_executable(benchmark ${benchmark_sources})
add_executable(helloworld helloworld/main.cc add_executable(webapp_test simple_example_test/main.cc)
helloworld/HelloController.cc add_executable(pipelining_test simple_example_test/HttpPipeliningTest.cc)
helloworld/HelloViewController.cc) add_executable(websocket_test simple_example_test/WebSocketTest.cc)
drogon_create_views(helloworld add_executable(multiple_ws_test simple_example_test/MultipleWsTest.cc)
${CMAKE_CURRENT_SOURCE_DIR}/helloworld
${CMAKE_CURRENT_BINARY_DIR})
add_executable(file_upload file_upload/file_upload.cc)
drogon_create_views(file_upload
${CMAKE_CURRENT_SOURCE_DIR}/file_upload
${CMAKE_CURRENT_BINARY_DIR})
add_executable(login_session login_session/main.cc)
drogon_create_views(login_session
${CMAKE_CURRENT_SOURCE_DIR}/login_session
${CMAKE_CURRENT_BINARY_DIR})
add_executable(jsonstore jsonstore/main.cc) add_custom_command(TARGET webapp POST_BUILD
COMMAND gzip
add_executable(redis_simple redis/main.cc ARGS
redis/controllers/Client.cc -c
redis/controllers/WsClient.cc) ${CMAKE_CURRENT_SOURCE_DIR}/simple_example/index.html
>
add_executable(redis_chat redis_chat/main.cc ${CMAKE_CURRENT_BINARY_DIR}/index.html.gz
redis_chat/controllers/Chat.cc) VERBATIM)
add_custom_command(
add_executable(async_stream async_stream/main.cc TARGET webapp POST_BUILD
async_stream/RequestStreamExampleCtrl.cc) COMMAND ${CMAKE_COMMAND}
-E
add_executable(cors cors/main.cc) copy_if_different
${PROJECT_SOURCE_DIR}/config.example.json
${PROJECT_SOURCE_DIR}/drogon.jpg
${CMAKE_CURRENT_SOURCE_DIR}/simple_example/index.html
${PROJECT_SOURCE_DIR}/trantor/trantor/tests/server.pem
$<TARGET_FILE_DIR:webapp>)
set(example_targets set(example_targets
benchmark webapp
webapp_test
client client
websocket_client benchmark
websocket_server pipelining_test
helloworld websocket_test
file_upload multiple_ws_test)
login_session
jsonstore
redis_simple
redis_chat
async_stream
cors)
# 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)
foreach(target ${example_targets})
target_compile_options(${target} PRIVATE -Wall -Wextra -Werror)
endforeach()
endif()
set_property(TARGET ${example_targets} set_property(TARGET ${example_targets}
PROPERTY CXX_STANDARD ${DROGON_CXX_STANDARD}) PROPERTY CXX_STANDARD ${DROGON_CXX_STANDARD})

View File

@ -2,21 +2,10 @@
The following examples can help you understand how to use Drogon: The following examples can help you understand how to use Drogon:
1. [helloworld](https://github.com/drogonframework/drogon/tree/master/examples/helloworld) - The multiple ways of "Hello, World!" 1. [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)
2. [client_example](https://github.com/drogonframework/drogon/tree/master/examples/client_example/main.cc) - A client example 2. [client_example](https://github.com/an-tao/drogon/tree/master/examples/client_example/main.cc) - A client example.
3. [websocket_client](https://github.com/drogonframework/drogon/tree/master/examples/websocket_client/WebSocketClient.cc) - An example on how to use the WebSocket client 3. [simple_example](https://github.com/an-tao/drogon/tree/master/examples/simple_example) - A simple example showing how to create a web application using Drogon.
4. [login_session](https://github.com/drogonframework/drogon/tree/master/examples/login_session) - How to use the built-in session system to handle login and out 4. [simple_example_test](https://github.com/an-tao/drogon/tree/master/examples/simple_example_test) - Some tests for the `simple_example`.
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
### [TechEmpower Framework Benchmarks](https://github.com/TechEmpower/FrameworkBenchmarks) test suite ### [TechEmpower Framework Benchmarks](https://github.com/TechEmpower/FrameworkBenchmarks) test suite
@ -24,4 +13,4 @@ I created a benchmark suite for the `tfb`, see [here](https://github.com/TechEmp
### Another test suite ### Another test suite
I also created a test suite for another web frameworks benchmark repository, see [here](https://github.com/the-benchmarker/web-frameworks/tree/master/cpp/drogon). In this project, Drogon is used as a sub-module (locally include in the project). I also created a test suite for another web frameworks benchmark repository, see [here](https://github.com/the-benchmarker/web-frameworks/tree/master/cpp/drogon), in this project, drogon is used as a sub-module (locally include in the project).

View File

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

View File

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

View File

@ -1,7 +1,6 @@
#include "BenchmarkCtrl.h" #include "BenchmarkCtrl.h"
void BenchmarkCtrl::asyncHandleHttpRequest( void BenchmarkCtrl::asyncHandleHttpRequest(
const HttpRequestPtr &, const HttpRequestPtr &req,
std::function<void(const HttpResponsePtr &)> &&callback) std::function<void(const HttpResponsePtr &)> &&callback)
{ {
// write your application logic here // write your application logic here

View File

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

View File

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

View File

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

View File

@ -1,23 +1,16 @@
#include <drogon/drogon.h> #include <drogon/drogon.h>
using namespace drogon; using namespace drogon;
int main() int main()
{ {
app() app().setLogPath("./");
.setLogPath("./") app().setLogLevel(trantor::Logger::WARN);
.setLogLevel(trantor::Logger::kWarn) app().addListener("0.0.0.0", 7770);
.addListener("0.0.0.0", 7770) app().setThreadNum(16);
.setThreadNum(0) app().enableRunAsDaemon();
.registerSyncAdvice([](const HttpRequestPtr &req) -> HttpResponsePtr { // app().enableRelaunchOnError();
const auto &path = req->path(); // app().loop()->runEvery(1, []() {
if (path.length() == 1 && path[0] == '/') // LOG_WARN << "HAHA";
{ // });
auto response = HttpResponse::newHttpResponse(); app().run();
response->setBody("<p>Hello, world!</p>");
return response;
}
return nullptr;
})
.run();
} }

View File

@ -1,65 +1,28 @@
#include <drogon/drogon.h> #include <drogon/drogon.h>
#include <future>
#include <iostream> #include <iostream>
#include <future>
#ifdef __linux__
#include <sys/socket.h>
#include <netinet/tcp.h>
#endif
using namespace drogon; using namespace drogon;
int nth_resp = 0;
int main() int main()
{ {
trantor::Logger::setLogLevel(trantor::Logger::kTrace); trantor::Logger::setLogLevel(trantor::Logger::TRACE);
{ {
int count = 0;
auto client = HttpClient::newHttpClient("http://www.baidu.com"); 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(); auto req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get); req->setMethod(drogon::Get);
req->setPath("/s"); req->setPath("/s");
req->setParameter("wd", "wx"); req->setParameter("wd", "weixin");
req->setParameter("oq", "wx");
for (int i = 0; i < 10; ++i) for (int i = 0; i < 10; i++)
{ {
client->sendRequest( client->sendRequest(
req, [](ReqResult result, const HttpResponsePtr &response) { req,
if (result != ReqResult::Ok) [&count](ReqResult result, const HttpResponsePtr &response) {
{
std::cout
<< "error while sending request to server! result: "
<< result << std::endl;
return;
}
std::cout << "receive response!" << std::endl; std::cout << "receive response!" << std::endl;
// auto headers=response. // auto headers=response.
++nth_resp; count++;
std::cout << response->getBody() << std::endl; std::cout << response->getBody() << std::endl;
auto cookies = response->cookies(); auto cookies = response->cookies();
for (auto const &cookie : cookies) for (auto const &cookie : cookies)
@ -69,11 +32,9 @@ int main()
<< ":domain=" << cookie.second.domain() << ":domain=" << cookie.second.domain()
<< std::endl; << std::endl;
} }
std::cout << "count=" << nth_resp << std::endl; std::cout << "count=" << count << std::endl;
}); });
} }
std::cout << "requestsBufferSize:" << client->requestsBufferSize()
<< std::endl;
} }
app().run(); app().run();

View File

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

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