mirror of
https://github.com/drogonframework/drogon.git
synced 2025-06-24 00:00:43 -04:00
Compare commits
No commits in common. "master" and "v1.9.7" have entirely different histories.
12
.github/workflows/cmake.yml
vendored
12
.github/workflows/cmake.yml
vendored
@ -17,7 +17,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
windows:
|
windows:
|
||||||
name: windows/msvc - ${{ matrix.link }}
|
name: windows/msvc - ${{ matrix.link }}
|
||||||
runs-on: windows-2022
|
runs-on: windows-2019
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@ -44,6 +44,9 @@ jobs:
|
|||||||
- name: Create Build Environment & Configure Cmake
|
- name: Create Build Environment & Configure Cmake
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: ./build
|
working-directory: ./build
|
||||||
|
# For unknown reasons, we fail to create file in windows ci environment.
|
||||||
|
# So examples, drogon_ctl and integration tests can not be built in windows ci.
|
||||||
|
# We should try to enable them again in the future.
|
||||||
run: |
|
run: |
|
||||||
[[ ${{ matrix.link }} == "SHARED" ]] && shared="ON" || shared="OFF"
|
[[ ${{ matrix.link }} == "SHARED" ]] && shared="ON" || shared="OFF"
|
||||||
cmake .. \
|
cmake .. \
|
||||||
@ -51,8 +54,8 @@ jobs:
|
|||||||
-DBUILD_TESTING=on \
|
-DBUILD_TESTING=on \
|
||||||
-DBUILD_SHARED_LIBS=$shared \
|
-DBUILD_SHARED_LIBS=$shared \
|
||||||
-DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" \
|
-DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" \
|
||||||
-DBUILD_CTL=ON \
|
-DBUILD_CTL=OFF \
|
||||||
-DBUILD_EXAMPLES=ON \
|
-DBUILD_EXAMPLES=OFF \
|
||||||
-DUSE_SPDLOG=ON \
|
-DUSE_SPDLOG=ON \
|
||||||
-DCMAKE_INSTALL_PREFIX=../install \
|
-DCMAKE_INSTALL_PREFIX=../install \
|
||||||
-DCMAKE_POLICY_DEFAULT_CMP0091=NEW \
|
-DCMAKE_POLICY_DEFAULT_CMP0091=NEW \
|
||||||
@ -70,7 +73,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
osver: [13, 14, 15]
|
osver: [12, 13]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Drogon source code
|
- name: Checkout Drogon source code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@ -99,6 +102,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Prepare for testing
|
- name: Prepare for testing
|
||||||
run: |
|
run: |
|
||||||
|
brew tap homebrew/services
|
||||||
brew services restart postgresql@14
|
brew services restart postgresql@14
|
||||||
brew services start mariadb
|
brew services start mariadb
|
||||||
brew services start redis
|
brew services start redis
|
||||||
|
4
.github/workflows/codespell.yml
vendored
4
.github/workflows/codespell.yml
vendored
@ -11,5 +11,5 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- run: sudo apt-get install -y codespell
|
- run: pip install --user codespell[toml]
|
||||||
- run: codespell --ignore-words-list="coo,folx,ot,statics,xwindows,NotIn,aNULL," --skip="*.csp"
|
- run: codespell --ignore-words-list="coo,folx,ot,statics,xwindows,NotIn," --skip="*.csp"
|
||||||
|
2
.github/workflows/cpp.yml
vendored
2
.github/workflows/cpp.yml
vendored
@ -30,7 +30,7 @@ jobs:
|
|||||||
CLANG_FORMAT: clang-format-17
|
CLANG_FORMAT: clang-format-17
|
||||||
|
|
||||||
cpplint:
|
cpplint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
28
.github/workflows/docker-publish.yml
vendored
28
.github/workflows/docker-publish.yml
vendored
@ -1,28 +0,0 @@
|
|||||||
name: Build and Push Docker Image
|
|
||||||
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [created] # 当新版本被创建时触发
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Check out code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Log in to Docker Hub
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
|
|
||||||
- name: Build Docker image
|
|
||||||
run: |
|
|
||||||
cd docker/ubuntu
|
|
||||||
docker build -t drogonframework/drogon:latest .
|
|
||||||
|
|
||||||
- name: Push Docker image
|
|
||||||
run: |
|
|
||||||
docker push drogonframework/drogon:latest
|
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -35,6 +35,7 @@ build/
|
|||||||
cmake-build-debug/
|
cmake-build-debug/
|
||||||
cmake-build-debug-visual-studio/
|
cmake-build-debug-visual-studio/
|
||||||
.idea/
|
.idea/
|
||||||
|
lib/inc/drogon/version.h
|
||||||
html/
|
html/
|
||||||
latex/
|
latex/
|
||||||
.vscode
|
.vscode
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.5...3.31)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
project(drogon)
|
project(drogon)
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ CMAKE_DEPENDENT_OPTION(USE_SPDLOG "Allow using the spdlog logging library" OFF "
|
|||||||
|
|
||||||
set(DROGON_MAJOR_VERSION 1)
|
set(DROGON_MAJOR_VERSION 1)
|
||||||
set(DROGON_MINOR_VERSION 9)
|
set(DROGON_MINOR_VERSION 9)
|
||||||
set(DROGON_PATCH_VERSION 11)
|
set(DROGON_PATCH_VERSION 7)
|
||||||
set(DROGON_VERSION
|
set(DROGON_VERSION
|
||||||
${DROGON_MAJOR_VERSION}.${DROGON_MINOR_VERSION}.${DROGON_PATCH_VERSION})
|
${DROGON_MAJOR_VERSION}.${DROGON_MINOR_VERSION}.${DROGON_PATCH_VERSION})
|
||||||
set(DROGON_VERSION_STRING "${DROGON_VERSION}")
|
set(DROGON_VERSION_STRING "${DROGON_VERSION}")
|
||||||
@ -42,7 +42,7 @@ set(INSTALL_DROGON_CMAKE_DIR ${DEF_INSTALL_DROGON_CMAKE_DIR}
|
|||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||||
# Force MSVC to use UTF-8 because that's what we use. Otherwise it uses
|
# Force MSVC to use UTF-8 because that's what we use. Otherwise it uses
|
||||||
# the default of whatever Windows sets and causes encoding issues.
|
# the default of whatever Windows sets and causes encoding issues.
|
||||||
message(STATUS "You are using MSVC. Forcing to use UTF-8")
|
message(STATUS "You are using MSVC. Forceing to use UTF-8")
|
||||||
add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
|
add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
|
||||||
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
|
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
|
||||||
if (MSVC_VERSION GREATER_EQUAL 1914)
|
if (MSVC_VERSION GREATER_EQUAL 1914)
|
||||||
@ -121,7 +121,6 @@ endif()
|
|||||||
target_include_directories(
|
target_include_directories(
|
||||||
${PROJECT_NAME}
|
${PROJECT_NAME}
|
||||||
PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/lib/inc>
|
PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/lib/inc>
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/lib/inc>
|
|
||||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/orm_lib/inc>
|
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/orm_lib/inc>
|
||||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/nosql_lib/redis/inc>
|
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/nosql_lib/redis/inc>
|
||||||
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>
|
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>
|
||||||
@ -343,21 +342,21 @@ set(private_headers
|
|||||||
lib/src/ConfigAdapter.h
|
lib/src/ConfigAdapter.h
|
||||||
lib/src/MultipartStreamParser.h)
|
lib/src/MultipartStreamParser.h)
|
||||||
|
|
||||||
if (NOT WIN32 AND NOT CMAKE_SYSTEM_NAME STREQUAL "iOS")
|
if (NOT WIN32)
|
||||||
set(DROGON_SOURCES
|
set(DROGON_SOURCES
|
||||||
${DROGON_SOURCES}
|
${DROGON_SOURCES}
|
||||||
lib/src/SharedLibManager.cc)
|
lib/src/SharedLibManager.cc)
|
||||||
set(private_headers
|
set(private_headers
|
||||||
${private_headers}
|
${private_headers}
|
||||||
lib/src/SharedLibManager.h)
|
lib/src/SharedLibManager.h)
|
||||||
elseif(WIN32)
|
else (NOT WIN32)
|
||||||
set(DROGON_SOURCES
|
set(DROGON_SOURCES
|
||||||
${DROGON_SOURCES}
|
${DROGON_SOURCES}
|
||||||
third_party/mman-win32/mman.c)
|
third_party/mman-win32/mman.c)
|
||||||
set(private_headers
|
set(private_headers
|
||||||
${private_headers}
|
${private_headers}
|
||||||
third_party/mman-win32/mman.h)
|
third_party/mman-win32/mman.h)
|
||||||
endif()
|
endif (NOT WIN32)
|
||||||
|
|
||||||
if (BUILD_POSTGRESQL)
|
if (BUILD_POSTGRESQL)
|
||||||
# find postgres
|
# find postgres
|
||||||
@ -511,7 +510,7 @@ execute_process(COMMAND "git" rev-parse HEAD
|
|||||||
OUTPUT_VARIABLE GIT_SHA1
|
OUTPUT_VARIABLE GIT_SHA1
|
||||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
configure_file("${PROJECT_SOURCE_DIR}/cmake/templates/version.h.in"
|
configure_file("${PROJECT_SOURCE_DIR}/cmake/templates/version.h.in"
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/lib/inc/drogon/version.h" @ONLY)
|
"${PROJECT_SOURCE_DIR}/lib/inc/drogon/version.h" @ONLY)
|
||||||
|
|
||||||
if (DROGON_CXX_STANDARD EQUAL 20)
|
if (DROGON_CXX_STANDARD EQUAL 20)
|
||||||
option(USE_COROUTINE "Enable C++20 coroutine support" ON)
|
option(USE_COROUTINE "Enable C++20 coroutine support" ON)
|
||||||
@ -584,7 +583,7 @@ set(DROGON_HEADERS
|
|||||||
lib/inc/drogon/WebSocketConnection.h
|
lib/inc/drogon/WebSocketConnection.h
|
||||||
lib/inc/drogon/WebSocketController.h
|
lib/inc/drogon/WebSocketController.h
|
||||||
lib/inc/drogon/drogon.h
|
lib/inc/drogon/drogon.h
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/lib/inc/drogon/version.h
|
lib/inc/drogon/version.h
|
||||||
lib/inc/drogon/drogon_callbacks.h
|
lib/inc/drogon/drogon_callbacks.h
|
||||||
lib/inc/drogon/PubSubService.h
|
lib/inc/drogon/PubSubService.h
|
||||||
lib/inc/drogon/drogon_test.h
|
lib/inc/drogon/drogon_test.h
|
||||||
|
@ -10,9 +10,8 @@ filter=-runtime/references
|
|||||||
# CHECK macros are from Drogon, not Google Test.
|
# CHECK macros are from Drogon, not Google Test.
|
||||||
filter=-readability/check
|
filter=-readability/check
|
||||||
|
|
||||||
# Don't warn about the use of C++11 or C++17 features.
|
# Don't warn about the use of C++11 features.
|
||||||
filter=-build/c++11
|
filter=-build/c++11
|
||||||
filter=-build/c++17
|
|
||||||
|
|
||||||
filter=-build/include_subdir
|
filter=-build/include_subdir
|
||||||
|
|
||||||
|
131
ChangeLog.md
131
ChangeLog.md
@ -4,123 +4,6 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
## [1.9.11] - 2025-06-20
|
|
||||||
|
|
||||||
### API changes list
|
|
||||||
|
|
||||||
- Add a new overload for execSqlCoro.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Do not write to source directory during build.
|
|
||||||
|
|
||||||
- Improve Postgres connection stability.
|
|
||||||
|
|
||||||
- Add handleFatalError in handleClosed.
|
|
||||||
|
|
||||||
- Add -o|--output option to drogon_ctl create models.
|
|
||||||
|
|
||||||
- Add qrcode for WeChat official account to the README file.
|
|
||||||
|
|
||||||
- Support for iOS compiling.
|
|
||||||
|
|
||||||
- Add cors example to demonstrate cross-origin support in drogon.
|
|
||||||
|
|
||||||
- Add support for continuation frame in WebSocketMessageParser.
|
|
||||||
|
|
||||||
- Add RawParameter API to pass raw SQL parameters.
|
|
||||||
|
|
||||||
- Upgrade Windows image and re-enable tests on Windows.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fix a bug in isAutoCreationClass<T>.
|
|
||||||
|
|
||||||
- Fix CI on MacOS.
|
|
||||||
|
|
||||||
- Fix issue with precision loss of double-type parameters in ORM inputs.
|
|
||||||
|
|
||||||
|
|
||||||
## [1.9.10] - 2025-02-20
|
|
||||||
|
|
||||||
### API changes list
|
|
||||||
|
|
||||||
- Add setConnectionCallback.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- ORM:Avoid unnecessary copies when returning search results.
|
|
||||||
|
|
||||||
- Improve the zh-TW README translation.
|
|
||||||
|
|
||||||
- Make quit function thread safe.
|
|
||||||
|
|
||||||
- Added path_exempt in AccessLogger plugin config to exclude desired paths.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fix the issue in view generation by including the missing header file.
|
|
||||||
|
|
||||||
- Fix ci: codespell.
|
|
||||||
|
|
||||||
## [1.9.9] - 2025-01-01
|
|
||||||
|
|
||||||
### API changes list
|
|
||||||
|
|
||||||
- Added Partitioned flag for cookies.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Update FindFilesystem.cmake to check for GNU instead of GCC for CMAKE_CXX_COMPILER_ID.
|
|
||||||
|
|
||||||
- Update README.
|
|
||||||
|
|
||||||
- Chore(workflow/cmake.yml): upgrade macos runner.
|
|
||||||
|
|
||||||
- Add emptiness check to the LogStream &operator<< with std::string_view.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fix a bug in plugin Redirector.
|
|
||||||
|
|
||||||
- Fix CMAKE issues mentioned in #2144 and a linking problem which manifest with gcc12.3 when building with shared libs.
|
|
||||||
|
|
||||||
- Fix: Remove dependency on locales being installed on the system.
|
|
||||||
|
|
||||||
## [1.9.8] - 2024-10-27
|
|
||||||
|
|
||||||
### API changes list
|
|
||||||
|
|
||||||
- Add in-place base64 encode and decode.
|
|
||||||
|
|
||||||
- Add check the client connection status.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Add Hodor whitelists.
|
|
||||||
|
|
||||||
- Include exception header for std::exception_ptr.
|
|
||||||
|
|
||||||
- Add support for escaped identifiers in Postgresql.
|
|
||||||
|
|
||||||
- Remove content-length header from 101 Switching Protocols response.
|
|
||||||
|
|
||||||
- Remove websocketResponseTest from windows shared library env.
|
|
||||||
|
|
||||||
- Optimize query params and allow for empty values.
|
|
||||||
|
|
||||||
- Replace rejection sampling and remove use of rand().
|
|
||||||
|
|
||||||
- Add sending customized http requests to drogon_ctl.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fix coroutine continuation handle.
|
|
||||||
|
|
||||||
- Fix some bugs in plugin PromExporter.
|
|
||||||
|
|
||||||
- Fix a bug after removing content-length header in some responses.
|
|
||||||
|
|
||||||
## [1.9.7] - 2024-09-10
|
## [1.9.7] - 2024-09-10
|
||||||
|
|
||||||
### API changes list
|
### API changes list
|
||||||
@ -520,7 +403,7 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
- Remove unused CI files and Jekyll config.
|
- Remove unused CI files and Jekyll config.
|
||||||
|
|
||||||
- Ensure that all filters, AOP advice, and handlers are executed within the IO threads.
|
- Ensure that all filters, AOP advices, and handlers are executed within the IO threads.
|
||||||
|
|
||||||
- Update test.sh and build.sh by appending prefix "X" to string variable comparisons.
|
- Update test.sh and build.sh by appending prefix "X" to string variable comparisons.
|
||||||
|
|
||||||
@ -852,7 +735,7 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
- Check HTTP client is not sending requests in sync mode on the same event loop.
|
- Check HTTP client is not sending requests in sync mode on the same event loop.
|
||||||
|
|
||||||
- Start listening after beginning advice.
|
- Start listening after beginning advices.
|
||||||
|
|
||||||
- Allow using json_cpp in other sublibraries.
|
- Allow using json_cpp in other sublibraries.
|
||||||
|
|
||||||
@ -1842,15 +1725,7 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
## [1.0.0-beta1] - 2019-06-11
|
## [1.0.0-beta1] - 2019-06-11
|
||||||
|
|
||||||
[Unreleased]: https://github.com/an-tao/drogon/compare/v1.9.11...HEAD
|
[Unreleased]: https://github.com/an-tao/drogon/compare/v1.9.7...HEAD
|
||||||
|
|
||||||
[1.9.11]: https://github.com/an-tao/drogon/compare/v1.9.10...v1.9.11
|
|
||||||
|
|
||||||
[1.9.10]: https://github.com/an-tao/drogon/compare/v1.9.9...v1.9.10
|
|
||||||
|
|
||||||
[1.9.9]: https://github.com/an-tao/drogon/compare/v1.9.8...v1.9.9
|
|
||||||
|
|
||||||
[1.9.8]: https://github.com/an-tao/drogon/compare/v1.9.7...v1.9.8
|
|
||||||
|
|
||||||
[1.9.7]: https://github.com/an-tao/drogon/compare/v1.9.6...v1.9.7
|
[1.9.7]: https://github.com/an-tao/drogon/compare/v1.9.6...v1.9.7
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||

|

|
||||||
|
|
||||||
[](https://github.com/drogonframework/drogon/actions)
|
[](https://github.com/drogonframework/drogon/actions)
|
||||||
[](https://conan.io/center/recipes/drogon)
|
[](https://conan.io/center/recipes/drogon)
|
||||||
[](https://t.me/joinchat/_mMNGv0748ZkMDAx)
|
[](https://t.me/joinchat/_mMNGv0748ZkMDAx)
|
||||||
[](https://discord.gg/3DvHY6Ewuj)
|
[](https://discord.gg/3DvHY6Ewuj)
|
||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
English | [简体中文](./README.zh-CN.md) | [繁體中文](./README.zh-TW.md)
|
English | [简体中文](./README.zh-CN.md) | [繁體中文](./README.zh-TW.md)
|
||||||
### Overview
|
### Overview
|
||||||
**Drogon** is a C++17/20 based HTTP application framework. Drogon can be used to easily build various types of web application server programs using C++. **Drogon** is the name of a dragon from the American TV series *Game of Thrones*, which I really enjoy.
|
**Drogon** is a C++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 in the American TV series "Game of Thrones" that I really like.
|
||||||
|
|
||||||
Drogon is a cross-platform framework, It supports Linux, macOS, FreeBSD, OpenBSD, HaikuOS, and Windows. Its main features are as follows:
|
Drogon is a cross-platform framework, It supports Linux, macOS, FreeBSD, OpenBSD, HaikuOS, and Windows. Its main features are as follows:
|
||||||
|
|
||||||
@ -183,7 +183,7 @@ As you can see, users can use the `HttpController` to map paths and parameters a
|
|||||||
|
|
||||||
In addition, you can also find that all handler interfaces are in asynchronous mode, where the response is returned by a callback object. This design is for performance reasons because in asynchronous mode the drogon application can handle a large number of concurrent requests with a small number of threads.
|
In addition, you can also find that all handler interfaces are in asynchronous mode, where the response is returned by a callback object. This design is for performance reasons because in asynchronous mode the drogon application can handle a large number of concurrent requests with a small number of threads.
|
||||||
|
|
||||||
After compiling all of the above source files, we get a very simple web application. This is a good start. **For more information, please visit the [documentation](https://drogonframework.github.io/drogon-docs/#/) on GitHub**.
|
After compiling all of the above source files, we get a very simple web application. This is a good start. **For more information, please visit the [wiki](https://github.com/an-tao/drogon/wiki/ENG-01-Overview)**
|
||||||
|
|
||||||
## Cross-compilation
|
## Cross-compilation
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||

|

|
||||||
|
|
||||||
[](https://github.com/drogonframework/drogon/actions)
|
[](https://github.com/drogonframework/drogon/actions)
|
||||||
[](https://conan.io/center/recipes/drogon)
|
[](https://conan.io/center/recipes/drogon)
|
||||||
[](https://t.me/joinchat/_mMNGv0748ZkMDAx)
|
[](https://t.me/joinchat/_mMNGv0748ZkMDAx)
|
||||||
[](https://discord.gg/3DvHY6Ewuj)
|
[](https://discord.gg/3DvHY6Ewuj)
|
||||||
@ -186,7 +186,7 @@ class User : public drogon::HttpController<User>
|
|||||||
|
|
||||||
另外,你可以发现前面所有的处理函数接口都是异步的,处理器的响应是通过回调对象返回的。这种设计是出于对高性能的考虑,因为在异步模式下,可以使用少量的线程(比如和处理器核心数相等的线程)处理大量的并发请求。
|
另外,你可以发现前面所有的处理函数接口都是异步的,处理器的响应是通过回调对象返回的。这种设计是出于对高性能的考虑,因为在异步模式下,可以使用少量的线程(比如和处理器核心数相等的线程)处理大量的并发请求。
|
||||||
|
|
||||||
编译上述的所有源文件后,我们得到了一个非常简单的web应用程序,这是一个不错的开始。**请访问GitHub上的[文档](https://drogonframework.github.io/drogon-docs/#/CHN/CHN-01-%E6%A6%82%E8%BF%B0)**
|
编译上述的所有源文件后,我们得到了一个非常简单的web应用程序,这是一个不错的开始。**请访问[wiki](https://github.com/an-tao/drogon/wiki/CHN-01-概述)**
|
||||||
|
|
||||||
## 贡献方式
|
## 贡献方式
|
||||||
|
|
||||||
@ -197,9 +197,3 @@ class User : public drogon::HttpController<User>
|
|||||||
## QQ交流群:1137909452
|
## QQ交流群:1137909452
|
||||||
|
|
||||||
欢迎交流探讨。
|
欢迎交流探讨。
|
||||||
|
|
||||||
## 微信公众号:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
会不定期推送一些Drogon的使用技巧和更新信息,欢迎关注。
|
|
@ -1,6 +1,6 @@
|
|||||||

|

|
||||||
|
|
||||||
[](https://github.com/drogonframework/drogon/actions)
|
[](https://github.com/drogonframework/drogon/actions)
|
||||||
[](https://conan.io/center/recipes/drogon)
|
[](https://conan.io/center/recipes/drogon)
|
||||||
[](https://t.me/joinchat/_mMNGv0748ZkMDAx)
|
[](https://t.me/joinchat/_mMNGv0748ZkMDAx)
|
||||||
[](https://discord.gg/3DvHY6Ewuj)
|
[](https://discord.gg/3DvHY6Ewuj)
|
||||||
@ -8,42 +8,41 @@
|
|||||||
|
|
||||||
[English](./README.md) | [简体中文](./README.zh-CN.md) | 繁體中文
|
[English](./README.md) | [简体中文](./README.zh-CN.md) | 繁體中文
|
||||||
|
|
||||||
**Drogon** 是一個基於 C++17/20 的 HTTP 應用程式框架,使用 Drogon 可以方便地用 C++ 建立各種類型的 Web App 伺服器端程式。
|
**Drogon**是一個基於C++17/20的Http應用框架,使用Drogon可以方便的使用C++構建各種類型的Web App伺服器程式。
|
||||||
|
本版本庫是github上[Drogon](https://github.com/an-tao/drogon)的鏡像庫。 **Drogon**是作者非常喜歡的美劇《冰與火之歌:權力遊戲》中的一條龍的名字(漢譯作卓耿),和龍有關但並不是dragon的誤寫,為了不至於引起不必要的誤會這裡說明一下。
|
||||||
|
|
||||||
這個版本庫是 GitHub 上 [Drogon](https://github.com/an-tao/drogon) 的鏡像庫。**Drogon** 是作者非常喜歡的美劇《冰與火之歌:權力遊戲》中的一條龍的名字(中文譯作卓耿),和龍有關但並不是 dragon 的誤寫,為了避免不必要的誤會在此說明。
|
Drogon是一個跨平台框架,它支援Linux,也支援macOS、FreeBSD/OpenBSD、HaikuOS和Windows。它的主要特點如下:
|
||||||
|
|
||||||
Drogon 是一個跨平台框架,支援 Linux、macOS、FreeBSD/OpenBSD、HaikuOS 和 Windows。主要特點如下:
|
* 網路層使用基於epoll(macOS/FreeBSD下是kqueue)的非阻塞IO框架,提供高並發、高性能的網路IO。詳細請見[TFB Tests Results](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=composite);
|
||||||
|
* 全異步程式設計;
|
||||||
* 網路層使用基於 epoll(macOS/FreeBSD 下是 kqueue)的非阻塞 IO 框架,提供高並行、高效能的網路 IO。詳細請見 [TFB Tests Results](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=composite);
|
* 支援Http1.0/1.1(server端和client端);
|
||||||
* 完全非同步的程式撰寫邏輯;
|
* 基於模板(template)實現了簡單的反射機制,使主程式框架、控制器(controller)和視圖(view)完全去耦;
|
||||||
* 支援 HTTP 1.0/1.1(伺服器端和用戶端);
|
* 支援cookies和內建的session;
|
||||||
* 基於樣板(template)實作的簡單反射機制,使主程式框架、控制器(controller)和視圖(view)完全解耦;
|
* 支援後端渲染,把控制器生成的數據交給視圖生成Html頁面,視圖由CSP模板文件描述,通過CSP標籤把C++程式碼嵌入到Html頁面,由drogon的指令列工具在編譯階段自動生成C++程式碼並編譯;
|
||||||
* 支援 cookies 和內建的 session;
|
* 支援運行期的視圖頁面動態加載(動態編譯和載入so文件);
|
||||||
* 支援後端算繪,將控制器產生的資料交給視圖產生 HTML 頁面,視圖由 CSP 樣板檔案描述,透過 CSP 標籤將 C++ 程式碼嵌入 HTML 頁面,由 drogon 的命令列工具在編譯階段自動產生 C++ 程式碼並編譯;
|
* 非常方便靈活的路徑(path)到控制器處理函數(handler)的映射方案;
|
||||||
* 支援執行期的視圖頁面動態載入(動態編譯和載入 so 檔案);
|
* 支援過濾器(filter)鏈,方便在控制器之前執行統一的邏輯(如登錄驗證、Http Method約束驗證等);
|
||||||
* 非常方便靈活的路徑(path)到控制器處理函式(handler)的對應方案;
|
* 支援https(基於OpenSSL);
|
||||||
* 支援過濾器(filter)鏈,方便在控制器之前執行統一的邏輯(如登入驗證、HTTP Method 限制驗證等);
|
* 支援websocket(server端和client端);
|
||||||
* 支援 HTTPS(基於 OpenSSL);
|
* 支援Json格式的請求和回應, 方便開發Restful API;
|
||||||
* 支援 WebSocket(伺服器端和用戶端);
|
* 支援文件下載和上傳,支援sendfile系統呼叫;
|
||||||
* 支援 JSON 格式的請求和回應,方便開發 RESTful API;
|
* 支援gzip/brotli壓縮傳輸;
|
||||||
* 支援檔案下載和上傳,支援 `sendfile` 系統呼叫;
|
* 支援pipelining;
|
||||||
* 支援 Gzip/Brotli 壓縮傳輸;
|
* 提供一個輕量的指令列工具drogon_ctl,幫助簡化各種類的創造和視圖程式碼的生成過程;
|
||||||
* 支援 pipelining;
|
* 非同步的讀寫資料庫,目前支援PostgreSQL和MySQL(MariaDB)資料庫;
|
||||||
* 提供輕量的命令列工具 `drogon_ctl`,幫助簡化各種類別的建立和視圖程式碼的產生過程;
|
* 支援異步讀寫Redis;
|
||||||
* 非同步的讀寫資料庫,目前支援 PostgreSQL 和 MySQL(MariaDB)資料庫;
|
* 基於執行序池實現sqlite3資料庫的異步讀寫,提供與上文資料庫相同的接口;
|
||||||
* 支援非同步讀寫 Redis;
|
* 支援ARM架構;
|
||||||
* 基於執行緒池實作 sqlite3 資料庫的非同步讀寫,提供與上述資料庫相同的介面;
|
* 方便的輕量級ORM實現,一般物件到資料庫的雙向映射;
|
||||||
* 支援 ARM 架構;
|
* 支援外掛,可通過設定文件在載入時動態載入;
|
||||||
* 方便的輕量級 ORM 實現,一般物件到資料庫的雙向對應;
|
* 支援內建插入點的AOP
|
||||||
* 支援外掛,可透過設定檔案在載入時動態載入;
|
* 支援C++ coroutine
|
||||||
* 支援內建插入點的 AOP;
|
|
||||||
* 支援 C++ coroutine。
|
|
||||||
|
|
||||||
## 一個非常簡單的例子
|
## 一個非常簡單的例子
|
||||||
|
|
||||||
不像大多數 C++ 框架,drogon 的主程式可以非常簡單。Drogon 使用了一些小技巧使主程式和控制器解耦。控制器的路由設定可以在控制器類別中定義或在設定檔案中完成。
|
不像大多數C++框架那樣,drogon的主程式可以非常簡單。 Drogon使用了一些小技巧使主程式和控制器去耦. 控制器的路由設定可以在控制器類別中定義或者設定文件中完成.
|
||||||
|
|
||||||
下面是一個典型主程式的樣子:
|
下面是一個典型的主程式的樣子:
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
#include <drogon/drogon.h>
|
#include <drogon/drogon.h>
|
||||||
@ -59,7 +58,7 @@ int main()
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
如果使用設定檔案,可以進一步簡化成:
|
如果使用設定文件,可以進一步簡化成這樣:
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
#include <drogon/drogon.h>
|
#include <drogon/drogon.h>
|
||||||
@ -70,7 +69,7 @@ int main()
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
當然,Drogon 也提供了一些函式,讓使用者可以在 `main()` 函式中直接加入控制器邏輯,例如,使用者可以註冊一個 lambda 處理常式到 drogon 框架中,如下所示:
|
當然,Drogon也提供了一些函數,使使用者可以在main()函數中直接添加控制器邏輯,比如,使用者可以註冊一個lambda處理器到drogon框架中,如下所示:
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
app().registerHandler("/test?username={name}",
|
app().registerHandler("/test?username={name}",
|
||||||
@ -87,7 +86,9 @@ app().registerHandler("/test?username={name}",
|
|||||||
{Get,"LoginFilter"});
|
{Get,"LoginFilter"});
|
||||||
```
|
```
|
||||||
|
|
||||||
這看起來很方便,但不適用於複雜的場景,試想如果有數十個或數百個處理函式要註冊進框架,`main()` 函式將變得難以閱讀。顯然,讓每個包含處理函式的類別在自己的定義中完成註冊是更好的選擇。所以,除非你的應用邏輯非常簡單,我們不建議使用上述介面,更好的做法是建立一個 HttpSimpleController 類別,如下:
|
|
||||||
|
這看起來是很方便,但是這並不適用於復雜的場景,試想假如有數十個或者數百個處理函數要註冊進框架,main()函數將膨脹到不可讀的程度。顯然,讓每個包含處理函數的類在自己的定義中完成註冊是更好的選擇。所以,除非你的應用邏輯非常簡單,我們不推薦使用上述接口,更好的實踐是,我們可以創造一個HttpSimpleController類別,如下:
|
||||||
|
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
/// The TestCtrl.h file
|
/// The TestCtrl.h file
|
||||||
@ -116,9 +117,9 @@ void TestCtrl::asyncHandleHttpRequest(const HttpRequestPtr& req,
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**上述程式的大部分程式碼都可以由 `drogon_ctl` 指令產生**(使用指令 `drogon_ctl create controller TestCtr`)。使用者只需要加入自己的業務邏輯。在這個範例中,當用戶端存取 URL `http://ip/test` 時,控制器簡單地回傳一個 `Hello, world!` 頁面。
|
**上面程式的大部分程式碼都可以由`drogon_ctl`指令創造**(這個指令是`drogon_ctl create controller TestCtr`)。使用者所需做的就是添加自己的業務邏輯。在這個例子中,當客戶端訪問URL`http://ip/test`時,控制器簡單的回傳了一個`Hello, world!`頁面。
|
||||||
|
|
||||||
對於 JSON 格式的回應,我們可以這樣建立控制器:
|
對於JSON格式的回應,我們可以像下面這樣創造控制器:
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
/// The header file
|
/// The header file
|
||||||
@ -147,7 +148,7 @@ void JsonCtrl::asyncHandleHttpRequest(const HttpRequestPtr &req,
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
讓我們更進一步,透過 HttpController 類別建立一個 RESTful API 的範例,如下所示(省略實作檔案):
|
讓我們更進一步,通過HttpController類別創造一個RESTful API的例子,如下所示(忽略了實做文件):
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
/// The header file
|
/// The header file
|
||||||
@ -181,18 +182,18 @@ class User : public drogon::HttpController<User>
|
|||||||
} // namespace api
|
} // namespace api
|
||||||
```
|
```
|
||||||
|
|
||||||
如你所見,透過 `HttpController` 類別,使用者可以同時對應路徑和路徑參數,這對 RESTful API 應用來說非常方便。
|
如你所見,通過`HttpController`類別,使用者可以同時映射路徑和路徑參數,這對RESTful API應用來說非常方便。
|
||||||
|
|
||||||
另外,你可以發現前面所有的處理函式介面都是非同步的,處理器的回應是透過回呼物件回傳的。這種設計是考慮到效能,因為在非同步模式下,可以使用少量的執行緒(例如和處理器核心數相等的執行緒)處理大量的並行請求。
|
另外,你可以發現前面所有的處理函數接口都是異步的,處理器的回應是通過回調對象回傳的。這種設計是出於對高性能的考慮,因為在異步模式下,可以使用少量的執行序(比如和處理器核心數相等的執行序)處理大量的並發請求。
|
||||||
|
|
||||||
編譯上述所有原始檔案後,我們得到了一個非常簡單的網頁應用程式,這是一個不錯的開始。**請瀏覽 GitHub 上的[文件](https://drogonframework.github.io/drogon-docs/#/CHN/CHN-01-%E6%A6%82%E8%BF%B0)**
|
編譯上述的所有源文件後,我們得到了一個非常簡單的web應用程式,這是一個不錯的開始。 **請瀏覽[wiki](https://github.com/an-tao/drogon/wiki/CHN-01-概述)**
|
||||||
|
|
||||||
## 貢獻方式
|
## 貢獻方式
|
||||||
|
|
||||||
歡迎您的貢獻。請閱讀[貢獻指南](CONTRIBUTING.md)以取得更多資訊。
|
歡迎您的貢獻。請閱讀[貢獻指南](CONTRIBUTING.md)以獲取更多的信息。
|
||||||
|
|
||||||
<a href="https://github.com/drogonframework/drogon/graphs/contributors"><img src="https://contributors-svg.opencollective.com/drogon/contributors.svg?width=890&button=false" alt="Code contributors" /></a>
|
<a href="https://github.com/drogonframework/drogon/graphs/contributors"><img src="https://contributors-svg.opencollective.com/drogon/contributors.svg?width=890&button=false" alt="Code contributors" /></a>
|
||||||
|
|
||||||
## QQ 交流群:1137909452
|
## QQ交流群:1137909452
|
||||||
|
|
||||||
歡迎交流討論。
|
歡迎交流探討。
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
# ParseAndAddDrogonTests(${PROJECT_NAME}) #
|
# ParseAndAddDrogonTests(${PROJECT_NAME}) #
|
||||||
#==================================================================================================#
|
#==================================================================================================#
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.5...3.31)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
# This removes the contents between
|
# This removes the contents between
|
||||||
# - block comments (i.e. /* ... */)
|
# - block comments (i.e. /* ... */)
|
||||||
|
@ -212,7 +212,7 @@ if(CXX_FILESYSTEM_HAVE_FS)
|
|||||||
]] code @ONLY)
|
]] code @ONLY)
|
||||||
|
|
||||||
# HACK: Needed to compile correctly on Yocto Linux
|
# HACK: Needed to compile correctly on Yocto Linux
|
||||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang"
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "GCC" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang"
|
||||||
OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||||
set(CMAKE_REQUIRED_FLAGS ${prev_req_flags} -std=c++17)
|
set(CMAKE_REQUIRED_FLAGS ${prev_req_flags} -std=c++17)
|
||||||
endif ()
|
endif ()
|
||||||
|
@ -51,9 +51,7 @@ if(Jsoncpp_FOUND)
|
|||||||
COMMAND awk "{ printf \$3 }"
|
COMMAND awk "{ printf \$3 }"
|
||||||
COMMAND sed -e "s/\"//g"
|
COMMAND sed -e "s/\"//g"
|
||||||
OUTPUT_VARIABLE jsoncpp_ver)
|
OUTPUT_VARIABLE jsoncpp_ver)
|
||||||
if(NOT Jsoncpp_FIND_QUIETLY)
|
message(STATUS "jsoncpp version:" ${jsoncpp_ver})
|
||||||
message(STATUS "jsoncpp version:" ${jsoncpp_ver})
|
|
||||||
endif()
|
|
||||||
if(jsoncpp_ver LESS 1.7)
|
if(jsoncpp_ver LESS 1.7)
|
||||||
message(
|
message(
|
||||||
FATAL_ERROR
|
FATAL_ERROR
|
||||||
|
@ -42,8 +42,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";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,17 +66,6 @@ static std::string escapeConnString(const std::string &str)
|
|||||||
return escaped;
|
return escaped;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string drogon_ctl::escapeIdentifier(const std::string &identifier,
|
|
||||||
const std::string &rdbms)
|
|
||||||
{
|
|
||||||
if (rdbms != "postgresql")
|
|
||||||
{
|
|
||||||
return identifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
return "\\\"" + identifier + "\\\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::map<std::string, std::vector<ConvertMethod>> getConvertMethods(
|
static std::map<std::string, std::vector<ConvertMethod>> getConvertMethods(
|
||||||
const Json::Value &convertColumns)
|
const Json::Value &convertColumns)
|
||||||
{
|
{
|
||||||
@ -177,7 +166,7 @@ void create_model::createModelClassFromPG(
|
|||||||
auto className = nameTransform(tableName, true);
|
auto className = nameTransform(tableName, true);
|
||||||
HttpViewData data;
|
HttpViewData data;
|
||||||
data["className"] = className;
|
data["className"] = className;
|
||||||
data["tableName"] = tableName;
|
data["tableName"] = toLower(tableName);
|
||||||
data["hasPrimaryKey"] = (int)0;
|
data["hasPrimaryKey"] = (int)0;
|
||||||
data["primaryKeyName"] = "";
|
data["primaryKeyName"] = "";
|
||||||
data["dbName"] = dbname_;
|
data["dbName"] = dbname_;
|
||||||
@ -826,7 +815,6 @@ void create_model::createModel(const std::string &path,
|
|||||||
auto restfulApiConfig = config["restful_api_controllers"];
|
auto restfulApiConfig = config["restful_api_controllers"];
|
||||||
auto relationships = getRelationships(config["relationships"]);
|
auto relationships = getRelationships(config["relationships"]);
|
||||||
auto convertMethods = getConvertMethods(config["convert"]);
|
auto convertMethods = getConvertMethods(config["convert"]);
|
||||||
drogon::utils::createPath(path);
|
|
||||||
if (dbType == "postgresql")
|
if (dbType == "postgresql")
|
||||||
{
|
{
|
||||||
#if USE_POSTGRESQL
|
#if USE_POSTGRESQL
|
||||||
@ -1174,9 +1162,7 @@ void create_model::createModel(const std::string &path,
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
infile >> configJsonRoot;
|
infile >> configJsonRoot;
|
||||||
createModel(outputPath_.empty() ? path : outputPath_,
|
createModel(path, configJsonRoot, singleModelName);
|
||||||
configJsonRoot,
|
|
||||||
singleModelName);
|
|
||||||
}
|
}
|
||||||
catch (const std::exception &exception)
|
catch (const std::exception &exception)
|
||||||
{
|
{
|
||||||
@ -1214,22 +1200,6 @@ void create_model::handleCommand(std::vector<std::string> ¶meters)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto iter = parameters.begin(); iter != parameters.end();)
|
|
||||||
{
|
|
||||||
auto &file = *iter;
|
|
||||||
if (file == "-o" || file == "--output")
|
|
||||||
{
|
|
||||||
iter = parameters.erase(iter);
|
|
||||||
if (iter != parameters.end())
|
|
||||||
{
|
|
||||||
outputPath_ = *iter;
|
|
||||||
iter = parameters.erase(iter);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
++iter;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto const &path : parameters)
|
for (auto const &path : parameters)
|
||||||
{
|
{
|
||||||
createModel(path, singleModelName);
|
createModel(path, singleModelName);
|
||||||
|
@ -78,9 +78,6 @@ inline std::string nameTransform(const std::string &origName, bool isType)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string escapeIdentifier(const std::string &identifier,
|
|
||||||
const std::string &rdbms);
|
|
||||||
|
|
||||||
class PivotTable
|
class PivotTable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -429,6 +426,5 @@ class create_model : public DrObject<create_model>, public CommandHandler
|
|||||||
const Json::Value &restfulApiConfig);
|
const Json::Value &restfulApiConfig);
|
||||||
std::string dbname_;
|
std::string dbname_;
|
||||||
bool forceOverwrite_{false};
|
bool forceOverwrite_{false};
|
||||||
std::string outputPath_;
|
|
||||||
};
|
};
|
||||||
} // namespace drogon_ctl
|
} // namespace drogon_ctl
|
||||||
|
@ -411,7 +411,6 @@ void create_view::newViewSourceFile(std::ofstream &file,
|
|||||||
"automatically,don't modify it!\n";
|
"automatically,don't modify it!\n";
|
||||||
file << "#include \"" << namespacePrefix << className << ".h\"\n";
|
file << "#include \"" << namespacePrefix << className << ".h\"\n";
|
||||||
file << "#include <drogon/utils/OStringStream.h>\n";
|
file << "#include <drogon/utils/OStringStream.h>\n";
|
||||||
file << "#include <drogon/utils/Utilities.h>\n";
|
|
||||||
file << "#include <string>\n";
|
file << "#include <string>\n";
|
||||||
file << "#include <map>\n";
|
file << "#include <map>\n";
|
||||||
file << "#include <vector>\n";
|
file << "#include <vector>\n";
|
||||||
|
@ -19,10 +19,6 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <json/json.h>
|
|
||||||
#include <fstream>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
@ -37,10 +33,9 @@ std::string press::detail()
|
|||||||
" -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 disable SSL certificate validation(default: enable)\n"
|
||||||
" -f customize http request json file(default: disenable)\n"
|
|
||||||
" -q no progress indication(default: show)\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 std::string_view &err)
|
||||||
@ -156,24 +151,6 @@ void press::handleCommand(std::vector<std::string> ¶meters)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (param.find("-f") == 0)
|
|
||||||
{
|
|
||||||
if (param == "-f")
|
|
||||||
{
|
|
||||||
++iter;
|
|
||||||
if (iter == parameters.end())
|
|
||||||
{
|
|
||||||
outputErrorAndExit("No http request json file!");
|
|
||||||
}
|
|
||||||
httpRequestJsonFile_ = *iter;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
httpRequestJsonFile_ = param.substr(2);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (param == "-k")
|
else if (param == "-k")
|
||||||
{
|
{
|
||||||
certValidation_ = false;
|
certValidation_ = false;
|
||||||
@ -213,118 +190,6 @@ void press::handleCommand(std::vector<std::string> ¶meters)
|
|||||||
path_ = url_.substr(posOfPath);
|
path_ = url_.substr(posOfPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
http_request.json
|
|
||||||
{
|
|
||||||
"method": "POST",
|
|
||||||
"header": {
|
|
||||||
"token": "e2e9d0fe-dd14-4eaf-8ac1-0997730a805d"
|
|
||||||
},
|
|
||||||
"body": {
|
|
||||||
"passwd": "123456",
|
|
||||||
"account": "10001"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
if (!httpRequestJsonFile_.empty())
|
|
||||||
{
|
|
||||||
Json::Value httpRequestJson;
|
|
||||||
std::ifstream httpRequestFile(httpRequestJsonFile_,
|
|
||||||
std::ifstream::binary);
|
|
||||||
if (!httpRequestFile.is_open())
|
|
||||||
{
|
|
||||||
outputErrorAndExit(std::string{"No "} + httpRequestJsonFile_);
|
|
||||||
}
|
|
||||||
httpRequestFile >> httpRequestJson;
|
|
||||||
|
|
||||||
if (!httpRequestJson.isMember("method"))
|
|
||||||
{
|
|
||||||
outputErrorAndExit("No contain method");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto methodStr = httpRequestJson["method"].asString();
|
|
||||||
std::transform(methodStr.begin(),
|
|
||||||
methodStr.end(),
|
|
||||||
methodStr.begin(),
|
|
||||||
::toupper);
|
|
||||||
|
|
||||||
auto toHttpMethod = [&]() -> drogon::HttpMethod {
|
|
||||||
if (methodStr == "GET")
|
|
||||||
{
|
|
||||||
return drogon::HttpMethod::Get;
|
|
||||||
}
|
|
||||||
else if (methodStr == "POST")
|
|
||||||
{
|
|
||||||
return drogon::HttpMethod::Post;
|
|
||||||
}
|
|
||||||
else if (methodStr == "HEAD")
|
|
||||||
{
|
|
||||||
return drogon::HttpMethod::Head;
|
|
||||||
}
|
|
||||||
else if (methodStr == "PUT")
|
|
||||||
{
|
|
||||||
return drogon::HttpMethod::Put;
|
|
||||||
}
|
|
||||||
else if (methodStr == "DELETE")
|
|
||||||
{
|
|
||||||
return drogon::HttpMethod::Delete;
|
|
||||||
}
|
|
||||||
else if (methodStr == "OPTIONS")
|
|
||||||
{
|
|
||||||
return drogon::HttpMethod::Options;
|
|
||||||
}
|
|
||||||
else if (methodStr == "PATCH")
|
|
||||||
{
|
|
||||||
return drogon::HttpMethod::Patch;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
outputErrorAndExit("invalid method");
|
|
||||||
}
|
|
||||||
return drogon::HttpMethod::Get;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unordered_map<std::string, std::string> header;
|
|
||||||
if (httpRequestJson.isMember("header"))
|
|
||||||
{
|
|
||||||
auto &jsonValue = httpRequestJson["header"];
|
|
||||||
for (const auto &key : jsonValue.getMemberNames())
|
|
||||||
{
|
|
||||||
if (jsonValue[key].isString())
|
|
||||||
{
|
|
||||||
header[key] = jsonValue[key].asString();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
header[key] = jsonValue[key].toStyledString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string body;
|
|
||||||
if (httpRequestJson.isMember("body"))
|
|
||||||
{
|
|
||||||
Json::FastWriter fastWriter;
|
|
||||||
body = fastWriter.write(httpRequestJson["body"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
createHttpRequestFunc_ = [this,
|
|
||||||
method = toHttpMethod(),
|
|
||||||
body = std::move(body),
|
|
||||||
header =
|
|
||||||
std::move(header)]() -> HttpRequestPtr {
|
|
||||||
auto request = HttpRequest::newHttpRequest();
|
|
||||||
request->setPath(path_);
|
|
||||||
request->setMethod(method);
|
|
||||||
for (const auto &[field, val] : header)
|
|
||||||
request->addHeader(field, val);
|
|
||||||
if (!body.empty())
|
|
||||||
request->setBody(body);
|
|
||||||
return request;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// std::cout << "host=" << host_ << std::endl;
|
// std::cout << "host=" << host_ << std::endl;
|
||||||
// std::cout << "path=" << path_ << std::endl;
|
// std::cout << "path=" << path_ << std::endl;
|
||||||
doTesting();
|
doTesting();
|
||||||
@ -367,19 +232,9 @@ void press::sendRequest(const HttpClientPtr &client)
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
auto request = HttpRequest::newHttpRequest();
|
||||||
HttpRequestPtr request;
|
request->setPath(path_);
|
||||||
if (createHttpRequestFunc_)
|
request->setMethod(Get);
|
||||||
{
|
|
||||||
request = createHttpRequestFunc_();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
request = HttpRequest::newHttpRequest();
|
|
||||||
request->setPath(path_);
|
|
||||||
request->setMethod(Get);
|
|
||||||
}
|
|
||||||
|
|
||||||
// std::cout << "send!" << std::endl;
|
// std::cout << "send!" << std::endl;
|
||||||
client->sendRequest(
|
client->sendRequest(
|
||||||
request,
|
request,
|
||||||
|
@ -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>
|
||||||
@ -63,8 +62,6 @@ class press : public DrObject<press>, public CommandHandler
|
|||||||
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_;
|
|
||||||
std::function<HttpRequestPtr()> createHttpRequestFunc_;
|
|
||||||
bool certValidation_{true};
|
bool certValidation_{true};
|
||||||
bool processIndication_{true};
|
bool processIndication_{true};
|
||||||
std::string url_;
|
std::string url_;
|
||||||
|
@ -76,7 +76,7 @@ else
|
|||||||
|
|
||||||
<%c++for(auto col:cols){
|
<%c++for(auto col:cols){
|
||||||
%>
|
%>
|
||||||
const std::string [[className]]::Cols::_{%col.colName_%} = "{%escapeIdentifier(col.colName_, rdbms)%}";
|
const std::string [[className]]::Cols::_{%col.colName_%} = "{%col.colName_%}";
|
||||||
<%c++
|
<%c++
|
||||||
}%>
|
}%>
|
||||||
<%c++if(@@.get<int>("hasPrimaryKey")<=1){%>
|
<%c++if(@@.get<int>("hasPrimaryKey")<=1){%>
|
||||||
@ -102,7 +102,7 @@ if(!schema.empty())
|
|||||||
{
|
{
|
||||||
$$<<schema<<".";
|
$$<<schema<<".";
|
||||||
}
|
}
|
||||||
%>{%escapeIdentifier(@@.get<std::string>("tableName"), rdbms)%}";
|
%>[[tableName]]";
|
||||||
|
|
||||||
const std::vector<typename [[className]]::MetaData> [[className]]::metaData_={
|
const std::vector<typename [[className]]::MetaData> [[className]]::metaData_={
|
||||||
<%c++for(size_t i=0;i<cols.size();i++){
|
<%c++for(size_t i=0;i<cols.size();i++){
|
||||||
|
@ -34,8 +34,6 @@ add_executable(redis_chat redis_chat/main.cc
|
|||||||
add_executable(async_stream async_stream/main.cc
|
add_executable(async_stream async_stream/main.cc
|
||||||
async_stream/RequestStreamExampleCtrl.cc)
|
async_stream/RequestStreamExampleCtrl.cc)
|
||||||
|
|
||||||
add_executable(cors cors/main.cc)
|
|
||||||
|
|
||||||
set(example_targets
|
set(example_targets
|
||||||
benchmark
|
benchmark
|
||||||
client
|
client
|
||||||
@ -47,8 +45,7 @@ set(example_targets
|
|||||||
jsonstore
|
jsonstore
|
||||||
redis_simple
|
redis_simple
|
||||||
redis_chat
|
redis_chat
|
||||||
async_stream
|
async_stream)
|
||||||
cors)
|
|
||||||
|
|
||||||
# Add warnings for our example targets--some warnings (such as -Wunused-parameter) only appear
|
# Add warnings for our example targets--some warnings (such as -Wunused-parameter) only appear
|
||||||
# when the templated functions are instantiated at their point of use.
|
# when the templated functions are instantiated at their point of use.
|
||||||
|
@ -16,7 +16,6 @@ proxy with a simple round robin
|
|||||||
11. [redis_cache](https://github.com/drogonframework/drogon/tree/master/examples/redis_cache) - An example for using coroutines of Redis clients
|
11. [redis_cache](https://github.com/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
|
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
|
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
|
||||||
|
|
||||||
|
@ -1,33 +1,15 @@
|
|||||||
#include <drogon/drogon.h>
|
#include <drogon/drogon.h>
|
||||||
#include <chrono>
|
#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 drogon;
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
std::mutex mutex;
|
|
||||||
std::unordered_map<trantor::TcpConnectionPtr, std::function<void()>>
|
|
||||||
connMapping;
|
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
app().registerHandler(
|
app().registerHandler(
|
||||||
"/stream",
|
"/stream",
|
||||||
[](const HttpRequestPtr &req,
|
[](const HttpRequestPtr &,
|
||||||
std::function<void(const HttpResponsePtr &)> &&callback) {
|
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(
|
auto resp = drogon::HttpResponse::newAsyncStreamResponse(
|
||||||
[](drogon::ResponseStreamPtr stream) {
|
[](drogon::ResponseStreamPtr stream) {
|
||||||
std::thread([stream =
|
std::thread([stream =
|
||||||
@ -97,17 +79,5 @@ int main()
|
|||||||
|
|
||||||
LOG_INFO << "Server running on 127.0.0.1:8848";
|
LOG_INFO << "Server running on 127.0.0.1:8848";
|
||||||
app().enableRequestStream(); // This is for request stream.
|
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();
|
app().addListener("127.0.0.1", 8848).run();
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
|
@ -1,4 +1,3 @@
|
|||||||
#include <trantor/utils/Logger.h>
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
#else
|
#else
|
||||||
@ -16,10 +15,8 @@ int main()
|
|||||||
// sent to Drogon
|
// sent to Drogon
|
||||||
app().registerHandler(
|
app().registerHandler(
|
||||||
"/",
|
"/",
|
||||||
[](const HttpRequestPtr &request,
|
[](const HttpRequestPtr &,
|
||||||
std::function<void(const HttpResponsePtr &)> &&callback) {
|
std::function<void(const HttpResponsePtr &)> &&callback) {
|
||||||
LOG_INFO << "connected:"
|
|
||||||
<< (request->connected() ? "true" : "false");
|
|
||||||
auto resp = HttpResponse::newHttpResponse();
|
auto resp = HttpResponse::newHttpResponse();
|
||||||
resp->setBody("Hello, World!");
|
resp->setBody("Hello, World!");
|
||||||
callback(resp);
|
callback(resp);
|
||||||
|
@ -156,18 +156,6 @@ class DROGON_EXPORT Cookie
|
|||||||
sameSite_ = sameSite;
|
sameSite_ = sameSite;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set the partitioned status of the cookie
|
|
||||||
*/
|
|
||||||
void setPartitioned(bool partitioned)
|
|
||||||
{
|
|
||||||
partitioned_ = partitioned;
|
|
||||||
if (partitioned)
|
|
||||||
{
|
|
||||||
setSecure(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the string value of the cookie
|
* @brief Get the string value of the cookie
|
||||||
*/
|
*/
|
||||||
@ -294,17 +282,6 @@ class DROGON_EXPORT Cookie
|
|||||||
return secure_;
|
return secure_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if the cookie is partitioned.
|
|
||||||
*
|
|
||||||
* @return true means the cookie is partitioned.
|
|
||||||
* @return false means the cookie is not partitioned.
|
|
||||||
*/
|
|
||||||
bool isPartitioned() const
|
|
||||||
{
|
|
||||||
return partitioned_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the max-age of the cookie
|
* @brief Get the max-age of the cookie
|
||||||
*/
|
*/
|
||||||
@ -417,7 +394,6 @@ class DROGON_EXPORT Cookie
|
|||||||
trantor::Date expiresDate_{(std::numeric_limits<int64_t>::max)()};
|
trantor::Date expiresDate_{(std::numeric_limits<int64_t>::max)()};
|
||||||
bool httpOnly_{true};
|
bool httpOnly_{true};
|
||||||
bool secure_{false};
|
bool secure_{false};
|
||||||
bool partitioned_{false};
|
|
||||||
std::string domain_;
|
std::string domain_;
|
||||||
std::string path_;
|
std::string path_;
|
||||||
std::string key_;
|
std::string key_;
|
||||||
|
@ -61,9 +61,9 @@ template <typename T>
|
|||||||
struct isAutoCreationClass
|
struct isAutoCreationClass
|
||||||
{
|
{
|
||||||
template <class C>
|
template <class C>
|
||||||
static constexpr auto check(C *) -> std::enable_if_t<
|
static constexpr auto check(C *)
|
||||||
std::is_same_v<decltype(C::isAutoCreation), const bool>,
|
-> std::enable_if_t<std::is_same_v<decltype(C::isAutoCreation), bool>,
|
||||||
bool>
|
bool>
|
||||||
{
|
{
|
||||||
return C::isAutoCreation;
|
return C::isAutoCreation;
|
||||||
}
|
}
|
||||||
|
@ -43,16 +43,6 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
#if defined(__APPLE__) && defined(__MACH__) && \
|
|
||||||
(defined(__ENVIRONMENT_IPHONE_OS__) || \
|
|
||||||
defined(__IPHONE_OS_VERSION_MIN_REQUIRED))
|
|
||||||
// iOS
|
|
||||||
#define TARGET_OS_IOS 1
|
|
||||||
#else
|
|
||||||
// not iOS
|
|
||||||
#define TARGET_OS_IOS 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace drogon
|
namespace drogon
|
||||||
{
|
{
|
||||||
// the drogon banner
|
// the drogon banner
|
||||||
@ -359,7 +349,7 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
|
|||||||
|
|
||||||
/// Register an advice called before routing
|
/// Register an advice called before routing
|
||||||
/**
|
/**
|
||||||
* @param advice is called after all the synchronous advice return
|
* @param advice is called after all the synchronous advices return
|
||||||
* nullptr and before the request is routed to any handler. The parameters
|
* nullptr and before the request is routed to any handler. The parameters
|
||||||
* of the advice are same as those of the doFilter method of the Filter
|
* of the advice are same as those of the doFilter method of the Filter
|
||||||
* class.
|
* class.
|
||||||
@ -1007,7 +997,7 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
|
|||||||
virtual HttpAppFramework &setFileTypes(
|
virtual HttpAppFramework &setFileTypes(
|
||||||
const std::vector<std::string> &types) = 0;
|
const std::vector<std::string> &types) = 0;
|
||||||
|
|
||||||
#if !defined(_WIN32) && !TARGET_OS_IOS
|
#ifndef _WIN32
|
||||||
/// Enable supporting for dynamic views loading.
|
/// Enable supporting for dynamic views loading.
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -1625,15 +1615,6 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
|
|||||||
virtual HttpAppFramework &setAfterAcceptSockOptCallback(
|
virtual HttpAppFramework &setAfterAcceptSockOptCallback(
|
||||||
std::function<void(int)> cb) = 0;
|
std::function<void(int)> cb) = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set the client disconnect or connect callback.
|
|
||||||
*
|
|
||||||
* @param cb This callback will be called, when the client disconnect or
|
|
||||||
* connect
|
|
||||||
*/
|
|
||||||
virtual HttpAppFramework &setConnectionCallback(
|
|
||||||
std::function<void(const trantor::TcpConnectionPtr &)> cb) = 0;
|
|
||||||
|
|
||||||
virtual HttpAppFramework &enableRequestStream(bool enable = true) = 0;
|
virtual HttpAppFramework &enableRequestStream(bool enable = true) = 0;
|
||||||
virtual bool isRequestStreamEnabled() const = 0;
|
virtual bool isRequestStreamEnabled() const = 0;
|
||||||
|
|
||||||
|
@ -30,7 +30,6 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <trantor/net/TcpConnection.h>
|
|
||||||
|
|
||||||
namespace drogon
|
namespace drogon
|
||||||
{
|
{
|
||||||
@ -505,11 +504,6 @@ class DROGON_EXPORT HttpRequest
|
|||||||
virtual void setContentTypeString(const char *typeString,
|
virtual void setContentTypeString(const char *typeString,
|
||||||
size_t typeStringLength) = 0;
|
size_t typeStringLength) = 0;
|
||||||
|
|
||||||
virtual bool connected() const noexcept = 0;
|
|
||||||
|
|
||||||
virtual const std::weak_ptr<trantor::TcpConnection> &getConnectionPtr()
|
|
||||||
const noexcept = 0;
|
|
||||||
|
|
||||||
virtual ~HttpRequest()
|
virtual ~HttpRequest()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <exception>
|
|
||||||
|
|
||||||
namespace drogon
|
namespace drogon
|
||||||
{
|
{
|
||||||
@ -88,7 +87,7 @@ class StreamError final : public std::exception
|
|||||||
* An interface for stream request reading.
|
* An interface for stream request reading.
|
||||||
* User should create an implementation class, or use built-in handlers
|
* User should create an implementation class, or use built-in handlers
|
||||||
*/
|
*/
|
||||||
class DROGON_EXPORT RequestStreamReader
|
class RequestStreamReader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~RequestStreamReader() = default;
|
virtual ~RequestStreamReader() = default;
|
||||||
|
@ -36,7 +36,6 @@ namespace plugin
|
|||||||
// "show_microseconds": true,
|
// "show_microseconds": true,
|
||||||
// "custom_time_format": "",
|
// "custom_time_format": "",
|
||||||
// "use_real_ip": false
|
// "use_real_ip": false
|
||||||
// "path_exempt": ""
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@endcode
|
@endcode
|
||||||
@ -99,10 +98,6 @@ namespace plugin
|
|||||||
* Enable the plugin by adding the configuration to the list of plugins in the
|
* Enable the plugin by adding the configuration to the list of plugins in the
|
||||||
* configuration file.
|
* configuration file.
|
||||||
*
|
*
|
||||||
* path_exempt: must be a string or a string array, present a regular expression
|
|
||||||
* (for matching the path of a request) or a regular expression list for URLs
|
|
||||||
* that don't have to be logged.
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
class DROGON_EXPORT AccessLogger : public drogon::Plugin<AccessLogger>
|
class DROGON_EXPORT AccessLogger : public drogon::Plugin<AccessLogger>
|
||||||
{
|
{
|
||||||
@ -122,8 +117,6 @@ class DROGON_EXPORT AccessLogger : public drogon::Plugin<AccessLogger>
|
|||||||
bool useCustomTimeFormat_{false};
|
bool useCustomTimeFormat_{false};
|
||||||
std::string timeFormat_;
|
std::string timeFormat_;
|
||||||
static bool useRealIp_;
|
static bool useRealIp_;
|
||||||
std::regex exemptRegex_;
|
|
||||||
bool regexFlag_{false};
|
|
||||||
|
|
||||||
using LogFunction = std::function<void(trantor::LogStream &,
|
using LogFunction = std::function<void(trantor::LogStream &,
|
||||||
const drogon::HttpRequestPtr &,
|
const drogon::HttpRequestPtr &,
|
||||||
|
@ -71,9 +71,7 @@ IPs or users. the default value is 600.
|
|||||||
"ip_capacity": 0,
|
"ip_capacity": 0,
|
||||||
"user_capacity": 0
|
"user_capacity": 0
|
||||||
},...
|
},...
|
||||||
],
|
]
|
||||||
// Trusted proxy ip or cidr
|
|
||||||
"trust_ips": ["127.0.0.1", "172.16.0.0/12"],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@endcode
|
@endcode
|
||||||
@ -139,14 +137,12 @@ class DROGON_EXPORT Hodor : public drogon::Plugin<Hodor>
|
|||||||
std::function<HttpResponsePtr(const drogon::HttpRequestPtr &)>
|
std::function<HttpResponsePtr(const drogon::HttpRequestPtr &)>
|
||||||
rejectResponseFactory_;
|
rejectResponseFactory_;
|
||||||
|
|
||||||
RealIpResolver::CIDRs trustCIDRs_;
|
|
||||||
|
|
||||||
void onHttpRequest(const drogon::HttpRequestPtr &,
|
void onHttpRequest(const drogon::HttpRequestPtr &,
|
||||||
AdviceCallback &&,
|
AdviceCallback &&,
|
||||||
AdviceChainCallback &&);
|
AdviceChainCallback &&);
|
||||||
bool checkLimit(const drogon::HttpRequestPtr &req,
|
bool checkLimit(const drogon::HttpRequestPtr &req,
|
||||||
const LimitStrategy &strategy,
|
const LimitStrategy &strategy,
|
||||||
const trantor::InetAddress &ip,
|
const std::string &ip,
|
||||||
const std::optional<std::string> &userId);
|
const std::optional<std::string> &userId);
|
||||||
HttpResponsePtr rejectResponse_;
|
HttpResponsePtr rejectResponse_;
|
||||||
};
|
};
|
||||||
|
@ -57,6 +57,7 @@ class DROGON_EXPORT RealIpResolver : public drogon::Plugin<RealIpResolver>
|
|||||||
private:
|
private:
|
||||||
const trantor::InetAddress &getRealAddr(
|
const trantor::InetAddress &getRealAddr(
|
||||||
const drogon::HttpRequestPtr &req) const;
|
const drogon::HttpRequestPtr &req) const;
|
||||||
|
bool matchCidr(const trantor::InetAddress &addr) const;
|
||||||
|
|
||||||
struct CIDR
|
struct CIDR
|
||||||
{
|
{
|
||||||
@ -65,12 +66,7 @@ class DROGON_EXPORT RealIpResolver : public drogon::Plugin<RealIpResolver>
|
|||||||
in_addr_t mask_{32};
|
in_addr_t mask_{32};
|
||||||
};
|
};
|
||||||
|
|
||||||
using CIDRs = std::vector<CIDR>;
|
std::vector<CIDR> trustCIDRs_;
|
||||||
static bool matchCidr(const trantor::InetAddress &addr,
|
|
||||||
const CIDRs &trustCIDRs);
|
|
||||||
|
|
||||||
friend class Hodor;
|
|
||||||
CIDRs trustCIDRs_;
|
|
||||||
std::string fromHeader_;
|
std::string fromHeader_;
|
||||||
std::string attributeKey_;
|
std::string attributeKey_;
|
||||||
bool useXForwardedFor_{false};
|
bool useXForwardedFor_{false};
|
||||||
|
@ -134,85 +134,47 @@ constexpr size_t base64EncodedLength(size_t in_len, bool padded = true)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Encode the string to base64 format.
|
/// Encode the string to base64 format.
|
||||||
DROGON_EXPORT void base64Encode(const unsigned char *bytesToEncode,
|
DROGON_EXPORT std::string base64Encode(const unsigned char *bytes_to_encode,
|
||||||
size_t inLen,
|
size_t in_len,
|
||||||
unsigned char *outputBuffer,
|
bool url_safe = false,
|
||||||
bool urlSafe = false,
|
bool padded = true);
|
||||||
bool padded = true);
|
|
||||||
|
|
||||||
/// Encode the string to base64 format.
|
|
||||||
inline std::string base64Encode(const unsigned char *bytesToEncode,
|
|
||||||
size_t inLen,
|
|
||||||
bool urlSafe = false,
|
|
||||||
bool padded = true)
|
|
||||||
{
|
|
||||||
std::string ret;
|
|
||||||
ret.resize(base64EncodedLength(inLen, padded));
|
|
||||||
base64Encode(
|
|
||||||
bytesToEncode, inLen, (unsigned char *)ret.data(), urlSafe, padded);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Encode the string to base64 format.
|
/// Encode the string to base64 format.
|
||||||
inline std::string base64Encode(std::string_view data,
|
inline std::string base64Encode(std::string_view data,
|
||||||
bool urlSafe = false,
|
bool url_safe = false,
|
||||||
bool padded = true)
|
bool padded = true)
|
||||||
{
|
{
|
||||||
return base64Encode((unsigned char *)data.data(),
|
return base64Encode((unsigned char *)data.data(),
|
||||||
data.size(),
|
data.size(),
|
||||||
urlSafe,
|
url_safe,
|
||||||
padded);
|
padded);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encode the string to base64 format with no padding.
|
/// Encode the string to base64 format with no padding.
|
||||||
inline void base64EncodeUnpadded(const unsigned char *bytesToEncode,
|
inline std::string base64EncodeUnpadded(const unsigned char *bytes_to_encode,
|
||||||
size_t inLen,
|
size_t in_len,
|
||||||
unsigned char *outputBuffer,
|
bool url_safe = false)
|
||||||
bool urlSafe = false)
|
|
||||||
{
|
{
|
||||||
base64Encode(bytesToEncode, inLen, outputBuffer, urlSafe, false);
|
return base64Encode(bytes_to_encode, in_len, url_safe, false);
|
||||||
}
|
|
||||||
|
|
||||||
/// Encode the string to base64 format with no padding.
|
|
||||||
inline std::string base64EncodeUnpadded(const unsigned char *bytesToEncode,
|
|
||||||
size_t inLen,
|
|
||||||
bool urlSafe = false)
|
|
||||||
{
|
|
||||||
return base64Encode(bytesToEncode, inLen, urlSafe, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encode the string to base64 format with no padding.
|
/// Encode the string to base64 format with no padding.
|
||||||
inline std::string base64EncodeUnpadded(std::string_view data,
|
inline std::string base64EncodeUnpadded(std::string_view data,
|
||||||
bool urlSafe = false)
|
bool url_safe = false)
|
||||||
{
|
{
|
||||||
return base64Encode(data, urlSafe, false);
|
return base64Encode(data, url_safe, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the decoded length of base64.
|
/// Get the decoded length of base64.
|
||||||
constexpr size_t base64DecodedLength(size_t inLen)
|
constexpr size_t base64DecodedLength(size_t in_len)
|
||||||
{
|
{
|
||||||
return (inLen * 3) / 4;
|
return (in_len * 3) / 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decode the base64 format string.
|
/// Decode the base64 format string.
|
||||||
/// Return the number of bytes written.
|
DROGON_EXPORT std::string base64Decode(std::string_view encoded_string);
|
||||||
DROGON_EXPORT size_t base64Decode(const char *encodedString,
|
|
||||||
size_t inLen,
|
|
||||||
unsigned char *outputBuffer);
|
|
||||||
|
|
||||||
/// Decode the base64 format string.
|
|
||||||
inline std::string base64Decode(std::string_view encodedString)
|
|
||||||
{
|
|
||||||
auto inLen = encodedString.size();
|
|
||||||
std::string ret;
|
|
||||||
ret.resize(base64DecodedLength(inLen));
|
|
||||||
ret.resize(
|
|
||||||
base64Decode(encodedString.data(), inLen, (unsigned char *)ret.data()));
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
DROGON_EXPORT std::vector<char> base64DecodeToVector(
|
DROGON_EXPORT std::vector<char> base64DecodeToVector(
|
||||||
std::string_view encodedString);
|
std::string_view encoded_string);
|
||||||
|
|
||||||
/// Check if the string need decoding
|
/// Check if the string need decoding
|
||||||
DROGON_EXPORT bool needUrlDecoding(const char *begin, const char *end);
|
DROGON_EXPORT bool needUrlDecoding(const char *begin, const char *end);
|
||||||
@ -301,12 +263,6 @@ DROGON_EXPORT std::string brotliDecompress(const char *data,
|
|||||||
DROGON_EXPORT char *getHttpFullDate(
|
DROGON_EXPORT char *getHttpFullDate(
|
||||||
const trantor::Date &date = trantor::Date::now());
|
const trantor::Date &date = trantor::Date::now());
|
||||||
|
|
||||||
DROGON_EXPORT const std::string &getHttpFullDateStr(
|
|
||||||
const trantor::Date &date = trantor::Date::now());
|
|
||||||
|
|
||||||
DROGON_EXPORT void dateToCustomFormattedString(const std::string &fmtStr,
|
|
||||||
std::string &str,
|
|
||||||
const trantor::Date &date);
|
|
||||||
/// Get the trantor::Date object according to the http full date string
|
/// Get the trantor::Date object according to the http full date string
|
||||||
/**
|
/**
|
||||||
* Returns trantor::Date(std::numeric_limits<int64_t>::max()) upon failure.
|
* Returns trantor::Date(std::numeric_limits<int64_t>::max()) upon failure.
|
||||||
@ -584,8 +540,7 @@ namespace trantor
|
|||||||
{
|
{
|
||||||
inline LogStream &operator<<(LogStream &ls, const std::string_view &v)
|
inline LogStream &operator<<(LogStream &ls, const std::string_view &v)
|
||||||
{
|
{
|
||||||
if (!v.empty())
|
ls.append(v.data(), v.length());
|
||||||
ls.append(v.data(), v.length());
|
|
||||||
return ls;
|
return ls;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,7 +245,7 @@ struct [[nodiscard]] Task
|
|||||||
|
|
||||||
std::optional<T> value;
|
std::optional<T> value;
|
||||||
std::exception_ptr exception_;
|
std::exception_ptr exception_;
|
||||||
std::coroutine_handle<> continuation_{std::noop_coroutine()};
|
std::coroutine_handle<> continuation_;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto operator co_await() const noexcept
|
auto operator co_await() const noexcept
|
||||||
@ -332,7 +332,7 @@ struct [[nodiscard]] Task<void>
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::exception_ptr exception_;
|
std::exception_ptr exception_;
|
||||||
std::coroutine_handle<> continuation_{std::noop_coroutine()};
|
std::coroutine_handle<> continuation_;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto operator co_await() const noexcept
|
auto operator co_await() const noexcept
|
||||||
|
@ -91,7 +91,7 @@ class Gauge : public Metric
|
|||||||
|
|
||||||
static std::string_view type()
|
static std::string_view type()
|
||||||
{
|
{
|
||||||
return "gauge";
|
return "counter";
|
||||||
}
|
}
|
||||||
|
|
||||||
void setToCurrentTime()
|
void setToCurrentTime()
|
||||||
|
@ -20,10 +20,10 @@
|
|||||||
namespace drogon
|
namespace drogon
|
||||||
{
|
{
|
||||||
|
|
||||||
static void doAdviceChain(
|
static void doAdvicesChain(
|
||||||
const std::vector<std::function<void(const HttpRequestPtr &,
|
const std::vector<std::function<void(const HttpRequestPtr &,
|
||||||
AdviceCallback &&,
|
AdviceCallback &&,
|
||||||
AdviceChainCallback &&)>> &adviceChain,
|
AdviceChainCallback &&)>> &advices,
|
||||||
size_t index,
|
size_t index,
|
||||||
const HttpRequestImplPtr &req,
|
const HttpRequestImplPtr &req,
|
||||||
std::shared_ptr<const std::function<void(const HttpResponsePtr &)>>
|
std::shared_ptr<const std::function<void(const HttpResponsePtr &)>>
|
||||||
@ -88,7 +88,7 @@ void AopAdvice::passPreRoutingAdvices(
|
|||||||
|
|
||||||
auto callbackPtr =
|
auto callbackPtr =
|
||||||
std::make_shared<std::decay_t<decltype(callback)>>(std::move(callback));
|
std::make_shared<std::decay_t<decltype(callback)>>(std::move(callback));
|
||||||
doAdviceChain(preRoutingAdvices_, 0, req, std::move(callbackPtr));
|
doAdvicesChain(preRoutingAdvices_, 0, req, std::move(callbackPtr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AopAdvice::passPostRoutingObservers(const HttpRequestImplPtr &req) const
|
void AopAdvice::passPostRoutingObservers(const HttpRequestImplPtr &req) const
|
||||||
@ -114,7 +114,7 @@ void AopAdvice::passPostRoutingAdvices(
|
|||||||
|
|
||||||
auto callbackPtr =
|
auto callbackPtr =
|
||||||
std::make_shared<std::decay_t<decltype(callback)>>(std::move(callback));
|
std::make_shared<std::decay_t<decltype(callback)>>(std::move(callback));
|
||||||
doAdviceChain(postRoutingAdvices_, 0, req, std::move(callbackPtr));
|
doAdvicesChain(postRoutingAdvices_, 0, req, std::move(callbackPtr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AopAdvice::passPreHandlingObservers(const HttpRequestImplPtr &req) const
|
void AopAdvice::passPreHandlingObservers(const HttpRequestImplPtr &req) const
|
||||||
@ -140,7 +140,7 @@ void AopAdvice::passPreHandlingAdvices(
|
|||||||
|
|
||||||
auto callbackPtr =
|
auto callbackPtr =
|
||||||
std::make_shared<std::decay_t<decltype(callback)>>(std::move(callback));
|
std::make_shared<std::decay_t<decltype(callback)>>(std::move(callback));
|
||||||
doAdviceChain(preHandlingAdvices_, 0, req, std::move(callbackPtr));
|
doAdvicesChain(preHandlingAdvices_, 0, req, std::move(callbackPtr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AopAdvice::passPostHandlingAdvices(const HttpRequestImplPtr &req,
|
void AopAdvice::passPostHandlingAdvices(const HttpRequestImplPtr &req,
|
||||||
@ -161,43 +161,43 @@ void AopAdvice::passPreSendingAdvices(const HttpRequestImplPtr &req,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void doAdviceChain(
|
static void doAdvicesChain(
|
||||||
const std::vector<std::function<void(const HttpRequestPtr &,
|
const std::vector<std::function<void(const HttpRequestPtr &,
|
||||||
AdviceCallback &&,
|
AdviceCallback &&,
|
||||||
AdviceChainCallback &&)>> &adviceChain,
|
AdviceChainCallback &&)>> &advices,
|
||||||
size_t index,
|
size_t index,
|
||||||
const HttpRequestImplPtr &req,
|
const HttpRequestImplPtr &req,
|
||||||
std::shared_ptr<const std::function<void(const HttpResponsePtr &)>>
|
std::shared_ptr<const std::function<void(const HttpResponsePtr &)>>
|
||||||
&&callbackPtr)
|
&&callbackPtr)
|
||||||
{
|
{
|
||||||
if (index < adviceChain.size())
|
if (index < advices.size())
|
||||||
{
|
{
|
||||||
auto &advice = adviceChain[index];
|
auto &advice = advices[index];
|
||||||
advice(
|
advice(
|
||||||
req,
|
req,
|
||||||
[/*copy*/ callbackPtr](const HttpResponsePtr &resp) {
|
[/*copy*/ callbackPtr](const HttpResponsePtr &resp) {
|
||||||
(*callbackPtr)(resp);
|
(*callbackPtr)(resp);
|
||||||
},
|
},
|
||||||
[index, req, callbackPtr, &adviceChain]() mutable {
|
[index, req, callbackPtr, &advices]() mutable {
|
||||||
auto ioLoop = req->getLoop();
|
auto ioLoop = req->getLoop();
|
||||||
if (ioLoop && !ioLoop->isInLoopThread())
|
if (ioLoop && !ioLoop->isInLoopThread())
|
||||||
{
|
{
|
||||||
ioLoop->queueInLoop([index,
|
ioLoop->queueInLoop([index,
|
||||||
req,
|
req,
|
||||||
callbackPtr = std::move(callbackPtr),
|
callbackPtr = std::move(callbackPtr),
|
||||||
&adviceChain]() mutable {
|
&advices]() mutable {
|
||||||
doAdviceChain(adviceChain,
|
doAdvicesChain(advices,
|
||||||
index + 1,
|
index + 1,
|
||||||
req,
|
req,
|
||||||
std::move(callbackPtr));
|
std::move(callbackPtr));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
doAdviceChain(adviceChain,
|
doAdvicesChain(advices,
|
||||||
index + 1,
|
index + 1,
|
||||||
req,
|
req,
|
||||||
std::move(callbackPtr));
|
std::move(callbackPtr));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -113,46 +113,6 @@ void AccessLogger::initAndStart(const Json::Value &config)
|
|||||||
}
|
}
|
||||||
createLogFunctions(format);
|
createLogFunctions(format);
|
||||||
auto logPath = config.get("log_path", "").asString();
|
auto logPath = config.get("log_path", "").asString();
|
||||||
|
|
||||||
if (config.isMember("path_exempt"))
|
|
||||||
{
|
|
||||||
if (config["path_exempt"].isArray())
|
|
||||||
{
|
|
||||||
const auto &exempts = config["path_exempt"];
|
|
||||||
size_t exemptsCount = exempts.size();
|
|
||||||
if (exemptsCount)
|
|
||||||
{
|
|
||||||
std::string regexString;
|
|
||||||
size_t len = 0;
|
|
||||||
for (const auto &exempt : exempts)
|
|
||||||
{
|
|
||||||
assert(exempt.isString());
|
|
||||||
len += exempt.size();
|
|
||||||
}
|
|
||||||
regexString.reserve((exemptsCount * (1 + 2)) - 1 + len);
|
|
||||||
|
|
||||||
const auto last = --exempts.end();
|
|
||||||
for (auto exempt = exempts.begin(); exempt != last; ++exempt)
|
|
||||||
regexString.append("(")
|
|
||||||
.append(exempt->asString())
|
|
||||||
.append(")|");
|
|
||||||
regexString.append("(").append(last->asString()).append(")");
|
|
||||||
|
|
||||||
exemptRegex_ = std::regex(regexString);
|
|
||||||
regexFlag_ = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (config["path_exempt"].isString())
|
|
||||||
{
|
|
||||||
exemptRegex_ = std::regex(config["path_exempt"].asString());
|
|
||||||
regexFlag_ = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOG_ERROR << "path_exempt must be a string or string array!";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DROGON_SPDLOG_SUPPORT
|
#ifdef DROGON_SPDLOG_SUPPORT
|
||||||
auto logWithSpdlog = trantor::Logger::hasSpdLogSupport() &&
|
auto logWithSpdlog = trantor::Logger::hasSpdLogSupport() &&
|
||||||
config.get("use_spdlog", false).asBool();
|
config.get("use_spdlog", false).asBool();
|
||||||
@ -268,17 +228,7 @@ void AccessLogger::initAndStart(const Json::Value &config)
|
|||||||
drogon::app().registerPreSendingAdvice(
|
drogon::app().registerPreSendingAdvice(
|
||||||
[this](const drogon::HttpRequestPtr &req,
|
[this](const drogon::HttpRequestPtr &req,
|
||||||
const drogon::HttpResponsePtr &resp) {
|
const drogon::HttpResponsePtr &resp) {
|
||||||
if (regexFlag_)
|
logging(LOG_RAW_TO(logIndex_), req, resp);
|
||||||
{
|
|
||||||
if (!std::regex_match(req->path(), exemptRegex_))
|
|
||||||
{
|
|
||||||
logging(LOG_RAW_TO(logIndex_), req, resp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logging(LOG_RAW_TO(logIndex_), req, resp);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,7 +383,7 @@ static void loadApp(const Json::Value &app)
|
|||||||
{
|
{
|
||||||
drogon::app().setMaxConnectionNumPerIP(maxConnsPerIP);
|
drogon::app().setMaxConnectionNumPerIP(maxConnsPerIP);
|
||||||
}
|
}
|
||||||
#if !defined(_WIN32) && !TARGET_OS_IOS
|
#ifndef _WIN32
|
||||||
// dynamic views
|
// dynamic views
|
||||||
auto enableDynamicViews = app.get("load_dynamic_views", false).asBool();
|
auto enableDynamicViews = app.get("load_dynamic_views", false).asBool();
|
||||||
if (enableDynamicViews)
|
if (enableDynamicViews)
|
||||||
|
@ -30,7 +30,7 @@ std::string Cookie::cookieString() const
|
|||||||
expiresDate_.microSecondsSinceEpoch() >= 0)
|
expiresDate_.microSecondsSinceEpoch() >= 0)
|
||||||
{
|
{
|
||||||
ret.append("Expires=")
|
ret.append("Expires=")
|
||||||
.append(utils::getHttpFullDateStr(expiresDate_))
|
.append(utils::getHttpFullDate(expiresDate_))
|
||||||
.append("; ");
|
.append("; ");
|
||||||
}
|
}
|
||||||
if (maxAge_.has_value())
|
if (maxAge_.has_value())
|
||||||
@ -69,7 +69,7 @@ std::string Cookie::cookieString() const
|
|||||||
ret.append("SameSite=Lax; ");
|
ret.append("SameSite=Lax; ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((secure_ && sameSite_ != SameSite::kNone) || partitioned_)
|
if (secure_ && sameSite_ != SameSite::kNone)
|
||||||
{
|
{
|
||||||
ret.append("Secure; ");
|
ret.append("Secure; ");
|
||||||
}
|
}
|
||||||
@ -77,10 +77,6 @@ std::string Cookie::cookieString() const
|
|||||||
{
|
{
|
||||||
ret.append("HttpOnly; ");
|
ret.append("HttpOnly; ");
|
||||||
}
|
}
|
||||||
if (partitioned_)
|
|
||||||
{
|
|
||||||
ret.append("Partitioned; ");
|
|
||||||
}
|
|
||||||
ret.resize(ret.length() - 2); // delete last semicolon
|
ret.resize(ret.length() - 2); // delete last semicolon
|
||||||
ret.append("\r\n");
|
ret.append("\r\n");
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -105,17 +105,6 @@ void Hodor::initAndStart(const Json::Value &config)
|
|||||||
limitStrategies_.emplace_back(makeLimitStrategy(subLimit));
|
limitStrategies_.emplace_back(makeLimitStrategy(subLimit));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Json::Value &trustIps = config["trust_ips"];
|
|
||||||
if (!trustIps.isNull() && !trustIps.isArray())
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Invalid trusted_ips. Should be array.");
|
|
||||||
}
|
|
||||||
for (const auto &ipOrCidr : trustIps)
|
|
||||||
{
|
|
||||||
trustCIDRs_.emplace_back(ipOrCidr.asString());
|
|
||||||
}
|
|
||||||
|
|
||||||
app().registerPreHandlingAdvice([this](const drogon::HttpRequestPtr &req,
|
app().registerPreHandlingAdvice([this](const drogon::HttpRequestPtr &req,
|
||||||
AdviceCallback &&acb,
|
AdviceCallback &&acb,
|
||||||
AdviceChainCallback &&accb) {
|
AdviceChainCallback &&accb) {
|
||||||
@ -130,13 +119,9 @@ void Hodor::shutdown()
|
|||||||
|
|
||||||
bool Hodor::checkLimit(const drogon::HttpRequestPtr &req,
|
bool Hodor::checkLimit(const drogon::HttpRequestPtr &req,
|
||||||
const LimitStrategy &strategy,
|
const LimitStrategy &strategy,
|
||||||
const trantor::InetAddress &ip,
|
const std::string &ip,
|
||||||
const std::optional<std::string> &userId)
|
const std::optional<std::string> &userId)
|
||||||
{
|
{
|
||||||
if (RealIpResolver::matchCidr(ip, trustCIDRs_))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strategy.regexFlag)
|
if (strategy.regexFlag)
|
||||||
{
|
{
|
||||||
if (!std::regex_match(req->path(), strategy.urlsRegex))
|
if (!std::regex_match(req->path(), strategy.urlsRegex))
|
||||||
@ -155,7 +140,7 @@ bool Hodor::checkLimit(const drogon::HttpRequestPtr &req,
|
|||||||
{
|
{
|
||||||
RateLimiterPtr limiterPtr;
|
RateLimiterPtr limiterPtr;
|
||||||
strategy.ipLimiterMapPtr->modify(
|
strategy.ipLimiterMapPtr->modify(
|
||||||
ip.toIpNetEndian(),
|
ip,
|
||||||
[this, &limiterPtr, &strategy](RateLimiterPtr &ptr) {
|
[this, &limiterPtr, &strategy](RateLimiterPtr &ptr) {
|
||||||
if (!ptr)
|
if (!ptr)
|
||||||
{
|
{
|
||||||
@ -222,9 +207,10 @@ void Hodor::onHttpRequest(const drogon::HttpRequestPtr &req,
|
|||||||
drogon::AdviceCallback &&adviceCallback,
|
drogon::AdviceCallback &&adviceCallback,
|
||||||
drogon::AdviceChainCallback &&chainCallback)
|
drogon::AdviceChainCallback &&chainCallback)
|
||||||
{
|
{
|
||||||
const trantor::InetAddress &ip =
|
auto ip =
|
||||||
useRealIpResolver_ ? drogon::plugin::RealIpResolver::GetRealAddr(req)
|
(useRealIpResolver_ ? drogon::plugin::RealIpResolver::GetRealAddr(req)
|
||||||
: req->peerAddr();
|
: req->peerAddr())
|
||||||
|
.toIpNetEndian();
|
||||||
std::optional<std::string> userId;
|
std::optional<std::string> userId;
|
||||||
if (userIdGetter_)
|
if (userIdGetter_)
|
||||||
{
|
{
|
||||||
|
@ -184,7 +184,7 @@ static void TERMFunction(int sig)
|
|||||||
HttpAppFrameworkImpl::~HttpAppFrameworkImpl() noexcept
|
HttpAppFrameworkImpl::~HttpAppFrameworkImpl() noexcept
|
||||||
{
|
{
|
||||||
// Destroy the following objects before the loop destruction
|
// Destroy the following objects before the loop destruction
|
||||||
#if !defined(_WIN32) && !TARGET_OS_IOS
|
#ifndef _WIN32
|
||||||
sharedLibManagerPtr_.reset();
|
sharedLibManagerPtr_.reset();
|
||||||
#endif
|
#endif
|
||||||
sessionManagerPtr_.reset();
|
sessionManagerPtr_.reset();
|
||||||
@ -236,7 +236,7 @@ const std::string &HttpAppFrameworkImpl::getImplicitPage() const
|
|||||||
{
|
{
|
||||||
return StaticFileRouter::instance().getImplicitPage();
|
return StaticFileRouter::instance().getImplicitPage();
|
||||||
}
|
}
|
||||||
#if !defined(_WIN32) && !TARGET_OS_IOS
|
#ifndef _WIN32
|
||||||
HttpAppFramework &HttpAppFrameworkImpl::enableDynamicViewsLoading(
|
HttpAppFramework &HttpAppFrameworkImpl::enableDynamicViewsLoading(
|
||||||
const std::vector<std::string> &libPaths,
|
const std::vector<std::string> &libPaths,
|
||||||
const std::string &outputPath)
|
const std::string &outputPath)
|
||||||
@ -599,7 +599,7 @@ void HttpAppFrameworkImpl::run()
|
|||||||
LOG_INFO << "Start child process";
|
LOG_INFO << "Start child process";
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(_WIN32) && !TARGET_OS_IOS
|
#ifndef _WIN32
|
||||||
if (!libFilePaths_.empty())
|
if (!libFilePaths_.empty())
|
||||||
{
|
{
|
||||||
sharedLibManagerPtr_ =
|
sharedLibManagerPtr_ =
|
||||||
@ -1033,7 +1033,7 @@ HttpAppFramework &HttpAppFrameworkImpl::createRedisClient(
|
|||||||
|
|
||||||
void HttpAppFrameworkImpl::quit()
|
void HttpAppFrameworkImpl::quit()
|
||||||
{
|
{
|
||||||
if (getLoop()->isRunning() && running_.exchange(false))
|
if (getLoop()->isRunning())
|
||||||
{
|
{
|
||||||
getLoop()->queueInLoop([this]() {
|
getLoop()->queueInLoop([this]() {
|
||||||
// Release members in the reverse order of initialization
|
// Release members in the reverse order of initialization
|
||||||
@ -1044,6 +1044,7 @@ void HttpAppFrameworkImpl::quit()
|
|||||||
pluginsManagerPtr_.reset();
|
pluginsManagerPtr_.reset();
|
||||||
redisClientManagerPtr_.reset();
|
redisClientManagerPtr_.reset();
|
||||||
dbClientManagerPtr_.reset();
|
dbClientManagerPtr_.reset();
|
||||||
|
running_ = false;
|
||||||
getLoop()->quit();
|
getLoop()->quit();
|
||||||
for (trantor::EventLoop *loop : ioLoopThreadPool_->getLoops())
|
for (trantor::EventLoop *loop : ioLoopThreadPool_->getLoops())
|
||||||
{
|
{
|
||||||
@ -1366,10 +1367,3 @@ HttpAppFramework &HttpAppFrameworkImpl::setAfterAcceptSockOptCallback(
|
|||||||
listenerManagerPtr_->setAfterAcceptSockOptCallback(std::move(cb));
|
listenerManagerPtr_->setAfterAcceptSockOptCallback(std::move(cb));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpAppFramework &HttpAppFrameworkImpl::setConnectionCallback(
|
|
||||||
std::function<void(const trantor::TcpConnectionPtr &)> cb)
|
|
||||||
{
|
|
||||||
listenerManagerPtr_->setConnectionCallback(std::move(cb));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
@ -267,7 +267,7 @@ class HttpAppFrameworkImpl final : public HttpAppFramework
|
|||||||
HttpAppFramework &setUploadPath(const std::string &uploadPath) override;
|
HttpAppFramework &setUploadPath(const std::string &uploadPath) override;
|
||||||
HttpAppFramework &setFileTypes(
|
HttpAppFramework &setFileTypes(
|
||||||
const std::vector<std::string> &types) override;
|
const std::vector<std::string> &types) override;
|
||||||
#if !defined(_WIN32) && !TARGET_OS_IOS
|
#ifndef _WIN32
|
||||||
HttpAppFramework &enableDynamicViewsLoading(
|
HttpAppFramework &enableDynamicViewsLoading(
|
||||||
const std::vector<std::string> &libPaths,
|
const std::vector<std::string> &libPaths,
|
||||||
const std::string &outputPath) override;
|
const std::string &outputPath) override;
|
||||||
@ -665,8 +665,6 @@ class HttpAppFrameworkImpl final : public HttpAppFramework
|
|||||||
std::function<void(int)> cb) override;
|
std::function<void(int)> cb) override;
|
||||||
HttpAppFramework &setAfterAcceptSockOptCallback(
|
HttpAppFramework &setAfterAcceptSockOptCallback(
|
||||||
std::function<void(int)> cb) override;
|
std::function<void(int)> cb) override;
|
||||||
HttpAppFramework &setConnectionCallback(
|
|
||||||
std::function<void(const trantor::TcpConnectionPtr &)> cb) override;
|
|
||||||
|
|
||||||
HttpAppFramework &enableRequestStream(bool enable) override;
|
HttpAppFramework &enableRequestStream(bool enable) override;
|
||||||
bool isRequestStreamEnabled() const override;
|
bool isRequestStreamEnabled() const override;
|
||||||
@ -709,7 +707,7 @@ class HttpAppFrameworkImpl final : public HttpAppFramework
|
|||||||
size_t threadNum_{1};
|
size_t threadNum_{1};
|
||||||
std::unique_ptr<trantor::EventLoopThreadPool> ioLoopThreadPool_;
|
std::unique_ptr<trantor::EventLoopThreadPool> ioLoopThreadPool_;
|
||||||
|
|
||||||
#if !defined(_WIN32) && !TARGET_OS_IOS
|
#ifndef _WIN32
|
||||||
std::vector<std::string> libFilePaths_;
|
std::vector<std::string> libFilePaths_;
|
||||||
std::string libFileOutputPath_;
|
std::string libFileOutputPath_;
|
||||||
std::unique_ptr<SharedLibManager> sharedLibManagerPtr_;
|
std::unique_ptr<SharedLibManager> sharedLibManagerPtr_;
|
||||||
|
@ -96,15 +96,13 @@ void HttpRequestImpl::parseParameters() const
|
|||||||
while (cpos < key.length() &&
|
while (cpos < key.length() &&
|
||||||
isspace(static_cast<unsigned char>(key[cpos])))
|
isspace(static_cast<unsigned char>(key[cpos])))
|
||||||
++cpos;
|
++cpos;
|
||||||
key.remove_prefix(cpos);
|
key = key.substr(cpos);
|
||||||
auto pvalue = coo.substr(epos + 1);
|
auto pvalue = coo.substr(epos + 1);
|
||||||
parameters_[utils::urlDecode(key)] = utils::urlDecode(pvalue);
|
std::string pdecode = utils::urlDecode(pvalue);
|
||||||
|
std::string keydecode = utils::urlDecode(key);
|
||||||
|
parameters_[keydecode] = pdecode;
|
||||||
}
|
}
|
||||||
else
|
value = value.substr(pos + 1);
|
||||||
{
|
|
||||||
parameters_[utils::urlDecode(coo)];
|
|
||||||
}
|
|
||||||
value.remove_prefix(pos + 1);
|
|
||||||
}
|
}
|
||||||
if (value.length() > 0)
|
if (value.length() > 0)
|
||||||
{
|
{
|
||||||
@ -117,13 +115,11 @@ void HttpRequestImpl::parseParameters() const
|
|||||||
while (cpos < key.length() &&
|
while (cpos < key.length() &&
|
||||||
isspace(static_cast<unsigned char>(key[cpos])))
|
isspace(static_cast<unsigned char>(key[cpos])))
|
||||||
++cpos;
|
++cpos;
|
||||||
key.remove_prefix(cpos);
|
key = key.substr(cpos);
|
||||||
auto pvalue = coo.substr(epos + 1);
|
auto pvalue = coo.substr(epos + 1);
|
||||||
parameters_[utils::urlDecode(key)] = utils::urlDecode(pvalue);
|
std::string pdecode = utils::urlDecode(pvalue);
|
||||||
}
|
std::string keydecode = utils::urlDecode(key);
|
||||||
else
|
parameters_[keydecode] = pdecode;
|
||||||
{
|
|
||||||
parameters_[utils::urlDecode(coo)];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -157,15 +153,13 @@ void HttpRequestImpl::parseParameters() const
|
|||||||
while (cpos < key.length() &&
|
while (cpos < key.length() &&
|
||||||
isspace(static_cast<unsigned char>(key[cpos])))
|
isspace(static_cast<unsigned char>(key[cpos])))
|
||||||
++cpos;
|
++cpos;
|
||||||
key.remove_prefix(cpos);
|
key = key.substr(cpos);
|
||||||
auto pvalue = coo.substr(epos + 1);
|
auto pvalue = coo.substr(epos + 1);
|
||||||
parameters_[utils::urlDecode(key)] = utils::urlDecode(pvalue);
|
std::string pdecode = utils::urlDecode(pvalue);
|
||||||
|
std::string keydecode = utils::urlDecode(key);
|
||||||
|
parameters_[keydecode] = pdecode;
|
||||||
}
|
}
|
||||||
else
|
value = value.substr(pos + 1);
|
||||||
{
|
|
||||||
parameters_[utils::urlDecode(coo)];
|
|
||||||
}
|
|
||||||
value.remove_prefix(pos + 1);
|
|
||||||
}
|
}
|
||||||
if (value.length() > 0)
|
if (value.length() > 0)
|
||||||
{
|
{
|
||||||
@ -178,13 +172,11 @@ void HttpRequestImpl::parseParameters() const
|
|||||||
while (cpos < key.length() &&
|
while (cpos < key.length() &&
|
||||||
isspace(static_cast<unsigned char>(key[cpos])))
|
isspace(static_cast<unsigned char>(key[cpos])))
|
||||||
++cpos;
|
++cpos;
|
||||||
key.remove_prefix(cpos);
|
key = key.substr(cpos);
|
||||||
auto pvalue = coo.substr(epos + 1);
|
auto pvalue = coo.substr(epos + 1);
|
||||||
parameters_[utils::urlDecode(key)] = utils::urlDecode(pvalue);
|
std::string pdecode = utils::urlDecode(pvalue);
|
||||||
}
|
std::string keydecode = utils::urlDecode(key);
|
||||||
else
|
parameters_[keydecode] = pdecode;
|
||||||
{
|
|
||||||
parameters_[utils::urlDecode(coo)];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -600,7 +592,6 @@ void HttpRequestImpl::swap(HttpRequestImpl &that) noexcept
|
|||||||
swap(streamFinishCb_, that.streamFinishCb_);
|
swap(streamFinishCb_, that.streamFinishCb_);
|
||||||
swap(streamExceptionPtr_, that.streamExceptionPtr_);
|
swap(streamExceptionPtr_, that.streamExceptionPtr_);
|
||||||
swap(startProcessing_, that.startProcessing_);
|
swap(startProcessing_, that.startProcessing_);
|
||||||
swap(connPtr_, that.connPtr_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *HttpRequestImpl::versionString() const
|
const char *HttpRequestImpl::versionString() const
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
#include "HttpUtils.h"
|
#include "HttpUtils.h"
|
||||||
#include "CacheFile.h"
|
#include "CacheFile.h"
|
||||||
#include "impl_forwards.h"
|
|
||||||
#include <drogon/utils/Utilities.h>
|
#include <drogon/utils/Utilities.h>
|
||||||
#include <drogon/HttpRequest.h>
|
#include <drogon/HttpRequest.h>
|
||||||
#include <drogon/RequestStream.h>
|
#include <drogon/RequestStream.h>
|
||||||
@ -27,12 +26,9 @@
|
|||||||
#include <trantor/utils/Logger.h>
|
#include <trantor/utils/Logger.h>
|
||||||
#include <trantor/utils/MsgBuffer.h>
|
#include <trantor/utils/MsgBuffer.h>
|
||||||
#include <trantor/utils/NonCopyable.h>
|
#include <trantor/utils/NonCopyable.h>
|
||||||
#include <trantor/net/TcpConnection.h>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <future>
|
#include <thread>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -101,7 +97,6 @@ class HttpRequestImpl : public HttpRequest
|
|||||||
streamFinishCb_ = nullptr;
|
streamFinishCb_ = nullptr;
|
||||||
streamExceptionPtr_ = nullptr;
|
streamExceptionPtr_ = nullptr;
|
||||||
startProcessing_ = false;
|
startProcessing_ = false;
|
||||||
connPtr_.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trantor::EventLoop *getLoop()
|
trantor::EventLoop *getLoop()
|
||||||
@ -331,11 +326,6 @@ class HttpRequestImpl : public HttpRequest
|
|||||||
peerCertificate_ = cert;
|
peerCertificate_ = cert;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setConnectionPtr(const std::shared_ptr<trantor::TcpConnection> &ptr)
|
|
||||||
{
|
|
||||||
connPtr_ = ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addHeader(const char *start, const char *colon, const char *end);
|
void addHeader(const char *start, const char *colon, const char *end);
|
||||||
|
|
||||||
void removeHeader(std::string key) override
|
void removeHeader(std::string key) override
|
||||||
@ -564,21 +554,6 @@ class HttpRequestImpl : public HttpRequest
|
|||||||
return keepAlive_;
|
return keepAlive_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool connected() const noexcept override
|
|
||||||
{
|
|
||||||
if (auto conn = connPtr_.lock())
|
|
||||||
{
|
|
||||||
return conn->connected();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::weak_ptr<trantor::TcpConnection> &getConnectionPtr()
|
|
||||||
const noexcept override
|
|
||||||
{
|
|
||||||
return connPtr_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isOnSecureConnection() const noexcept override
|
bool isOnSecureConnection() const noexcept override
|
||||||
{
|
{
|
||||||
return isOnSecureConnection_;
|
return isOnSecureConnection_;
|
||||||
@ -698,9 +673,6 @@ class HttpRequestImpl : public HttpRequest
|
|||||||
HttpMethod previousMethod_{Invalid};
|
HttpMethod previousMethod_{Invalid};
|
||||||
Version version_{Version::kUnknown};
|
Version version_{Version::kUnknown};
|
||||||
std::string path_;
|
std::string path_;
|
||||||
/// Contains the encoded `path_` if and only if `path_` is set in encoded
|
|
||||||
/// form. If path is in a normal form and needed no decoding, then this will
|
|
||||||
/// be empty, as we do not need to store a duplicate.
|
|
||||||
std::string originalPath_;
|
std::string originalPath_;
|
||||||
bool pathEncode_{true};
|
bool pathEncode_{true};
|
||||||
std::string_view matchedPathPattern_{""};
|
std::string_view matchedPathPattern_{""};
|
||||||
@ -730,7 +702,6 @@ class HttpRequestImpl : public HttpRequest
|
|||||||
RequestStreamReaderPtr streamReaderPtr_;
|
RequestStreamReaderPtr streamReaderPtr_;
|
||||||
std::exception_ptr streamExceptionPtr_;
|
std::exception_ptr streamExceptionPtr_;
|
||||||
bool startProcessing_{false};
|
bool startProcessing_{false};
|
||||||
std::weak_ptr<trantor::TcpConnection> connPtr_;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::string content_;
|
std::string content_;
|
||||||
|
@ -516,7 +516,6 @@ void HttpResponseImpl::makeHeaderString(trantor::MsgBuffer &buffer)
|
|||||||
statusCode_);
|
statusCode_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.hasWritten(len);
|
buffer.hasWritten(len);
|
||||||
|
|
||||||
if (!statusMessage_.empty())
|
if (!statusMessage_.empty())
|
||||||
@ -526,18 +525,7 @@ void HttpResponseImpl::makeHeaderString(trantor::MsgBuffer &buffer)
|
|||||||
if (!passThrough_)
|
if (!passThrough_)
|
||||||
{
|
{
|
||||||
buffer.ensureWritableBytes(64);
|
buffer.ensureWritableBytes(64);
|
||||||
if (!contentLengthIsAllowed())
|
if (streamCallback_ || asyncStreamCallback_)
|
||||||
{
|
|
||||||
len = 0;
|
|
||||||
if ((bodyPtr_ && bodyPtr_->length() > 0) ||
|
|
||||||
!sendfileName_.empty() || streamCallback_ ||
|
|
||||||
asyncStreamCallback_)
|
|
||||||
{
|
|
||||||
LOG_ERROR << "The body should be empty when the content-length "
|
|
||||||
"is not allowed!";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (streamCallback_ || asyncStreamCallback_)
|
|
||||||
{
|
{
|
||||||
// When the headers are created, it is time to set the transfer
|
// When the headers are created, it is time to set the transfer
|
||||||
// encoding to chunked if the contents size is not specified
|
// encoding to chunked if the contents size is not specified
|
||||||
@ -632,14 +620,15 @@ void HttpResponseImpl::renderToBuffer(trantor::MsgBuffer &buffer)
|
|||||||
drogon::HttpAppFrameworkImpl::instance().sendDateHeader())
|
drogon::HttpAppFrameworkImpl::instance().sendDateHeader())
|
||||||
{
|
{
|
||||||
buffer.append("date: ");
|
buffer.append("date: ");
|
||||||
buffer.append(utils::getHttpFullDateStr(trantor::Date::date()));
|
buffer.append(utils::getHttpFullDate(trantor::Date::date()),
|
||||||
|
httpFullDateStringLength);
|
||||||
buffer.append("\r\n\r\n");
|
buffer.append("\r\n\r\n");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
buffer.append("\r\n");
|
buffer.append("\r\n");
|
||||||
}
|
}
|
||||||
if (bodyPtr_ && contentLengthIsAllowed())
|
if (bodyPtr_)
|
||||||
buffer.append(bodyPtr_->data(), bodyPtr_->length());
|
buffer.append(bodyPtr_->data(), bodyPtr_->length());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -705,7 +694,8 @@ std::shared_ptr<trantor::MsgBuffer> HttpResponseImpl::renderToBuffer()
|
|||||||
{
|
{
|
||||||
httpString->append("date: ");
|
httpString->append("date: ");
|
||||||
auto datePos = httpString->readableBytes();
|
auto datePos = httpString->readableBytes();
|
||||||
httpString->append(utils::getHttpFullDateStr(trantor::Date::date()));
|
httpString->append(utils::getHttpFullDate(trantor::Date::date()),
|
||||||
|
httpFullDateStringLength);
|
||||||
httpString->append("\r\n\r\n");
|
httpString->append("\r\n\r\n");
|
||||||
datePos_ = datePos;
|
datePos_ = datePos;
|
||||||
}
|
}
|
||||||
@ -967,8 +957,7 @@ bool HttpResponseImpl::shouldBeCompressed() const
|
|||||||
{
|
{
|
||||||
if (streamCallback_ || asyncStreamCallback_ || !sendfileName_.empty() ||
|
if (streamCallback_ || asyncStreamCallback_ || !sendfileName_.empty() ||
|
||||||
contentType() >= CT_APPLICATION_OCTET_STREAM ||
|
contentType() >= CT_APPLICATION_OCTET_STREAM ||
|
||||||
getBody().length() < 1024 ||
|
getBody().length() < 1024 || !(getHeaderBy("content-encoding").empty()))
|
||||||
!(getHeaderBy("content-encoding").empty()) || !contentLengthIsAllowed())
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -402,16 +402,6 @@ class DROGON_EXPORT HttpResponseImpl : public HttpResponse
|
|||||||
addHeader("content-length", std::to_string(bodyPtr_->length()));
|
addHeader("content-length", std::to_string(bodyPtr_->length()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool contentLengthIsAllowed() const
|
|
||||||
{
|
|
||||||
int statusCode =
|
|
||||||
customStatusCode_ >= 0 ? customStatusCode_ : statusCode_;
|
|
||||||
|
|
||||||
// return false if status code is 1xx or 204
|
|
||||||
return (statusCode >= k200OK || statusCode < k100Continue) &&
|
|
||||||
statusCode != k204NoContent;
|
|
||||||
}
|
|
||||||
#ifdef USE_BROTLI
|
#ifdef USE_BROTLI
|
||||||
void brDecompress()
|
void brDecompress()
|
||||||
{
|
{
|
||||||
|
@ -30,7 +30,6 @@
|
|||||||
#include "HttpControllersRouter.h"
|
#include "HttpControllersRouter.h"
|
||||||
#include "StaticFileRouter.h"
|
#include "StaticFileRouter.h"
|
||||||
#include "WebSocketConnectionImpl.h"
|
#include "WebSocketConnectionImpl.h"
|
||||||
#include "impl_forwards.h"
|
|
||||||
|
|
||||||
#if COZ_PROFILING
|
#if COZ_PROFILING
|
||||||
#include <coz.h>
|
#include <coz.h>
|
||||||
@ -76,12 +75,7 @@ HttpServer::HttpServer(EventLoop *loop,
|
|||||||
: server_(loop, listenAddr, std::move(name), true, app().reusePort())
|
: server_(loop, listenAddr, std::move(name), true, app().reusePort())
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
server_.setConnectionCallback(
|
server_.setConnectionCallback(onConnection);
|
||||||
[this](const trantor::TcpConnectionPtr &conn) {
|
|
||||||
onConnection(conn);
|
|
||||||
if (connectionCallback_)
|
|
||||||
connectionCallback_(conn);
|
|
||||||
});
|
|
||||||
server_.setRecvMessageCallback(onMessage);
|
server_.setRecvMessageCallback(onMessage);
|
||||||
server_.kickoffIdleConnections(
|
server_.kickoffIdleConnections(
|
||||||
HttpAppFrameworkImpl::instance().getIdleConnectionTimeout());
|
HttpAppFrameworkImpl::instance().getIdleConnectionTimeout());
|
||||||
@ -226,7 +220,6 @@ void HttpServer::onMessage(const TcpConnectionPtr &conn, MsgBuffer *buf)
|
|||||||
req->setCreationDate(trantor::Date::date());
|
req->setCreationDate(trantor::Date::date());
|
||||||
req->setSecure(conn->isSSLConnection());
|
req->setSecure(conn->isSSLConnection());
|
||||||
req->setPeerCertificate(conn->peerCertificate());
|
req->setPeerCertificate(conn->peerCertificate());
|
||||||
req->setConnectionPtr(conn);
|
|
||||||
// TODO: maybe call onRequests() directly in stream mode
|
// TODO: maybe call onRequests() directly in stream mode
|
||||||
requests.push_back(req);
|
requests.push_back(req);
|
||||||
}
|
}
|
||||||
@ -313,7 +306,7 @@ void HttpServer::onRequests(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// flush response for not passing sync advice
|
// flush response for not passing sync advices
|
||||||
if (conn->connected() && !requestParser->getResponseBuffer().empty())
|
if (conn->connected() && !requestParser->getResponseBuffer().empty())
|
||||||
{
|
{
|
||||||
sendResponses(conn,
|
sendResponses(conn,
|
||||||
@ -973,8 +966,6 @@ void HttpServer::sendResponse(const TcpConnectionPtr &conn,
|
|||||||
{
|
{
|
||||||
auto httpString = respImplPtr->renderToBuffer();
|
auto httpString = respImplPtr->renderToBuffer();
|
||||||
conn->send(httpString);
|
conn->send(httpString);
|
||||||
if (!respImplPtr->contentLengthIsAllowed())
|
|
||||||
return;
|
|
||||||
auto &asyncStreamCallback = respImplPtr->asyncStreamCallback();
|
auto &asyncStreamCallback = respImplPtr->asyncStreamCallback();
|
||||||
if (asyncStreamCallback)
|
if (asyncStreamCallback)
|
||||||
{
|
{
|
||||||
@ -1055,8 +1046,6 @@ void HttpServer::sendResponses(
|
|||||||
{
|
{
|
||||||
// Not HEAD method
|
// Not HEAD method
|
||||||
respImplPtr->renderToBuffer(buffer);
|
respImplPtr->renderToBuffer(buffer);
|
||||||
if (!respImplPtr->contentLengthIsAllowed())
|
|
||||||
continue;
|
|
||||||
auto &asyncStreamCallback = respImplPtr->asyncStreamCallback();
|
auto &asyncStreamCallback = respImplPtr->asyncStreamCallback();
|
||||||
if (asyncStreamCallback)
|
if (asyncStreamCallback)
|
||||||
{
|
{
|
||||||
@ -1202,7 +1191,7 @@ static inline HttpResponsePtr tryDecompressRequest(
|
|||||||
* @brief Check request against each sync advice, generate response if request
|
* @brief Check request against each sync advice, generate response if request
|
||||||
* is rejected by any one of them.
|
* is rejected by any one of them.
|
||||||
*
|
*
|
||||||
* @return true if all sync advice are passed.
|
* @return true if all sync advices are passed.
|
||||||
* @return false if rejected by any sync advice.
|
* @return false if rejected by any sync advice.
|
||||||
*/
|
*/
|
||||||
static inline bool passSyncAdvices(
|
static inline bool passSyncAdvices(
|
||||||
|
@ -69,12 +69,6 @@ class HttpServer : trantor::NonCopyable
|
|||||||
afterAcceptSetSockOptCallback_ = std::move(cb);
|
afterAcceptSetSockOptCallback_ = std::move(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setConnectionCallback(
|
|
||||||
std::function<void(const trantor::TcpConnectionPtr &)> cb)
|
|
||||||
{
|
|
||||||
connectionCallback_ = std::move(cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class HttpInternalForwardHelper;
|
friend class HttpInternalForwardHelper;
|
||||||
|
|
||||||
@ -150,7 +144,6 @@ class HttpServer : trantor::NonCopyable
|
|||||||
|
|
||||||
std::function<void(int)> beforeListenSetSockOptCallback_;
|
std::function<void(int)> beforeListenSetSockOptCallback_;
|
||||||
std::function<void(int)> afterAcceptSetSockOptCallback_;
|
std::function<void(int)> afterAcceptSetSockOptCallback_;
|
||||||
std::function<void(const trantor::TcpConnectionPtr &)> connectionCallback_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class HttpInternalForwardHelper
|
class HttpInternalForwardHelper
|
||||||
|
@ -121,10 +121,6 @@ void ListenerManager::createListeners(
|
|||||||
serverPtr->setAfterAcceptSockOptCallback(
|
serverPtr->setAfterAcceptSockOptCallback(
|
||||||
afterAcceptSetSockOptCallback_);
|
afterAcceptSetSockOptCallback_);
|
||||||
}
|
}
|
||||||
if (connectionCallback_)
|
|
||||||
{
|
|
||||||
serverPtr->setConnectionCallback(connectionCallback_);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listener.useSSL_ && utils::supportsTls())
|
if (listener.useSSL_ && utils::supportsTls())
|
||||||
{
|
{
|
||||||
|
@ -61,12 +61,6 @@ class ListenerManager : public trantor::NonCopyable
|
|||||||
afterAcceptSetSockOptCallback_ = std::move(cb);
|
afterAcceptSetSockOptCallback_ = std::move(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setConnectionCallback(
|
|
||||||
std::function<void(const trantor::TcpConnectionPtr &)> cb)
|
|
||||||
{
|
|
||||||
connectionCallback_ = std::move(cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
void reloadSSLFiles();
|
void reloadSSLFiles();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -107,7 +101,6 @@ class ListenerManager : public trantor::NonCopyable
|
|||||||
std::unique_ptr<trantor::EventLoopThread> listeningThread_;
|
std::unique_ptr<trantor::EventLoopThread> listeningThread_;
|
||||||
std::function<void(int)> beforeListenSetSockOptCallback_;
|
std::function<void(int)> beforeListenSetSockOptCallback_;
|
||||||
std::function<void(int)> afterAcceptSetSockOptCallback_;
|
std::function<void(int)> afterAcceptSetSockOptCallback_;
|
||||||
std::function<void(const trantor::TcpConnectionPtr &)> connectionCallback_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace drogon
|
} // namespace drogon
|
||||||
|
@ -28,7 +28,6 @@ void PromExporter::initAndStart(const Json::Value &config)
|
|||||||
}
|
}
|
||||||
auto resp = HttpResponse::newHttpResponse();
|
auto resp = HttpResponse::newHttpResponse();
|
||||||
resp->setBody(thisPtr->exportMetrics());
|
resp->setBody(thisPtr->exportMetrics());
|
||||||
resp->setContentTypeCode(CT_TEXT_PLAIN);
|
|
||||||
resp->setExpiredTime(5);
|
resp->setExpiredTime(5);
|
||||||
callback(resp);
|
callback(resp);
|
||||||
},
|
},
|
||||||
@ -119,12 +118,12 @@ static std::string exportCollector(
|
|||||||
.append(collector->name())
|
.append(collector->name())
|
||||||
.append(" ")
|
.append(" ")
|
||||||
.append(collector->help())
|
.append(collector->help())
|
||||||
.append("\n");
|
.append("\r\n");
|
||||||
res.append("# TYPE ")
|
res.append("# TYPE ")
|
||||||
.append(collector->name())
|
.append(collector->name())
|
||||||
.append(" ")
|
.append(" ")
|
||||||
.append(collector->type())
|
.append(collector->type())
|
||||||
.append("\n");
|
.append("\r\n");
|
||||||
for (auto const &sampleGroup : sampleGroups)
|
for (auto const &sampleGroup : sampleGroups)
|
||||||
{
|
{
|
||||||
auto const &metricPtr = sampleGroup.metric;
|
auto const &metricPtr = sampleGroup.metric;
|
||||||
@ -158,11 +157,11 @@ static std::string exportCollector(
|
|||||||
res.append(" ")
|
res.append(" ")
|
||||||
.append(std::to_string(
|
.append(std::to_string(
|
||||||
sample.timestamp.microSecondsSinceEpoch() / 1000))
|
sample.timestamp.microSecondsSinceEpoch() / 1000))
|
||||||
.append("\n");
|
.append("\r\n");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
res.append("\n");
|
res.append("\r\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,20 +96,21 @@ void RealIpResolver::initAndStart(const Json::Value &config)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Json::Value &trustIps = config["trust_ips"];
|
const Json::Value &trustIps = config["trust_ips"];
|
||||||
if (!trustIps.isNull() && !trustIps.isArray())
|
if (!trustIps.isArray())
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Invalid trusted_ips. Should be array.");
|
throw std::runtime_error("Invalid trusted_ips. Should be array.");
|
||||||
}
|
}
|
||||||
for (const auto &ipOrCidr : trustIps)
|
for (const auto &elem : trustIps)
|
||||||
{
|
{
|
||||||
trustCIDRs_.emplace_back(ipOrCidr.asString());
|
std::string ipOrCidr = elem.asString();
|
||||||
|
trustCIDRs_.emplace_back(ipOrCidr);
|
||||||
}
|
}
|
||||||
|
|
||||||
drogon::app().registerPreRoutingAdvice([this](const HttpRequestPtr &req) {
|
drogon::app().registerPreRoutingAdvice([this](const HttpRequestPtr &req) {
|
||||||
const auto &headers = req->headers();
|
const auto &headers = req->headers();
|
||||||
auto ipHeaderFind = headers.find(fromHeader_);
|
auto ipHeaderFind = headers.find(fromHeader_);
|
||||||
const trantor::InetAddress &peerAddr = req->getPeerAddr();
|
const trantor::InetAddress &peerAddr = req->getPeerAddr();
|
||||||
if (ipHeaderFind == headers.end() || !matchCidr(peerAddr, trustCIDRs_))
|
if (ipHeaderFind == headers.end() || !matchCidr(peerAddr))
|
||||||
{
|
{
|
||||||
// Target header is empty, or
|
// Target header is empty, or
|
||||||
// direct peer is already a non-proxy
|
// direct peer is already a non-proxy
|
||||||
@ -138,7 +139,7 @@ void RealIpResolver::initAndStart(const Json::Value &config)
|
|||||||
while (!(ip = parser.getNext()).empty())
|
while (!(ip = parser.getNext()).empty())
|
||||||
{
|
{
|
||||||
trantor::InetAddress addr = parseAddress(ip);
|
trantor::InetAddress addr = parseAddress(ip);
|
||||||
if (addr.isUnspecified() || matchCidr(addr, trustCIDRs_))
|
if (addr.isUnspecified() || matchCidr(addr))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -176,10 +177,9 @@ const trantor::InetAddress &RealIpResolver::getRealAddr(
|
|||||||
return attributesPtr->get<trantor::InetAddress>(attributeKey_);
|
return attributesPtr->get<trantor::InetAddress>(attributeKey_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RealIpResolver::matchCidr(const trantor::InetAddress &addr,
|
bool RealIpResolver::matchCidr(const trantor::InetAddress &addr) const
|
||||||
const CIDRs &trustCIDRs)
|
|
||||||
{
|
{
|
||||||
for (const auto &cidr : trustCIDRs)
|
for (auto &cidr : trustCIDRs_)
|
||||||
{
|
{
|
||||||
if ((addr.ipNetEndian() & cidr.mask_) == cidr.addr_)
|
if ((addr.ipNetEndian() & cidr.mask_) == cidr.addr_)
|
||||||
{
|
{
|
||||||
|
@ -30,10 +30,6 @@ void Redirector::initAndStart(const Json::Value &config)
|
|||||||
}
|
}
|
||||||
std::string protocol, host;
|
std::string protocol, host;
|
||||||
bool pathChanged{false};
|
bool pathChanged{false};
|
||||||
for (auto &handler : thisPtr->pathRewriteHandlers_)
|
|
||||||
{
|
|
||||||
pathChanged |= handler(req);
|
|
||||||
}
|
|
||||||
for (auto &handler : thisPtr->handlers_)
|
for (auto &handler : thisPtr->handlers_)
|
||||||
{
|
{
|
||||||
if (!handler(req, protocol, host, pathChanged))
|
if (!handler(req, protocol, host, pathChanged))
|
||||||
@ -41,6 +37,10 @@ void Redirector::initAndStart(const Json::Value &config)
|
|||||||
return HttpResponse::newNotFoundResponse(req);
|
return HttpResponse::newNotFoundResponse(req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (auto &handler : thisPtr->pathRewriteHandlers_)
|
||||||
|
{
|
||||||
|
pathChanged |= handler(req);
|
||||||
|
}
|
||||||
if (!protocol.empty() || !host.empty() || pathChanged)
|
if (!protocol.empty() || !host.empty() || pathChanged)
|
||||||
{
|
{
|
||||||
std::string url;
|
std::string url;
|
||||||
|
@ -44,7 +44,7 @@ void StaticFileRouter::init(const std::vector<trantor::EventLoop *> &ioLoops)
|
|||||||
size_t i) {
|
size_t i) {
|
||||||
assert(i == ioLoops[i]->index());
|
assert(i == ioLoops[i]->index());
|
||||||
mapPtr = std::make_unique<CacheMap<std::string, char>>(ioLoops[i],
|
mapPtr = std::make_unique<CacheMap<std::string, char>>(ioLoops[i],
|
||||||
1.0f,
|
1.0,
|
||||||
4,
|
4,
|
||||||
50);
|
50);
|
||||||
});
|
});
|
||||||
|
@ -33,7 +33,6 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <random>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <locale>
|
#include <locale>
|
||||||
@ -84,12 +83,12 @@ namespace drogon
|
|||||||
{
|
{
|
||||||
namespace utils
|
namespace utils
|
||||||
{
|
{
|
||||||
static constexpr std::string_view base64Chars =
|
static const std::string base64Chars =
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
"abcdefghijklmnopqrstuvwxyz"
|
"abcdefghijklmnopqrstuvwxyz"
|
||||||
"0123456789+/";
|
"0123456789+/";
|
||||||
|
|
||||||
static constexpr std::string_view urlBase64Chars =
|
static const std::string urlBase64Chars =
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
"abcdefghijklmnopqrstuvwxyz"
|
"abcdefghijklmnopqrstuvwxyz"
|
||||||
"0123456789-_";
|
"0123456789-_";
|
||||||
@ -163,16 +162,28 @@ bool isBase64(std::string_view str)
|
|||||||
|
|
||||||
std::string genRandomString(int length)
|
std::string genRandomString(int length)
|
||||||
{
|
{
|
||||||
static const std::string_view char_space =
|
static const char char_space[] =
|
||||||
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
std::uniform_int_distribution<size_t> dist(0, char_space.size() - 1);
|
static std::once_flag once;
|
||||||
thread_local std::mt19937 rng(std::random_device{}());
|
static const size_t len = strlen(char_space);
|
||||||
|
static const int randMax = RAND_MAX - (RAND_MAX % len);
|
||||||
|
std::call_once(once, []() {
|
||||||
|
std::srand(static_cast<unsigned int>(time(nullptr)));
|
||||||
|
});
|
||||||
|
|
||||||
|
int i;
|
||||||
std::string str;
|
std::string str;
|
||||||
str.resize(length);
|
str.resize(length);
|
||||||
for (char &ch : str)
|
|
||||||
|
for (i = 0; i < length; ++i)
|
||||||
{
|
{
|
||||||
ch = char_space[dist(rng)];
|
int x = std::rand();
|
||||||
|
while (x >= randMax)
|
||||||
|
{
|
||||||
|
x = std::rand();
|
||||||
|
}
|
||||||
|
x = (x % len);
|
||||||
|
str[i] = char_space[x];
|
||||||
}
|
}
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
@ -429,33 +440,33 @@ std::string getUuid(bool lowercase)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void base64Encode(const unsigned char *bytesToEncode,
|
std::string base64Encode(const unsigned char *bytes_to_encode,
|
||||||
size_t inLen,
|
size_t in_len,
|
||||||
unsigned char *outputBuffer,
|
bool url_safe,
|
||||||
bool urlSafe,
|
bool padded)
|
||||||
bool padded)
|
|
||||||
{
|
{
|
||||||
|
std::string ret;
|
||||||
|
ret.reserve(base64EncodedLength(in_len, padded));
|
||||||
int i = 0;
|
int i = 0;
|
||||||
unsigned char charArray3[3];
|
unsigned char char_array_3[3];
|
||||||
unsigned char charArray4[4];
|
unsigned char char_array_4[4];
|
||||||
|
|
||||||
const std::string_view charSet = urlSafe ? urlBase64Chars : base64Chars;
|
const std::string &charSet = url_safe ? urlBase64Chars : base64Chars;
|
||||||
|
|
||||||
size_t a = 0;
|
while (in_len--)
|
||||||
while (inLen--)
|
|
||||||
{
|
{
|
||||||
charArray3[i++] = *(bytesToEncode++);
|
char_array_3[i++] = *(bytes_to_encode++);
|
||||||
if (i == 3)
|
if (i == 3)
|
||||||
{
|
{
|
||||||
charArray4[0] = (charArray3[0] & 0xfc) >> 2;
|
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||||
charArray4[1] =
|
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) +
|
||||||
((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
|
((char_array_3[1] & 0xf0) >> 4);
|
||||||
charArray4[2] =
|
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) +
|
||||||
((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
|
((char_array_3[2] & 0xc0) >> 6);
|
||||||
charArray4[3] = charArray3[2] & 0x3f;
|
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||||
|
|
||||||
for (i = 0; (i < 4); ++i, ++a)
|
for (i = 0; (i < 4); ++i)
|
||||||
outputBuffer[a] = charSet[charArray4[i]];
|
ret += charSet[char_array_4[i]];
|
||||||
i = 0;
|
i = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -463,61 +474,59 @@ void base64Encode(const unsigned char *bytesToEncode,
|
|||||||
if (i)
|
if (i)
|
||||||
{
|
{
|
||||||
for (int j = i; j < 3; ++j)
|
for (int j = i; j < 3; ++j)
|
||||||
charArray3[j] = '\0';
|
char_array_3[j] = '\0';
|
||||||
|
|
||||||
charArray4[0] = (charArray3[0] & 0xfc) >> 2;
|
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||||
charArray4[1] =
|
char_array_4[1] =
|
||||||
((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
|
((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||||
charArray4[2] =
|
char_array_4[2] =
|
||||||
((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
|
((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||||
charArray4[3] = charArray3[2] & 0x3f;
|
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||||
|
|
||||||
for (int j = 0; (j <= i); ++j, ++a)
|
for (int j = 0; (j <= i); ++j)
|
||||||
outputBuffer[a] = charSet[charArray4[j]];
|
ret += charSet[char_array_4[j]];
|
||||||
|
|
||||||
if (padded)
|
if (padded)
|
||||||
while ((++i < 4))
|
while ((++i < 4))
|
||||||
{
|
ret += '=';
|
||||||
outputBuffer[a] = '=';
|
|
||||||
++a;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<char> base64DecodeToVector(std::string_view encodedString)
|
std::vector<char> base64DecodeToVector(std::string_view encoded_string)
|
||||||
{
|
{
|
||||||
auto inLen = encodedString.size();
|
auto in_len = encoded_string.size();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int in_{0};
|
int in_{0};
|
||||||
char charArray4[4], charArray3[3];
|
char char_array_4[4], char_array_3[3];
|
||||||
std::vector<char> ret;
|
std::vector<char> ret;
|
||||||
ret.reserve(base64DecodedLength(inLen));
|
ret.reserve(base64DecodedLength(in_len));
|
||||||
|
|
||||||
while (inLen-- && (encodedString[in_] != '='))
|
while (in_len-- && (encoded_string[in_] != '='))
|
||||||
{
|
{
|
||||||
if (!isBase64(encodedString[in_]))
|
if (!isBase64(encoded_string[in_]))
|
||||||
{
|
{
|
||||||
++in_;
|
++in_;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
charArray4[i++] = encodedString[in_];
|
char_array_4[i++] = encoded_string[in_];
|
||||||
++in_;
|
++in_;
|
||||||
if (i == 4)
|
if (i == 4)
|
||||||
{
|
{
|
||||||
for (i = 0; i < 4; ++i)
|
for (i = 0; i < 4; ++i)
|
||||||
{
|
{
|
||||||
charArray4[i] = base64CharMap.getIndex(charArray4[i]);
|
char_array_4[i] = base64CharMap.getIndex(char_array_4[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
charArray3[0] =
|
char_array_3[0] =
|
||||||
(charArray4[0] << 2) + ((charArray4[1] & 0x30) >> 4);
|
(char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||||
charArray3[1] =
|
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) +
|
||||||
((charArray4[1] & 0xf) << 4) + ((charArray4[2] & 0x3c) >> 2);
|
((char_array_4[2] & 0x3c) >> 2);
|
||||||
charArray3[2] = ((charArray4[2] & 0x3) << 6) + charArray4[3];
|
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||||
|
|
||||||
for (i = 0; (i < 3); ++i)
|
for (i = 0; (i < 3); ++i)
|
||||||
ret.push_back(charArray3[i]);
|
ret.push_back(char_array_3[i]);
|
||||||
i = 0;
|
i = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -525,59 +534,60 @@ std::vector<char> base64DecodeToVector(std::string_view encodedString)
|
|||||||
if (i)
|
if (i)
|
||||||
{
|
{
|
||||||
for (int j = i; j < 4; ++j)
|
for (int j = i; j < 4; ++j)
|
||||||
charArray4[j] = 0;
|
char_array_4[j] = 0;
|
||||||
|
|
||||||
for (int j = 0; j < 4; ++j)
|
for (int j = 0; j < 4; ++j)
|
||||||
{
|
{
|
||||||
charArray4[j] = base64CharMap.getIndex(charArray4[j]);
|
char_array_4[j] = base64CharMap.getIndex(char_array_4[j]);
|
||||||
}
|
}
|
||||||
|
|
||||||
charArray3[0] = (charArray4[0] << 2) + ((charArray4[1] & 0x30) >> 4);
|
char_array_3[0] =
|
||||||
charArray3[1] =
|
(char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||||
((charArray4[1] & 0xf) << 4) + ((charArray4[2] & 0x3c) >> 2);
|
char_array_3[1] =
|
||||||
charArray3[2] = ((charArray4[2] & 0x3) << 6) + charArray4[3];
|
((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||||
|
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||||
|
|
||||||
--i;
|
--i;
|
||||||
for (int j = 0; (j < i); ++j)
|
for (int j = 0; (j < i); ++j)
|
||||||
ret.push_back(charArray3[j]);
|
ret.push_back(char_array_3[j]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t base64Decode(const char *encodedString,
|
std::string base64Decode(std::string_view encoded_string)
|
||||||
size_t inLen,
|
|
||||||
unsigned char *outputBuffer)
|
|
||||||
{
|
{
|
||||||
|
auto in_len = encoded_string.size();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int in_{0};
|
int in_{0};
|
||||||
unsigned char charArray4[4], charArray3[3];
|
unsigned char char_array_4[4], char_array_3[3];
|
||||||
|
std::string ret;
|
||||||
|
ret.reserve(base64DecodedLength(in_len));
|
||||||
|
|
||||||
size_t a = 0;
|
while (in_len-- && (encoded_string[in_] != '='))
|
||||||
while (inLen-- && (encodedString[in_] != '='))
|
|
||||||
{
|
{
|
||||||
if (!isBase64(encodedString[in_]))
|
if (!isBase64(encoded_string[in_]))
|
||||||
{
|
{
|
||||||
++in_;
|
++in_;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
charArray4[i++] = encodedString[in_];
|
char_array_4[i++] = encoded_string[in_];
|
||||||
++in_;
|
++in_;
|
||||||
if (i == 4)
|
if (i == 4)
|
||||||
{
|
{
|
||||||
for (i = 0; i < 4; ++i)
|
for (i = 0; i < 4; ++i)
|
||||||
{
|
{
|
||||||
charArray4[i] = base64CharMap.getIndex(charArray4[i]);
|
char_array_4[i] = base64CharMap.getIndex(char_array_4[i]);
|
||||||
}
|
}
|
||||||
charArray3[0] =
|
char_array_3[0] =
|
||||||
(charArray4[0] << 2) + ((charArray4[1] & 0x30) >> 4);
|
(char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||||
charArray3[1] =
|
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) +
|
||||||
((charArray4[1] & 0xf) << 4) + ((charArray4[2] & 0x3c) >> 2);
|
((char_array_4[2] & 0x3c) >> 2);
|
||||||
charArray3[2] = ((charArray4[2] & 0x3) << 6) + charArray4[3];
|
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||||
|
|
||||||
for (i = 0; (i < 3); ++i, ++a)
|
for (i = 0; (i < 3); ++i)
|
||||||
outputBuffer[a] = charArray3[i];
|
ret += char_array_3[i];
|
||||||
i = 0;
|
i = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -585,24 +595,25 @@ size_t base64Decode(const char *encodedString,
|
|||||||
if (i)
|
if (i)
|
||||||
{
|
{
|
||||||
for (int j = i; j < 4; ++j)
|
for (int j = i; j < 4; ++j)
|
||||||
charArray4[j] = 0;
|
char_array_4[j] = 0;
|
||||||
|
|
||||||
for (int j = 0; j < 4; ++j)
|
for (int j = 0; j < 4; ++j)
|
||||||
{
|
{
|
||||||
charArray4[j] = base64CharMap.getIndex(charArray4[j]);
|
char_array_4[j] = base64CharMap.getIndex(char_array_4[j]);
|
||||||
}
|
}
|
||||||
|
|
||||||
charArray3[0] = (charArray4[0] << 2) + ((charArray4[1] & 0x30) >> 4);
|
char_array_3[0] =
|
||||||
charArray3[1] =
|
(char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||||
((charArray4[1] & 0xf) << 4) + ((charArray4[2] & 0x3c) >> 2);
|
char_array_3[1] =
|
||||||
charArray3[2] = ((charArray4[2] & 0x3) << 6) + charArray4[3];
|
((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||||
|
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||||
|
|
||||||
--i;
|
--i;
|
||||||
for (int j = 0; (j < i); ++j, ++a)
|
for (int j = 0; (j < i); ++j)
|
||||||
outputBuffer[a] = charArray3[j];
|
ret += char_array_3[j];
|
||||||
}
|
}
|
||||||
|
|
||||||
return a;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string charToHex(char c)
|
static std::string charToHex(char c)
|
||||||
@ -1034,35 +1045,6 @@ char *getHttpFullDate(const trantor::Date &date)
|
|||||||
return lastTimeString;
|
return lastTimeString;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dateToCustomFormattedString(const std::string &fmtStr,
|
|
||||||
std::string &str,
|
|
||||||
const trantor::Date &date)
|
|
||||||
{
|
|
||||||
auto nowSecond = date.microSecondsSinceEpoch() / MICRO_SECONDS_PRE_SEC;
|
|
||||||
time_t seconds = static_cast<time_t>(nowSecond);
|
|
||||||
struct tm tm_LValue = date.tmStruct();
|
|
||||||
std::stringstream Out;
|
|
||||||
Out.imbue(std::locale{"C"});
|
|
||||||
Out << std::put_time(&tm_LValue, fmtStr.c_str());
|
|
||||||
str = Out.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string &getHttpFullDateStr(const trantor::Date &date)
|
|
||||||
{
|
|
||||||
static thread_local int64_t lastSecond = 0;
|
|
||||||
static thread_local std::string lastTimeString(128, 0);
|
|
||||||
auto nowSecond = date.microSecondsSinceEpoch() / MICRO_SECONDS_PRE_SEC;
|
|
||||||
if (nowSecond == lastSecond)
|
|
||||||
{
|
|
||||||
return lastTimeString;
|
|
||||||
}
|
|
||||||
lastSecond = nowSecond;
|
|
||||||
dateToCustomFormattedString("%a, %d %b %Y %H:%M:%S GMT",
|
|
||||||
lastTimeString,
|
|
||||||
date);
|
|
||||||
return lastTimeString;
|
|
||||||
}
|
|
||||||
|
|
||||||
trantor::Date getHttpDate(const std::string &httpFullDateString)
|
trantor::Date getHttpDate(const std::string &httpFullDateString)
|
||||||
{
|
{
|
||||||
static const std::array<const char *, 4> formats = {
|
static const std::array<const char *, 4> formats = {
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
#include <json/value.h>
|
#include <json/value.h>
|
||||||
#include <json/writer.h>
|
#include <json/writer.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
using namespace drogon;
|
using namespace drogon;
|
||||||
|
|
||||||
@ -269,14 +268,14 @@ bool WebSocketMessageParser::parse(trantor::MsgBuffer *buffer)
|
|||||||
{
|
{
|
||||||
// According to the rfc6455
|
// According to the rfc6455
|
||||||
gotAll_ = false;
|
gotAll_ = false;
|
||||||
while (buffer->readableBytes() >= 2)
|
if (buffer->readableBytes() >= 2)
|
||||||
{
|
{
|
||||||
unsigned char opcode = (*buffer)[0] & 0x0f;
|
unsigned char opcode = (*buffer)[0] & 0x0f;
|
||||||
bool isControlFrame = false;
|
bool isControlFrame = false;
|
||||||
switch (opcode)
|
switch (opcode)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
LOG_TRACE << "continuation frame";
|
// continuation frame
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
type_ = WebSocketMessageType::Text;
|
type_ = WebSocketMessageType::Text;
|
||||||
@ -328,13 +327,8 @@ bool WebSocketMessageParser::parse(trantor::MsgBuffer *buffer)
|
|||||||
{
|
{
|
||||||
indexFirstMask = 10;
|
indexFirstMask = 10;
|
||||||
}
|
}
|
||||||
if (indexFirstMask > 2)
|
if (indexFirstMask > 2 && buffer->readableBytes() >= indexFirstMask)
|
||||||
{
|
{
|
||||||
if (buffer->readableBytes() < indexFirstMask)
|
|
||||||
{
|
|
||||||
// Not enough data yet, wait for more.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (isControlFrame)
|
if (isControlFrame)
|
||||||
{
|
{
|
||||||
// rfc6455-5.5
|
// rfc6455-5.5
|
||||||
@ -350,17 +344,14 @@ bool WebSocketMessageParser::parse(trantor::MsgBuffer *buffer)
|
|||||||
}
|
}
|
||||||
else if (indexFirstMask == 10)
|
else if (indexFirstMask == 10)
|
||||||
{
|
{
|
||||||
length = 0;
|
length = (unsigned char)(*buffer)[2];
|
||||||
for (int i = 2; i <= 9; ++i)
|
length = (length << 8) + (unsigned char)(*buffer)[3];
|
||||||
{
|
length = (length << 8) + (unsigned char)(*buffer)[4];
|
||||||
if (length > ((std::numeric_limits<size_t>::max)() >> 8))
|
length = (length << 8) + (unsigned char)(*buffer)[5];
|
||||||
{
|
length = (length << 8) + (unsigned char)(*buffer)[6];
|
||||||
LOG_ERROR
|
length = (length << 8) + (unsigned char)(*buffer)[7];
|
||||||
<< "Payload length too large to handle safely";
|
length = (length << 8) + (unsigned char)(*buffer)[8];
|
||||||
return false;
|
length = (length << 8) + (unsigned char)(*buffer)[9];
|
||||||
}
|
|
||||||
length = (length << 8) + (unsigned char)(*buffer)[i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -389,16 +380,9 @@ bool WebSocketMessageParser::parse(trantor::MsgBuffer *buffer)
|
|||||||
{
|
{
|
||||||
message_[oldLen + i] = (rawData[i] ^ masks[i % 4]);
|
message_[oldLen + i] = (rawData[i] ^ masks[i % 4]);
|
||||||
}
|
}
|
||||||
buffer->retrieve(indexFirstMask + 4 + length);
|
|
||||||
if (isFin)
|
if (isFin)
|
||||||
{
|
|
||||||
gotAll_ = true;
|
gotAll_ = true;
|
||||||
return true;
|
buffer->retrieve(indexFirstMask + 4 + length);
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Not enough data yet, wait for more.
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -408,16 +392,9 @@ bool WebSocketMessageParser::parse(trantor::MsgBuffer *buffer)
|
|||||||
{
|
{
|
||||||
auto rawData = buffer->peek() + indexFirstMask;
|
auto rawData = buffer->peek() + indexFirstMask;
|
||||||
message_.append(rawData, length);
|
message_.append(rawData, length);
|
||||||
buffer->retrieve(indexFirstMask + length);
|
|
||||||
if (isFin)
|
if (isFin)
|
||||||
{
|
|
||||||
gotAll_ = true;
|
gotAll_ = true;
|
||||||
return true;
|
buffer->retrieve(indexFirstMask + length);
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Not enough data yet, wait for more.
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,8 +42,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC" AND BUILD_SHARED_LIBS)
|
|||||||
set(UNITTEST_SOURCES ${UNITTEST_SOURCES} ../src/HttpUtils.cc)
|
set(UNITTEST_SOURCES ${UNITTEST_SOURCES} ../src/HttpUtils.cc)
|
||||||
else()
|
else()
|
||||||
set(UNITTEST_SOURCES ${UNITTEST_SOURCES} ../src/HttpFileImpl.cc
|
set(UNITTEST_SOURCES ${UNITTEST_SOURCES} ../src/HttpFileImpl.cc
|
||||||
unittests/HttpFileTest.cc
|
unittests/HttpFileTest.cc)
|
||||||
unittests/WebsocketResponseTest.cc)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_executable(unittest ${UNITTEST_SOURCES})
|
add_executable(unittest ${UNITTEST_SOURCES})
|
||||||
|
@ -408,7 +408,8 @@ int main()
|
|||||||
app().registerCustomExtensionMime("md", "text/markdown");
|
app().registerCustomExtensionMime("md", "text/markdown");
|
||||||
app().setFileTypes({"md", "html", "jpg", "cc", "txt"});
|
app().setFileTypes({"md", "html", "jpg", "cc", "txt"});
|
||||||
std::cout << "Date: "
|
std::cout << "Date: "
|
||||||
<< drogon::utils::getHttpFullDateStr(trantor::Date::now())
|
<< std::string{drogon::utils::getHttpFullDate(
|
||||||
|
trantor::Date::now())}
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
|
|
||||||
app().registerBeginningAdvice(
|
app().registerBeginningAdvice(
|
||||||
|
@ -35,29 +35,4 @@ DROGON_TEST(CookieTest)
|
|||||||
drogon::Cookie::convertString2SameSite("Strict"));
|
drogon::Cookie::convertString2SameSite("Strict"));
|
||||||
CHECK(drogon::Cookie::SameSite::kNone ==
|
CHECK(drogon::Cookie::SameSite::kNone ==
|
||||||
drogon::Cookie::convertString2SameSite("None"));
|
drogon::Cookie::convertString2SameSite("None"));
|
||||||
|
|
||||||
// Test for Partitioned attribute
|
|
||||||
drogon::Cookie cookie6("test", "6");
|
|
||||||
cookie6.setPartitioned(true);
|
|
||||||
CHECK(cookie6.cookieString() ==
|
|
||||||
"Set-Cookie: test=6; Secure; HttpOnly; Partitioned\r\n");
|
|
||||||
// Test that partitioned attribute automatically sets secure
|
|
||||||
drogon::Cookie cookie7("test", "7");
|
|
||||||
cookie7.setPartitioned(true);
|
|
||||||
CHECK(cookie7.isSecure() == true);
|
|
||||||
// Test other attributes
|
|
||||||
drogon::Cookie cookie8("test", "8");
|
|
||||||
cookie8.setPartitioned(true);
|
|
||||||
cookie8.setDomain("drogon.org");
|
|
||||||
cookie8.setMaxAge(3600);
|
|
||||||
CHECK(cookie8.cookieString() ==
|
|
||||||
"Set-Cookie: test=8; Max-Age=3600; Domain=drogon.org; Secure; "
|
|
||||||
"HttpOnly; Partitioned\r\n");
|
|
||||||
// Teset Partitioned and SameSite can coexist
|
|
||||||
drogon::Cookie cookie9("test", "9");
|
|
||||||
cookie9.setPartitioned(true);
|
|
||||||
cookie9.setSameSite(drogon::Cookie::SameSite::kLax);
|
|
||||||
CHECK(
|
|
||||||
cookie9.cookieString() ==
|
|
||||||
"Set-Cookie: test=9; SameSite=Lax; Secure; HttpOnly; Partitioned\r\n");
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#include <drogon/DrObject.h>
|
#include <drogon/DrObject.h>
|
||||||
#include <drogon/drogon_test.h>
|
#include <drogon/drogon_test.h>
|
||||||
#include <drogon/HttpController.h>
|
|
||||||
|
|
||||||
using namespace drogon;
|
using namespace drogon;
|
||||||
|
|
||||||
@ -42,45 +41,3 @@ DROGON_TEST(DrObjectNamespaceTest)
|
|||||||
CHECK(objPtr2.get() != nullptr);
|
CHECK(objPtr2.get() != nullptr);
|
||||||
CHECK(objPtr == objPtr2);
|
CHECK(objPtr == objPtr2);
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestC : public DrObject<TestC>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static constexpr bool isAutoCreation = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
class TestD : public DrObject<TestD>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static constexpr bool isAutoCreation = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
class TestE : public DrObject<TestE>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static constexpr double isAutoCreation = 3.0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CtrlA : public HttpController<CtrlA>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
METHOD_LIST_BEGIN
|
|
||||||
METHOD_LIST_END
|
|
||||||
};
|
|
||||||
|
|
||||||
class CtrlB : public HttpController<CtrlB, false>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
METHOD_LIST_BEGIN
|
|
||||||
METHOD_LIST_END
|
|
||||||
};
|
|
||||||
|
|
||||||
DROGON_TEST(IsAutoCreationClassTest)
|
|
||||||
{
|
|
||||||
STATIC_REQUIRE(isAutoCreationClass<TestA>::value == false);
|
|
||||||
STATIC_REQUIRE(isAutoCreationClass<TestC>::value == true);
|
|
||||||
STATIC_REQUIRE(isAutoCreationClass<TestD>::value == false);
|
|
||||||
STATIC_REQUIRE(isAutoCreationClass<TestE>::value == false);
|
|
||||||
STATIC_REQUIRE(isAutoCreationClass<CtrlA>::value == true);
|
|
||||||
STATIC_REQUIRE(isAutoCreationClass<CtrlB>::value == false);
|
|
||||||
}
|
|
||||||
|
@ -7,7 +7,7 @@ using namespace drogon;
|
|||||||
|
|
||||||
DROGON_TEST(HttpFullDateTest)
|
DROGON_TEST(HttpFullDateTest)
|
||||||
{
|
{
|
||||||
auto str = utils::getHttpFullDateStr();
|
auto str = utils::getHttpFullDate();
|
||||||
auto date = utils::getHttpDate(str);
|
auto date = utils::getHttpDate(str);
|
||||||
CHECK(utils::getHttpFullDate(date) == str);
|
CHECK(utils::getHttpFullDate(date) == str);
|
||||||
}
|
}
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
#include <drogon/drogon_test.h>
|
|
||||||
|
|
||||||
#include "../../lib/src/HttpRequestImpl.h"
|
|
||||||
#include "../../lib/src/HttpResponseImpl.h"
|
|
||||||
#include "../../lib/src/HttpControllerBinder.h"
|
|
||||||
|
|
||||||
using namespace drogon;
|
|
||||||
|
|
||||||
DROGON_TEST(WebsocketReponseTest)
|
|
||||||
{
|
|
||||||
WebsocketControllerBinder binder;
|
|
||||||
|
|
||||||
auto reqPtr = std::make_shared<HttpRequestImpl>(nullptr);
|
|
||||||
|
|
||||||
// Value from rfc6455-1.3
|
|
||||||
reqPtr->addHeader("sec-websocket-key", "dGhlIHNhbXBsZSBub25jZQ==");
|
|
||||||
|
|
||||||
binder.handleRequest(reqPtr, [&](const HttpResponsePtr &resp) {
|
|
||||||
CHECK(resp->statusCode() == k101SwitchingProtocols);
|
|
||||||
CHECK(resp->headers().size() == 3);
|
|
||||||
CHECK(resp->getHeader("Upgrade") == "websocket");
|
|
||||||
CHECK(resp->getHeader("Connection") == "Upgrade");
|
|
||||||
|
|
||||||
// Value from rfc6455-1.3
|
|
||||||
CHECK(resp->getHeader("Sec-WebSocket-Accept") ==
|
|
||||||
"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=");
|
|
||||||
|
|
||||||
auto implPtr = std::dynamic_pointer_cast<HttpResponseImpl>(resp);
|
|
||||||
|
|
||||||
implPtr->makeHeaderString();
|
|
||||||
auto buffer = implPtr->renderToBuffer();
|
|
||||||
auto str = std::string{buffer->peek(), buffer->readableBytes()};
|
|
||||||
|
|
||||||
CHECK(str.find("upgrade: websocket") != std::string::npos);
|
|
||||||
CHECK(str.find("connection: Upgrade") != std::string::npos);
|
|
||||||
CHECK(str.find("sec-websocket-accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=") !=
|
|
||||||
std::string::npos);
|
|
||||||
CHECK(str.find("content-length:") == std::string::npos);
|
|
||||||
});
|
|
||||||
}
|
|
@ -214,28 +214,6 @@ class DROGON_EXPORT DbClient : public trantor::NonCopyable
|
|||||||
(binder << std::forward<Arguments>(args), 0)...};
|
(binder << std::forward<Arguments>(args), 0)...};
|
||||||
return internal::SqlAwaiter(std::move(binder));
|
return internal::SqlAwaiter(std::move(binder));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Execute a SQL query asynchronously using coroutine support.
|
|
||||||
* This overload accepts a vector of arguments to bind to the query.
|
|
||||||
* @tparam T The type of the elements in the vector.
|
|
||||||
* @param sql The SQL query string to execute.
|
|
||||||
* @param args A vector of arguments to bind to the query.
|
|
||||||
* @return A SqlAwaiter object that can be co_awaited to retrieve the query
|
|
||||||
* result.
|
|
||||||
* @note This method is only available when coroutine support is enabled.
|
|
||||||
*/
|
|
||||||
template <typename T>
|
|
||||||
internal::SqlAwaiter execSqlCoro(const std::string &sql,
|
|
||||||
const std::vector<T> &args) noexcept
|
|
||||||
{
|
|
||||||
auto binder = *this << sql;
|
|
||||||
for (const auto &arg : args)
|
|
||||||
{
|
|
||||||
binder << arg;
|
|
||||||
}
|
|
||||||
return internal::SqlAwaiter(std::move(binder));
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Streaming-like method for sql execution. For more information, see the
|
/// Streaming-like method for sql execution. For more information, see the
|
||||||
|
@ -1053,9 +1053,9 @@ inline void Mapper<T>::findBy(const Criteria &criteria,
|
|||||||
std::vector<T> ret;
|
std::vector<T> ret;
|
||||||
for (auto const &row : r)
|
for (auto const &row : r)
|
||||||
{
|
{
|
||||||
ret.emplace_back(row);
|
ret.push_back(T(row));
|
||||||
}
|
}
|
||||||
rcb(std::move(ret));
|
rcb(ret);
|
||||||
};
|
};
|
||||||
binder >> ecb;
|
binder >> ecb;
|
||||||
}
|
}
|
||||||
|
@ -116,14 +116,6 @@ enum class Mode
|
|||||||
Blocking
|
Blocking
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RawParameter
|
|
||||||
{
|
|
||||||
std::shared_ptr<void> obj;
|
|
||||||
const char *parameter;
|
|
||||||
int length;
|
|
||||||
int format;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace internal
|
namespace internal
|
||||||
{
|
{
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -442,15 +434,6 @@ class DROGON_EXPORT SqlBinder : public trantor::NonCopyable
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
self &operator<<(const RawParameter &);
|
|
||||||
|
|
||||||
self &operator<<(RawParameter ¶m)
|
|
||||||
{
|
|
||||||
return operator<<((const RawParameter &)param);
|
|
||||||
}
|
|
||||||
|
|
||||||
self &operator<<(RawParameter &&);
|
|
||||||
|
|
||||||
// template <>
|
// template <>
|
||||||
self &operator<<(const char str[])
|
self &operator<<(const char str[])
|
||||||
{
|
{
|
||||||
|
@ -429,8 +429,6 @@ DbConnectionPtr DbClientImpl::newConnection(trantor::EventLoop *loop)
|
|||||||
}
|
}
|
||||||
// Reconnect after 1 second
|
// Reconnect after 1 second
|
||||||
auto loop = closeConnPtr->loop();
|
auto loop = closeConnPtr->loop();
|
||||||
// closeConnPtr may be not valid. Close the connection file descriptor.
|
|
||||||
closeConnPtr->disconnect();
|
|
||||||
loop->runAfter(1, [weakPtr, loop, closeConnPtr] {
|
loop->runAfter(1, [weakPtr, loop, closeConnPtr] {
|
||||||
auto thisPtr = weakPtr.lock();
|
auto thisPtr = weakPtr.lock();
|
||||||
if (!thisPtr)
|
if (!thisPtr)
|
||||||
|
@ -118,7 +118,7 @@ class DbConnection : public trantor::NonCopyable
|
|||||||
|
|
||||||
virtual ~DbConnection()
|
virtual ~DbConnection()
|
||||||
{
|
{
|
||||||
LOG_TRACE << "Destruct DbConn " << this;
|
LOG_TRACE << "Destruct DbConn" << this;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectStatus status() const
|
ConnectStatus status() const
|
||||||
|
@ -18,9 +18,6 @@
|
|||||||
#include <drogon/utils/Utilities.h>
|
#include <drogon/utils/Utilities.h>
|
||||||
#include <future>
|
#include <future>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#if defined(__cpp_lib_format)
|
|
||||||
#include <format>
|
|
||||||
#endif
|
|
||||||
#if USE_MYSQL
|
#if USE_MYSQL
|
||||||
#include <mysql.h>
|
#include <mysql.h>
|
||||||
#endif
|
#endif
|
||||||
@ -137,26 +134,6 @@ SqlBinder::~SqlBinder()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SqlBinder &SqlBinder::operator<<(const RawParameter ¶m)
|
|
||||||
{
|
|
||||||
objs_.push_back(param.obj);
|
|
||||||
parameters_.push_back(param.parameter);
|
|
||||||
lengths_.push_back(param.length);
|
|
||||||
formats_.push_back(param.format);
|
|
||||||
++parametersNumber_;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
SqlBinder &SqlBinder::operator<<(RawParameter &¶m)
|
|
||||||
{
|
|
||||||
objs_.push_back(std::move(param.obj));
|
|
||||||
parameters_.push_back(std::move(param.parameter));
|
|
||||||
lengths_.push_back(std::move(param.length));
|
|
||||||
formats_.push_back(std::move(param.format));
|
|
||||||
++parametersNumber_;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
SqlBinder &SqlBinder::operator<<(const std::string_view &str)
|
SqlBinder &SqlBinder::operator<<(const std::string_view &str)
|
||||||
{
|
{
|
||||||
auto obj = std::make_shared<std::string>(str.data(), str.length());
|
auto obj = std::make_shared<std::string>(str.data(), str.length());
|
||||||
@ -282,14 +259,7 @@ SqlBinder &SqlBinder::operator<<(double f)
|
|||||||
parameters_.push_back((char *)(obj.get()));
|
parameters_.push_back((char *)(obj.get()));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
return operator<<(std::to_string(f));
|
||||||
#if defined(__cpp_lib_format)
|
|
||||||
return operator<<(std::format("{:.17g}", f));
|
|
||||||
#else
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << std::setprecision(17) << f;
|
|
||||||
return operator<<(ss.str());
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SqlBinder &SqlBinder::operator<<(std::nullptr_t)
|
SqlBinder &SqlBinder::operator<<(std::nullptr_t)
|
||||||
|
@ -87,14 +87,11 @@ PgConnection::PgConnection(trantor::EventLoop *loop,
|
|||||||
[](PGconn *conn) { PQfinish(conn); })),
|
[](PGconn *conn) { PQfinish(conn); })),
|
||||||
channel_(loop, PQsocket(connectionPtr_.get()))
|
channel_(loop, PQsocket(connectionPtr_.get()))
|
||||||
{
|
{
|
||||||
if (channel_.fd() < 0)
|
|
||||||
{
|
|
||||||
LOG_ERROR << "Failed to create Postgres connection";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PgConnection::init()
|
void PgConnection::init()
|
||||||
{
|
{
|
||||||
|
PQsetnonblocking(connectionPtr_.get(), 1);
|
||||||
if (channel_.fd() < 0)
|
if (channel_.fd() < 0)
|
||||||
{
|
{
|
||||||
LOG_ERROR << "Connection with Postgres could not be established";
|
LOG_ERROR << "Connection with Postgres could not be established";
|
||||||
@ -106,8 +103,6 @@ void PgConnection::init()
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PQsetnonblocking(connectionPtr_.get(), 1);
|
|
||||||
channel_.setReadCallback([this]() {
|
channel_.setReadCallback([this]() {
|
||||||
if (status_ == ConnectStatus::Bad)
|
if (status_ == ConnectStatus::Bad)
|
||||||
{
|
{
|
||||||
@ -170,11 +165,8 @@ void PgConnection::disconnect()
|
|||||||
auto thisPtr = shared_from_this();
|
auto thisPtr = shared_from_this();
|
||||||
loop_->runInLoop([thisPtr, &pro]() {
|
loop_->runInLoop([thisPtr, &pro]() {
|
||||||
thisPtr->status_ = ConnectStatus::Bad;
|
thisPtr->status_ = ConnectStatus::Bad;
|
||||||
if (thisPtr->channel_.fd() >= 0)
|
thisPtr->channel_.disableAll();
|
||||||
{
|
thisPtr->channel_.remove();
|
||||||
thisPtr->channel_.disableAll();
|
|
||||||
thisPtr->channel_.remove();
|
|
||||||
}
|
|
||||||
thisPtr->connectionPtr_.reset();
|
thisPtr->connectionPtr_.reset();
|
||||||
pro.set_value(1);
|
pro.set_value(1);
|
||||||
});
|
});
|
||||||
@ -530,17 +522,11 @@ void PgConnection::handleFatalError(bool clearAll, bool isAbortPipeline)
|
|||||||
{
|
{
|
||||||
for (auto &cmd : batchCommandsForWaitingResults_)
|
for (auto &cmd : batchCommandsForWaitingResults_)
|
||||||
{
|
{
|
||||||
if (cmd->exceptionCallback_)
|
cmd->exceptionCallback_(exceptPtr);
|
||||||
{
|
|
||||||
cmd->exceptionCallback_(exceptPtr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for (auto &cmd : batchSqlCommands_)
|
for (auto &cmd : batchSqlCommands_)
|
||||||
{
|
{
|
||||||
if (cmd->exceptionCallback_)
|
cmd->exceptionCallback_(exceptPtr);
|
||||||
{
|
|
||||||
cmd->exceptionCallback_(exceptPtr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
batchCommandsForWaitingResults_.clear();
|
batchCommandsForWaitingResults_.clear();
|
||||||
batchSqlCommands_.clear();
|
batchSqlCommands_.clear();
|
||||||
@ -550,19 +536,13 @@ void PgConnection::handleFatalError(bool clearAll, bool isAbortPipeline)
|
|||||||
if (!batchSqlCommands_.empty() &&
|
if (!batchSqlCommands_.empty() &&
|
||||||
!batchSqlCommands_.front()->preparingStatement_.empty())
|
!batchSqlCommands_.front()->preparingStatement_.empty())
|
||||||
{
|
{
|
||||||
if (batchSqlCommands_.front()->exceptionCallback_)
|
batchSqlCommands_.front()->exceptionCallback_(exceptPtr);
|
||||||
{
|
|
||||||
batchSqlCommands_.front()->exceptionCallback_(exceptPtr);
|
|
||||||
}
|
|
||||||
batchSqlCommands_.pop_front();
|
batchSqlCommands_.pop_front();
|
||||||
}
|
}
|
||||||
else if (!batchCommandsForWaitingResults_.empty())
|
else if (!batchCommandsForWaitingResults_.empty())
|
||||||
{
|
{
|
||||||
auto &cmd = batchCommandsForWaitingResults_.front();
|
auto &cmd = batchCommandsForWaitingResults_.front();
|
||||||
if (cmd->exceptionCallback_)
|
cmd->exceptionCallback_(exceptPtr);
|
||||||
{
|
|
||||||
cmd->exceptionCallback_(exceptPtr);
|
|
||||||
}
|
|
||||||
batchCommandsForWaitingResults_.pop_front();
|
batchCommandsForWaitingResults_.pop_front();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -65,14 +65,11 @@ PgConnection::PgConnection(trantor::EventLoop *loop,
|
|||||||
[](PGconn *conn) { PQfinish(conn); })),
|
[](PGconn *conn) { PQfinish(conn); })),
|
||||||
channel_(loop, PQsocket(connectionPtr_.get()))
|
channel_(loop, PQsocket(connectionPtr_.get()))
|
||||||
{
|
{
|
||||||
if (channel_.fd() < 0)
|
|
||||||
{
|
|
||||||
LOG_ERROR << "Failed to create Postgres connection";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PgConnection::init()
|
void PgConnection::init()
|
||||||
{
|
{
|
||||||
|
PQsetnonblocking(connectionPtr_.get(), 1);
|
||||||
if (channel_.fd() < 0)
|
if (channel_.fd() < 0)
|
||||||
{
|
{
|
||||||
LOG_ERROR << "Connection with Postgres could not be established";
|
LOG_ERROR << "Connection with Postgres could not be established";
|
||||||
@ -83,8 +80,6 @@ void PgConnection::init()
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PQsetnonblocking(connectionPtr_.get(), 1);
|
|
||||||
channel_.setReadCallback([this]() {
|
channel_.setReadCallback([this]() {
|
||||||
if (status_ == ConnectStatus::Bad)
|
if (status_ == ConnectStatus::Bad)
|
||||||
{
|
{
|
||||||
@ -133,15 +128,6 @@ void PgConnection::handleClosed()
|
|||||||
if (status_ == ConnectStatus::Bad)
|
if (status_ == ConnectStatus::Bad)
|
||||||
return;
|
return;
|
||||||
status_ = ConnectStatus::Bad;
|
status_ = ConnectStatus::Bad;
|
||||||
|
|
||||||
if (isWorking_)
|
|
||||||
{
|
|
||||||
// Connection was closed unexpectedly while isWorking_ was true.
|
|
||||||
isWorking_ = false;
|
|
||||||
handleFatalError();
|
|
||||||
callback_ = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
channel_.disableAll();
|
channel_.disableAll();
|
||||||
channel_.remove();
|
channel_.remove();
|
||||||
assert(closeCallback_);
|
assert(closeCallback_);
|
||||||
@ -156,11 +142,8 @@ void PgConnection::disconnect()
|
|||||||
auto thisPtr = shared_from_this();
|
auto thisPtr = shared_from_this();
|
||||||
loop_->runInLoop([thisPtr, &pro]() {
|
loop_->runInLoop([thisPtr, &pro]() {
|
||||||
thisPtr->status_ = ConnectStatus::Bad;
|
thisPtr->status_ = ConnectStatus::Bad;
|
||||||
if (thisPtr->channel_.fd() >= 0)
|
thisPtr->channel_.disableAll();
|
||||||
{
|
thisPtr->channel_.remove();
|
||||||
thisPtr->channel_.disableAll();
|
|
||||||
thisPtr->channel_.remove();
|
|
||||||
}
|
|
||||||
thisPtr->connectionPtr_.reset();
|
thisPtr->connectionPtr_.reset();
|
||||||
pro.set_value(1);
|
pro.set_value(1);
|
||||||
});
|
});
|
||||||
@ -415,13 +398,9 @@ void PgConnection::doAfterPreparing()
|
|||||||
|
|
||||||
void PgConnection::handleFatalError()
|
void PgConnection::handleFatalError()
|
||||||
{
|
{
|
||||||
if (exceptionCallback_)
|
auto exceptPtr =
|
||||||
{
|
std::make_exception_ptr(Failure(PQerrorMessage(connectionPtr_.get())));
|
||||||
auto exceptPtr = std::make_exception_ptr(
|
exceptionCallback_(exceptPtr);
|
||||||
Failure(PQerrorMessage(connectionPtr_.get())));
|
|
||||||
exceptionCallback_(exceptPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
exceptionCallback_ = nullptr;
|
exceptionCallback_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,24 +287,11 @@ DROGON_TEST(PostgreTest)
|
|||||||
FAULT("postgresql - DbClient streaming-type interface(8) what():",
|
FAULT("postgresql - DbClient streaming-type interface(8) what():",
|
||||||
e.base().what());
|
e.base().what());
|
||||||
};
|
};
|
||||||
/// 1.10 query with raw parameter
|
/// 1.10 clean up
|
||||||
auto rawParamData = std::make_shared<int>(htonl(3));
|
|
||||||
auto rawParam = RawParameter{rawParamData,
|
|
||||||
reinterpret_cast<char *>(rawParamData.get()),
|
|
||||||
sizeof(int),
|
|
||||||
1};
|
|
||||||
*clientPtr << "select * from users where length(user_id)=$1" << rawParam >>
|
|
||||||
[TEST_CTX](const Result &r) { MANDATE(r.size() == 1); } >>
|
|
||||||
[TEST_CTX](const DrogonDbException &e) {
|
|
||||||
FAULT("postgresql - DbClient streaming-type interface(9) what():",
|
|
||||||
e.base().what());
|
|
||||||
};
|
|
||||||
|
|
||||||
/// 1.11 clean up
|
|
||||||
*clientPtr << "truncate table users restart identity" >>
|
*clientPtr << "truncate table users restart identity" >>
|
||||||
[TEST_CTX](const Result &r) { SUCCESS(); } >>
|
[TEST_CTX](const Result &r) { SUCCESS(); } >>
|
||||||
[TEST_CTX](const DrogonDbException &e) {
|
[TEST_CTX](const DrogonDbException &e) {
|
||||||
FAULT("postgresql - DbClient streaming-type interface(10) what():",
|
FAULT("postgresql - DbClient streaming-type interface(9) what():",
|
||||||
e.base().what());
|
e.base().what());
|
||||||
};
|
};
|
||||||
/// Test asynchronous method
|
/// Test asynchronous method
|
||||||
@ -392,21 +379,12 @@ DROGON_TEST(PostgreTest)
|
|||||||
"postgresql1",
|
"postgresql1",
|
||||||
"pg",
|
"pg",
|
||||||
"postgresql");
|
"postgresql");
|
||||||
/// 2.6 query with raw parameter
|
/// 2.6 clean up
|
||||||
clientPtr->execSqlAsync(
|
|
||||||
"select * from users where length(user_id)=$1",
|
|
||||||
[TEST_CTX](const Result &r) { MANDATE(r.size() == 1); },
|
|
||||||
[TEST_CTX](const DrogonDbException &e) {
|
|
||||||
FAULT("postgresql - DbClient asynchronous interface(7) what():",
|
|
||||||
e.base().what());
|
|
||||||
},
|
|
||||||
rawParam);
|
|
||||||
/// 2.7 clean up
|
|
||||||
clientPtr->execSqlAsync(
|
clientPtr->execSqlAsync(
|
||||||
"truncate table users restart identity",
|
"truncate table users restart identity",
|
||||||
[TEST_CTX](const Result &r) { SUCCESS(); },
|
[TEST_CTX](const Result &r) { SUCCESS(); },
|
||||||
[TEST_CTX](const DrogonDbException &e) {
|
[TEST_CTX](const DrogonDbException &e) {
|
||||||
FAULT("postgresql - DbClient asynchronous interface(8) what():",
|
FAULT("postgresql - DbClient asynchronous interface(7) what():",
|
||||||
e.base().what());
|
e.base().what());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -486,19 +464,7 @@ DROGON_TEST(PostgreTest)
|
|||||||
{
|
{
|
||||||
SUCCESS();
|
SUCCESS();
|
||||||
}
|
}
|
||||||
/// 3.6 query with raw parameter
|
/// 3.6 clean up
|
||||||
try
|
|
||||||
{
|
|
||||||
auto r = clientPtr->execSqlSync(
|
|
||||||
"select * from users where length(user_id)=$1", rawParam);
|
|
||||||
MANDATE(r.size() == 1);
|
|
||||||
}
|
|
||||||
catch (const DrogonDbException &e)
|
|
||||||
{
|
|
||||||
FAULT("postgresql - DbClient asynchronous interface(4) what():",
|
|
||||||
e.base().what());
|
|
||||||
}
|
|
||||||
/// 3.7 clean up
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto r =
|
auto r =
|
||||||
@ -591,20 +557,7 @@ DROGON_TEST(PostgreTest)
|
|||||||
{
|
{
|
||||||
SUCCESS();
|
SUCCESS();
|
||||||
}
|
}
|
||||||
/// 4.6 query with raw parameter
|
/// 4.6 clean up
|
||||||
f = clientPtr->execSqlAsyncFuture(
|
|
||||||
"select * from users where length(user_id)=$1", rawParam);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
auto r = f.get();
|
|
||||||
MANDATE(r.size() == 1);
|
|
||||||
}
|
|
||||||
catch (const DrogonDbException &e)
|
|
||||||
{
|
|
||||||
FAULT("postgresql - DbClient future interface(4) what():",
|
|
||||||
e.base().what());
|
|
||||||
}
|
|
||||||
/// 4.7 clean up
|
|
||||||
f = clientPtr->execSqlAsyncFuture("truncate table users restart identity");
|
f = clientPtr->execSqlAsyncFuture("truncate table users restart identity");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -1717,28 +1670,11 @@ DROGON_TEST(MySQLTest)
|
|||||||
FAULT("mysql - DbClient streaming-type interface(8) what():",
|
FAULT("mysql - DbClient streaming-type interface(8) what():",
|
||||||
e.base().what());
|
e.base().what());
|
||||||
};
|
};
|
||||||
/// 1.10 query with raw parameter
|
/// 1.10 truncate
|
||||||
// MariaDB uses little-endian, so the opposite of network ordering :P
|
|
||||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
|
||||||
auto rawParamData = std::make_shared<int>(3);
|
|
||||||
#else
|
|
||||||
auto rawParamData = std::make_shared<int>(0x03000000); // byteswapped 3
|
|
||||||
#endif
|
|
||||||
auto rawParam = RawParameter{rawParamData,
|
|
||||||
reinterpret_cast<char *>(rawParamData.get()),
|
|
||||||
sizeof(int),
|
|
||||||
internal::MySqlLong};
|
|
||||||
*clientPtr << "select * from users where length(user_id)=?" << rawParam >>
|
|
||||||
[TEST_CTX](const Result &r) { MANDATE(r.size() == 1); } >>
|
|
||||||
[TEST_CTX](const DrogonDbException &e) {
|
|
||||||
FAULT("mysql - DbClient streaming-type interface(9) what():",
|
|
||||||
e.base().what());
|
|
||||||
};
|
|
||||||
/// 1.11 truncate
|
|
||||||
*clientPtr << "truncate table users" >> [TEST_CTX](const Result &r) {
|
*clientPtr << "truncate table users" >> [TEST_CTX](const Result &r) {
|
||||||
SUCCESS();
|
SUCCESS();
|
||||||
} >> [TEST_CTX](const DrogonDbException &e) {
|
} >> [TEST_CTX](const DrogonDbException &e) {
|
||||||
FAULT("mysql - DbClient streaming-type interface(10) what():",
|
FAULT("mysql - DbClient streaming-type interface(9) what():",
|
||||||
e.base().what());
|
e.base().what());
|
||||||
};
|
};
|
||||||
/// Test asynchronous method
|
/// Test asynchronous method
|
||||||
@ -1823,21 +1759,12 @@ DROGON_TEST(MySQLTest)
|
|||||||
"postgresql1",
|
"postgresql1",
|
||||||
"pg",
|
"pg",
|
||||||
"postgresql");
|
"postgresql");
|
||||||
/// 2.6 query with raw parameter
|
/// 2.6 truncate
|
||||||
clientPtr->execSqlAsync(
|
|
||||||
"select * from users where length(user_id)=?",
|
|
||||||
[TEST_CTX](const Result &r) { MANDATE(r.size() == 1); },
|
|
||||||
[TEST_CTX](const DrogonDbException &e) {
|
|
||||||
FAULT("mysql - DbClient asynchronous interface(7) what():",
|
|
||||||
e.base().what());
|
|
||||||
},
|
|
||||||
rawParam);
|
|
||||||
/// 2.7 truncate
|
|
||||||
clientPtr->execSqlAsync(
|
clientPtr->execSqlAsync(
|
||||||
"truncate table users",
|
"truncate table users",
|
||||||
[TEST_CTX](const Result &r) { SUCCESS(); },
|
[TEST_CTX](const Result &r) { SUCCESS(); },
|
||||||
[TEST_CTX](const DrogonDbException &e) {
|
[TEST_CTX](const DrogonDbException &e) {
|
||||||
FAULT("mysql - DbClient asynchronous interface(9) what():",
|
FAULT("mysql - DbClient asynchronous interface(7) what():",
|
||||||
e.base().what());
|
e.base().what());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1918,19 +1845,7 @@ DROGON_TEST(MySQLTest)
|
|||||||
{
|
{
|
||||||
SUCCESS();
|
SUCCESS();
|
||||||
}
|
}
|
||||||
/// 3.6 query with raw parameter
|
/// 3.6 truncate
|
||||||
try
|
|
||||||
{
|
|
||||||
auto r = clientPtr->execSqlSync(
|
|
||||||
"select * from users where length(user_id)=?", rawParam);
|
|
||||||
MANDATE(r.size() == 1);
|
|
||||||
}
|
|
||||||
catch (const DrogonDbException &e)
|
|
||||||
{
|
|
||||||
FAULT("mysql - DbClient asynchronous interface(4) what():",
|
|
||||||
e.base().what());
|
|
||||||
}
|
|
||||||
/// 3.7 truncate
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto r = clientPtr->execSqlSync("truncate table users");
|
auto r = clientPtr->execSqlSync("truncate table users");
|
||||||
@ -2017,19 +1932,7 @@ DROGON_TEST(MySQLTest)
|
|||||||
{
|
{
|
||||||
SUCCESS();
|
SUCCESS();
|
||||||
}
|
}
|
||||||
/// 4.6. query with raw parameter
|
/// 4.6 truncate
|
||||||
f = clientPtr->execSqlAsyncFuture(
|
|
||||||
"select * from users where length(user_id)=?", rawParam);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
auto r = f.get();
|
|
||||||
MANDATE(r.size() == 1);
|
|
||||||
}
|
|
||||||
catch (const DrogonDbException &e)
|
|
||||||
{
|
|
||||||
FAULT("mysql - DbClient future interface(5) what():", e.base().what());
|
|
||||||
}
|
|
||||||
/// 4.7 truncate
|
|
||||||
f = clientPtr->execSqlAsyncFuture("truncate table users");
|
f = clientPtr->execSqlAsyncFuture("truncate table users");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -2038,7 +1941,7 @@ DROGON_TEST(MySQLTest)
|
|||||||
}
|
}
|
||||||
catch (const DrogonDbException &e)
|
catch (const DrogonDbException &e)
|
||||||
{
|
{
|
||||||
FAULT("mysql - DbClient future interface(6) what():", e.base().what());
|
FAULT("mysql - DbClient future interface(5) what():", e.base().what());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 5 Test Result and Row exception throwing
|
/// 5 Test Result and Row exception throwing
|
||||||
@ -2978,29 +2881,17 @@ DROGON_TEST(SQLite3Test)
|
|||||||
FAULT("sqlite3 - DbClient streaming-type interface(8) what():",
|
FAULT("sqlite3 - DbClient streaming-type interface(8) what():",
|
||||||
e.base().what());
|
e.base().what());
|
||||||
};
|
};
|
||||||
/// 1.10 query with raw parameter
|
/// 1.10 clean up
|
||||||
auto rawParamData = std::make_shared<int>(3);
|
|
||||||
auto rawParam = RawParameter{rawParamData,
|
|
||||||
reinterpret_cast<char *>(rawParamData.get()),
|
|
||||||
0,
|
|
||||||
Sqlite3TypeInt};
|
|
||||||
*clientPtr << "select * from users where length(user_id) = ?" << rawParam >>
|
|
||||||
[TEST_CTX](const Result &r) { MANDATE(r.size() == 1); } >>
|
|
||||||
[TEST_CTX](const DrogonDbException &e) {
|
|
||||||
FAULT("sqlite3 - DbClient streaming-type interface(9) what():",
|
|
||||||
e.base().what());
|
|
||||||
};
|
|
||||||
/// 1.11 clean up
|
|
||||||
*clientPtr << "delete from users" >> [TEST_CTX](const Result &r) {
|
*clientPtr << "delete from users" >> [TEST_CTX](const Result &r) {
|
||||||
SUCCESS();
|
SUCCESS();
|
||||||
} >> [TEST_CTX](const DrogonDbException &e) {
|
} >> [TEST_CTX](const DrogonDbException &e) {
|
||||||
FAULT("sqlite3 - DbClient streaming-type interface(10.1) what():",
|
FAULT("sqlite3 - DbClient streaming-type interface(9.1) what():",
|
||||||
e.base().what());
|
e.base().what());
|
||||||
};
|
};
|
||||||
*clientPtr << "UPDATE sqlite_sequence SET seq = 0" >>
|
*clientPtr << "UPDATE sqlite_sequence SET seq = 0" >>
|
||||||
[TEST_CTX](const Result &r) { SUCCESS(); } >>
|
[TEST_CTX](const Result &r) { SUCCESS(); } >>
|
||||||
[TEST_CTX](const DrogonDbException &e) {
|
[TEST_CTX](const DrogonDbException &e) {
|
||||||
FAULT("sqlite3 - DbClient streaming-type interface(10.2) what():",
|
FAULT("sqlite3 - DbClient streaming-type interface(9.2) what():",
|
||||||
e.base().what());
|
e.base().what());
|
||||||
};
|
};
|
||||||
/// Test asynchronous method
|
/// Test asynchronous method
|
||||||
@ -3084,28 +2975,19 @@ DROGON_TEST(SQLite3Test)
|
|||||||
"postgresql1",
|
"postgresql1",
|
||||||
"pg",
|
"pg",
|
||||||
"postgresql");
|
"postgresql");
|
||||||
/// 2.6 query with raw parameter
|
/// 2.6 clean up
|
||||||
clientPtr->execSqlAsync(
|
|
||||||
"select * from users where length(user_id) = ?",
|
|
||||||
[TEST_CTX](const Result &r) { MANDATE(r.size() == 1); },
|
|
||||||
[TEST_CTX](const DrogonDbException &e) {
|
|
||||||
FAULT("sqlite3 - DbClient asynchronous interface(7) what():",
|
|
||||||
e.base().what());
|
|
||||||
},
|
|
||||||
rawParam);
|
|
||||||
/// 2.7 clean up
|
|
||||||
clientPtr->execSqlAsync(
|
clientPtr->execSqlAsync(
|
||||||
"delete from users",
|
"delete from users",
|
||||||
[TEST_CTX](const Result &r) { SUCCESS(); },
|
[TEST_CTX](const Result &r) { SUCCESS(); },
|
||||||
[TEST_CTX](const DrogonDbException &e) {
|
[TEST_CTX](const DrogonDbException &e) {
|
||||||
FAULT("sqlite3 - DbClient asynchronous interface(8.1) what():",
|
FAULT("sqlite3 - DbClient asynchronous interface(7.1) what():",
|
||||||
e.base().what());
|
e.base().what());
|
||||||
});
|
});
|
||||||
clientPtr->execSqlAsync(
|
clientPtr->execSqlAsync(
|
||||||
"UPDATE sqlite_sequence SET seq = 0",
|
"UPDATE sqlite_sequence SET seq = 0",
|
||||||
[TEST_CTX](const Result &r) { SUCCESS(); },
|
[TEST_CTX](const Result &r) { SUCCESS(); },
|
||||||
[TEST_CTX](const DrogonDbException &e) {
|
[TEST_CTX](const DrogonDbException &e) {
|
||||||
FAULT("sqlite3 - DbClient asynchronous interface(8.2) what():",
|
FAULT("sqlite3 - DbClient asynchronous interface(7.2) what():",
|
||||||
e.base().what());
|
e.base().what());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -3192,19 +3074,7 @@ DROGON_TEST(SQLite3Test)
|
|||||||
{
|
{
|
||||||
SUCCESS();
|
SUCCESS();
|
||||||
}
|
}
|
||||||
/// 3.6 query with raw parameter
|
/// 3.6 clean up
|
||||||
try
|
|
||||||
{
|
|
||||||
auto r = clientPtr->execSqlSync(
|
|
||||||
"select * from users where length(user_id) = ?", rawParam);
|
|
||||||
MANDATE(r.size() == 1);
|
|
||||||
}
|
|
||||||
catch (const DrogonDbException &e)
|
|
||||||
{
|
|
||||||
FAULT("sqlite3 - DbClient asynchronous interface(4) what():",
|
|
||||||
e.base().what());
|
|
||||||
}
|
|
||||||
/// 3.7 clean up
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto r = clientPtr->execSqlSync("delete from users");
|
auto r = clientPtr->execSqlSync("delete from users");
|
||||||
@ -3307,19 +3177,6 @@ DROGON_TEST(SQLite3Test)
|
|||||||
{
|
{
|
||||||
SUCCESS();
|
SUCCESS();
|
||||||
}
|
}
|
||||||
/// 4.6 query with raw parameter
|
|
||||||
f = clientPtr->execSqlAsyncFuture(
|
|
||||||
"select * from users where length(user_id)=?", rawParam);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
auto r = f.get();
|
|
||||||
MANDATE(r.size() == 1);
|
|
||||||
}
|
|
||||||
catch (const DrogonDbException &e)
|
|
||||||
{
|
|
||||||
FAULT("sqlite3 - DbClient future interface(4) what():",
|
|
||||||
e.base().what());
|
|
||||||
}
|
|
||||||
/// 4.6 clean up
|
/// 4.6 clean up
|
||||||
f = clientPtr->execSqlAsyncFuture("delete from users");
|
f = clientPtr->execSqlAsyncFuture("delete from users");
|
||||||
try
|
try
|
||||||
|
2
trantor
2
trantor
@ -1 +1 @@
|
|||||||
Subproject commit 43fd79b2dbac59608a819ebba167e8fe2c079d90
|
Subproject commit a00f39b1127c98ec26dd1df871d2c3b26e6bbeeb
|
Loading…
x
Reference in New Issue
Block a user