diff --git a/.appveyor.yml b/.appveyor.yml deleted file mode 100644 index 37826fb4..00000000 --- a/.appveyor.yml +++ /dev/null @@ -1,26 +0,0 @@ -version: 1.1.1.{build} -image: Visual Studio 2019 -configuration: -- Release -- Debug - -environment: - matrix: - - GENERATOR: Visual Studio 16 2019 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 - ARCH: Win32 - - - GENERATOR: Visual Studio 16 2019 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 - ARCH: x64 - -init: [] - -before_build: -- md build -- cd build -- cmake --config "%CONFIGURATION%" -G "%GENERATOR%" .. -build_script: -- cmake --build . --config "%CONFIGURATION%" -build: - verbosity: minimal \ No newline at end of file diff --git a/.cmake-format b/.cmake-format new file mode 100644 index 00000000..bec68f36 --- /dev/null +++ b/.cmake-format @@ -0,0 +1,53 @@ +# notes for additional commands +# +# nargs: '*' to allow multiple arguments +# kwargs: &fookwargs to definite keyword arguments +# kwargs: *fookwargs to use the same keyword arguments as fookwargs +# NAME: 1 to allow single keyword arguments +# NAME: + to allow multiple keyword arguments +# NAME: * to allow multiple keyword arguments +# spelling: FOO to use foo to FOO spelling + +parse: + additional_commands: + FetchContent_Declare: + pargs: + nargs: '*' + flags: [] + kwargs: + GIT_TAG: 1 + GITHUB_REPOSITORY: 1 + GITLAB_REPOSITORY: 1 + GIT_REPOSITORY: 1 + SVN_REPOSITORY: 1 + SVN_REVISION: 1 + URL: 1 + URL_HASH: 1 + URL_MD5: 1 + FIND_PACKAGE_ARGS: + + FetchContent_MakeAvailable: + pargs: + nargs: '*' + flags: [] + execute_process: + pargs: + nargs: '*' + flags: [] + kwargs: + COMMAND: + + WORKING_DIRECTORY: 1 + set_target_properties: + pargs: + nargs: '*' + flags: [] + kwargs: + PROPERTIES: + + IMPORTED_LOCATION: 1 + INTERFACE_INCLUDE_DIRECTORIES: 1 +format: + tab_size: 2 + line_width: 120 + autosort: true + dangle_parens: true + max_subgroups_hwrap: 2 + max_pargs_hwrap: 3 \ No newline at end of file diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml deleted file mode 100644 index ceb95a7c..00000000 --- a/.github/workflows/cmake.yml +++ /dev/null @@ -1,141 +0,0 @@ -name: Build Trantor - -on: - push: - branches: [master] - pull_request: - workflow_dispatch: - -env: - # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) - BUILD_TYPE: Release - -jobs: - windows: - name: 'windows/msvc - ${{matrix.link}}' - runs-on: windows-latest - strategy: - fail-fast: false - matrix: - link: [ 'STATIC', 'SHARED' ] - steps: - - name: Checkout Trantor source code - uses: actions/checkout@v2 - with: - submodules: false - - - name: Install dependencies - run: | - pip install conan - - - name: Create build directory - run: | - mkdir build - - - name: Install conan packages - shell: pwsh - working-directory: ./build - run: | - conan profile detect - conan install .. -s compiler="msvc" -s compiler.version=193 -sbuild_type=Debug --build=missing - - - name: Create Build Environment & Configure Cmake - shell: bash - working-directory: ./build - run: | - [[ ${{ matrix.link }} == "SHARED" ]] && shared="ON" || shared="OFF" - cmake .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=on -DUSE_SPDLOG=ON -DBUILD_SHARED_LIBS=$shared -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" -DCMAKE_INSTALL_PREFIX=../install -DCMAKE_POLICY_DEFAULT_CMP0091=NEW - - - name: Build - working-directory: ${{env.GITHUB_WORKSPACE}} - shell: bash - run: | - cd build - cmake --build . --target install --parallel - unix: - name: ${{matrix.buildname}} - runs-on: ${{matrix.os}} - strategy: - fail-fast: false - matrix: - include: - - os: ubuntu-20.04 - buildname: 'ubuntu-20.04/gcc - OpenSSL' - triplet: x64-linux - compiler: gcc_64 - tls_provider: openssl - - os: ubuntu-20.04 - buildname: 'ubuntu-20.04/gcc' - triplet: x64-linux - compiler: gcc_64 - tls_provider: none - - os: macos-latest - buildname: 'macos/clang' - triplet: x64-osx - compiler: clang_64 - tls_provider: openssl - - os: macos-latest - buildname: 'macos/clang - Botan' - triplet: x64-osx - compiler: clang_64 - tls_provider: botan - - steps: - - name: Checkout Trantor source code - uses: actions/checkout@v2 - with: - submodules: true - fetch-depth: 0 - - - name: (macOS) Install dependencies - if: runner.os == 'macOS' - run: | - brew install c-ares openssl spdlog botan - - - name: (Linux) Install dependencies - if: matrix.os == 'Linux' - run: | - # Installing packages might fail as the github image becomes outdated - sudo apt update - sudo apt install openssl libssl-dev dos2unix libspdlog-dev libfmt-dev - - name: install gtest - run: | - wget https://github.com/google/googletest/archive/refs/tags/v1.13.0.tar.gz - tar xf v1.13.0.tar.gz - cd googletest-1.13.0 - cmake . - make - sudo make install - - - name: Create Build Environment & Configure Cmake - # Some projects don't allow in-source building, so create a separate build directory - # We'll use this as our working directory for all subsequent commands - shell: bash - working-directory: ${{env.GITHUB_WORKSPACE}} - run: | - mkdir build - cd build - cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_TESTING=on -DUSE_SPDLOG=ON -DTRANTOR_USE_TLS=${{ matrix.tls_provider }} - - - name: Build - working-directory: ${{env.GITHUB_WORKSPACE}} - shell: bash - # Execute the build. You can specify a specific target with "--target " - run: | - cd build - sudo make && sudo make install - - - name: Test - working-directory: ${{env.GITHUB_WORKSPACE}} - shell: bash - # Execute tests defined by the CMake configuration. - # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: | - cd build - make test - - - name: Lint - if: matrix.os == 'ubuntu-20.04' - working-directory: ${{env.GITHUB_WORKSPACE}} - shell: bash - run: ./format.sh && git diff --exit-code diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..c17f2550 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,31 @@ +name: Lint source code + +on: + push: + branches: [master] + pull_request: + workflow_dispatch: + +jobs: + unix: + name: Lint + runs-on: ubuntu-latest + strategy: + fail-fast: false + + steps: + - name: Checkout Trantor source code + uses: actions/checkout@v4 + with: + submodules: true + fetch-depth: 0 + + - name: (Linux) Install dependencies + run: | + # Installing packages might fail as the github image becomes outdated + sudo apt update + sudo apt install dos2unix clang-format + pip install cmake-format + + - name: Lint + run: ./format.sh && git diff --exit-code diff --git a/.github/workflows/macos-clang.yml b/.github/workflows/macos-clang.yml new file mode 100644 index 00000000..6baa1688 --- /dev/null +++ b/.github/workflows/macos-clang.yml @@ -0,0 +1,73 @@ +name: Build macos-clang + +on: + push: + branches: [master] + pull_request: + workflow_dispatch: + +jobs: + build: + name: '${{matrix.link}}-${{matrix.build-type}}-${{matrix.tls-provider}}' + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + link: [ 'STATIC', 'SHARED' ] + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + build-type: ['Debug', 'Release'] + # Botan needs std::ranges but clang on macOS doesn't support it yet + #tls-provider: ['', 'openssl', 'botan'] + tls-provider: ['', 'openssl'] + + steps: + - name: Install dependencies + # botan v3 + run: | + brew install botan spdlog + + - name: Install gtest + run: | + wget https://github.com/google/googletest/archive/refs/tags/v1.13.0.tar.gz + tar xf v1.13.0.tar.gz + cd googletest-1.13.0 + cmake . + make && sudo make install + + - name: Checkout Trantor source code + uses: actions/checkout@v4 + with: + submodules: true + fetch-depth: 0 + + - name: Create build directory + run: | + mkdir build + + - name: Create Build Environment & Configure Cmake + shell: bash + working-directory: ./build + run: | + [[ ${{ matrix.link }} == "SHARED" ]] && shared="ON" || shared="OFF" + cmake .. \ + -DTRANTOR_USE_TLS=${{matrix.tls-provider}} \ + -DCMAKE_BUILD_TYPE=${{matrix.build-type}} \ + -DBUILD_SHARED_LIBS=$shared \ + -DCMAKE_INSTALL_PREFIX=../install \ + -DUSE_SPDLOG=ON \ + -DBUILD_TESTING=ON \ + + - name: Build + shell: bash + working-directory: ./build + # Execute the build. You can specify a specific target with "--target " + run: | + sudo make && sudo make install + + - name: Test + working-directory: ./build + shell: bash + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: | + make test diff --git a/.github/workflows/rockylinux-gcc.yml b/.github/workflows/rockylinux-gcc.yml new file mode 100644 index 00000000..85de8c26 --- /dev/null +++ b/.github/workflows/rockylinux-gcc.yml @@ -0,0 +1,88 @@ +name: Build rockylinux-gcc + +on: + push: + branches: [master] + pull_request: + workflow_dispatch: + +jobs: + build: + name: '${{matrix.link}}-${{matrix.build-type}}-${{matrix.tls-provider}}' + runs-on: ubuntu-latest + container: + image: rockylinux:9.3 + options: --user root + strategy: + fail-fast: false + matrix: + link: [ 'STATIC', 'SHARED' ] + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + build-type: ['Debug', 'Release'] + # TODO: ubuntu botan is v2, v2 support is removed + # tls-provider: ['', 'openssl', 'botan'] + tls-provider: ['', 'openssl'] + + steps: + - name: Install dependencies + run: | + dnf install gcc-c++ cmake git wget -y + + - name: Install dependencies - spdlog + run: | + git clone https://github.com/gabime/spdlog.git + cd spdlog && mkdir build && cd build + cmake .. && make -j + + - name: Install dependencies - OpenSSL + if: matrix.tls-provider == 'openssl' + run: | + dnf install openssl-devel -y + + - name: Install gtest + run: | + wget https://github.com/google/googletest/archive/refs/tags/v1.13.0.tar.gz + tar xf v1.13.0.tar.gz + cd googletest-1.13.0 + cmake . + make -j && make install + + - name: Checkout Trantor source code + uses: actions/checkout@v4 + with: + submodules: true + fetch-depth: 0 + + - name: Create build directory + run: | + mkdir build + + - name: Create Build Environment & Configure Cmake + shell: bash + working-directory: ./build + if: ${{matrix.link}} == "SHARED" + run: | + [[ ${{ matrix.link }} == "SHARED" ]] && shared="ON" || shared="OFF" + cmake .. \ + -DTRANTOR_USE_TLS=${{matrix.tls-provider}} \ + -DCMAKE_BUILD_TYPE=${{matrix.build-type}} \ + -DBUILD_SHARED_LIBS=$shared \ + -DCMAKE_INSTALL_PREFIX=../install \ + -DUSE_SPDLOG=ON \ + -DBUILD_TESTING=ON + + - name: Build + shell: bash + working-directory: ./build + # Execute the build. You can specify a specific target with "--target " + run: | + make && make install + + - name: Test + working-directory: ./build + shell: bash + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: | + make test + diff --git a/.github/workflows/ubuntu-gcc.yml b/.github/workflows/ubuntu-gcc.yml new file mode 100644 index 00000000..04b47ff7 --- /dev/null +++ b/.github/workflows/ubuntu-gcc.yml @@ -0,0 +1,81 @@ +name: Build ubuntu-gcc + +on: + push: + branches: [master] + pull_request: + workflow_dispatch: + +jobs: + build: + name: '${{matrix.link}}-${{matrix.build-type}}-${{matrix.tls-provider}}' + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + link: [ 'STATIC', 'SHARED' ] + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + build-type: ['Debug', 'Release'] + # TODO: ubuntu botan is v2, v2 support is removed + # tls-provider: ['', 'openssl', 'botan'] + tls-provider: ['', 'openssl'] + + steps: + - name: Install dependencies + run: | + # Installing packages might fail as the github image becomes outdated + sudo apt update + sudo apt install libspdlog-dev libfmt-dev + + - name: Install dependencies - OpenSSL + if: matrix.tls-provider == 'openssl' + run: | + sudo apt install openssl libssl-dev + + - name: Install gtest + run: | + wget https://github.com/google/googletest/archive/refs/tags/v1.13.0.tar.gz + tar xf v1.13.0.tar.gz + cd googletest-1.13.0 + cmake . + make -j && sudo make install + + - name: Checkout Trantor source code + uses: actions/checkout@v4 + with: + submodules: true + fetch-depth: 0 + + - name: Create build directory + run: | + mkdir build + + - name: Create Build Environment & Configure Cmake + shell: bash + working-directory: ./build + if: ${{matrix.link}} == "SHARED" + run: | + [[ ${{ matrix.link }} == "SHARED" ]] && shared="ON" || shared="OFF" + cmake .. \ + -DTRANTOR_USE_TLS=${{matrix.tls-provider}} \ + -DCMAKE_BUILD_TYPE=${{matrix.build-type}} \ + -DBUILD_SHARED_LIBS=$shared \ + -DCMAKE_INSTALL_PREFIX=../install \ + -DUSE_SPDLOG=ON \ + -DBUILD_TESTING=ON + + - name: Build + shell: bash + working-directory: ./build + # Execute the build. You can specify a specific target with "--target " + run: | + sudo make && sudo make install + + - name: Test + working-directory: ./build + shell: bash + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: | + make test + diff --git a/.github/workflows/windows-msvc.yml b/.github/workflows/windows-msvc.yml new file mode 100644 index 00000000..1cfc7341 --- /dev/null +++ b/.github/workflows/windows-msvc.yml @@ -0,0 +1,62 @@ +name: Build windows-msvc + +on: + push: + branches: [master] + pull_request: + workflow_dispatch: + +jobs: + build: + name: '${{matrix.link}}-${{matrix.build-type}}-${{matrix.tls-provider}}' + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + link: [ 'STATIC', 'SHARED' ] + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + build-type: ['Debug', 'Release'] + # TODO: conan botan is v2, v2 support is removed + # tls-provider: ['', 'openssl', 'botan'] + tls-provider: ['', 'openssl'] + + steps: + - name: Checkout Trantor source code + uses: actions/checkout@v4 + with: + submodules: false + + - name: Create build directory + working-directory: ${{env.GITHUB_WORKSPACE}} + run: | + mkdir build + + - name: Install conan packages + shell: bash + working-directory: ./build + run: | + pip install conan + conan profile detect --force + conan install .. --output-folder=. --build=missing --settings=build_type=${{matrix.build-type}} --settings=compiler="msvc" + + - name: Create Build Environment & Configure Cmake + shell: bash + working-directory: ./build + # -DBUILD_TESTING=ON Removed, + # Due to unittest by GTest in windows runner will comes out 'error MSB3073' + run: | + [[ ${{ matrix.link }} == "SHARED" ]] && shared="ON" || shared="OFF" + cmake .. -G "Visual Studio 17 2022" -T host=x64 -A x64 \ + -DTRANTOR_USE_TLS=${{matrix.tls-provider}} \ + -DCMAKE_BUILD_TYPE=${{matrix.build-type}} \ + -DBUILD_SHARED_LIBS=$shared \ + -DCMAKE_INSTALL_PREFIX=../install \ + -DUSE_SPDLOG=ON \ + -DCMAKE_POLICY_DEFAULT_CMP0091=NEW + + - name: Build + working-directory: ./build + shell: bash + # multi config build using --config to switch Release|Debug + run: | + cmake --build . --config ${{matrix.build-type}} --parallel diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 542bc697..00000000 --- a/.travis.yml +++ /dev/null @@ -1,32 +0,0 @@ -matrix: - include: - - os: linux - dist: xenial - - os: osx - osx_image: xcode11 - -sudo: required - -language: cpp - -addons: - apt: - sources: - - xenial - - sourceline: 'deb http://archive.ubuntu.com/ubuntu xenial main' - packages: - - openssl - - libssl-dev - - build-essential - - cmake - - libgtest-dev - - libc-ares-dev - homebrew: - packages: - - openssl - - cmake - - libtool - - gtest - -script: - - ./build.sh -t diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ec54611..0b8bc6ee 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,61 +5,82 @@ option(BUILD_DOC "Build Doxygen documentation" OFF) option(BUILD_C-ARES "Build C-ARES" ON) option(BUILD_TESTING "Build tests" OFF) option(BUILD_SHARED_LIBS "Build trantor as a shared lib" OFF) -option(TRANTOR_USE_TLS "TLS provider for trantor. Valid options are 'openssl', 'botan' or '' (let the build scripr decide)" "") +option(TRANTOR_USE_TLS + "TLS provider for trantor. Valid options are 'openssl', 'botan' or '' (let the build scripr decide)" "" +) option(USE_SPDLOG "Allow using the spdlog logging library" OFF) list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules/) set(TRANTOR_MAJOR_VERSION 1) set(TRANTOR_MINOR_VERSION 5) -set(TRANTOR_PATCH_VERSION 15) -set(TRANTOR_VERSION - ${TRANTOR_MAJOR_VERSION}.${TRANTOR_MINOR_VERSION}.${TRANTOR_PATCH_VERSION}) +set(TRANTOR_PATCH_VERSION 23) +set(TRANTOR_VERSION ${TRANTOR_MAJOR_VERSION}.${TRANTOR_MINOR_VERSION}.${TRANTOR_PATCH_VERSION}) include(GNUInstallDirs) # Offer the user the choice of overriding the installation directories -set(INSTALL_BIN_DIR ${CMAKE_INSTALL_BINDIR} CACHE PATH "Installation directory for binaries") -set(INSTALL_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE PATH "Installation directory for libraries") +set(INSTALL_BIN_DIR + ${CMAKE_INSTALL_BINDIR} + CACHE PATH "Installation directory for binaries" +) +set(INSTALL_LIB_DIR + ${CMAKE_INSTALL_LIBDIR} + CACHE PATH "Installation directory for libraries" +) set(INSTALL_INCLUDE_DIR ${CMAKE_INSTALL_INCLUDEDIR} - CACHE PATH "Installation directory for header files") + CACHE PATH "Installation directory for header files" +) set(DEF_INSTALL_TRANTOR_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/Trantor) set(INSTALL_TRANTOR_CMAKE_DIR ${DEF_INSTALL_TRANTOR_CMAKE_DIR} - CACHE PATH "Installation directory for cmake files") + CACHE PATH "Installation directory for cmake files" +) add_library(${PROJECT_NAME}) if(BUILD_SHARED_LIBS) - list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES - "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR}" isSystemDir) + list( + FIND + CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES + "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR}" + isSystemDir + ) if("${isSystemDir}" STREQUAL "-1") set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR}") endif("${isSystemDir}" STREQUAL "-1") - set_target_properties(${PROJECT_NAME} PROPERTIES - VERSION ${TRANTOR_VERSION} - SOVERSION ${TRANTOR_MAJOR_VERSION}) + set_target_properties( + ${PROJECT_NAME} + PROPERTIES VERSION + ${TRANTOR_VERSION} + SOVERSION + ${TRANTOR_MAJOR_VERSION} + ) if(CMAKE_CXX_COMPILER_ID MATCHES MSVC) - # Ignore MSVC C4251 and C4275 warning of exporting std objects with no dll export - # We export class to facilitate maintenance, thus if you compile - # drogon on windows as a shared library, you will need to use - # exact same compiler for drogon and your app. + # Ignore MSVC C4251 and C4275 warning of exporting std objects with no dll export We export class to facilitate + # maintenance, thus if you compile drogon on windows as a shared library, you will need to use exact same compiler + # for drogon and your app. target_compile_options(${PROJECT_NAME} PUBLIC /wd4251 /wd4275) endif() endif(BUILD_SHARED_LIBS) -# Tells Visual Studio 2017 (15.7+) and newer to correctly set the value of the standard __cplusplus macro, -# instead of leaving it to 199711L and settings the effective c++ version in _MSVC_LANG -# Dropping support for older versions of VS would allow to only rely on __cplusplus +# Tells Visual Studio 2017 (15.7+) and newer to correctly set the value of the standard __cplusplus macro, instead of +# leaving it to 199711L and settings the effective c++ version in _MSVC_LANG Dropping support for older versions of VS +# would allow to only rely on __cplusplus if(MSVC AND MSVC_VERSION GREATER_EQUAL 1914) add_compile_options(/Zc:__cplusplus) endif(MSVC AND MSVC_VERSION GREATER_EQUAL 1914) -if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" AND CMAKE_CXX_COMPILER_ID MATCHES Clang|GNU) - target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Werror) +if(NOT + ${CMAKE_SYSTEM_NAME} + STREQUAL + "Windows" + AND CMAKE_CXX_COMPILER_ID MATCHES Clang|GNU +) + target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Werror) endif() if(${CMAKE_SYSTEM_NAME} STREQUAL "Haiku") - target_link_libraries(${PROJECT_NAME} PRIVATE network) + target_link_libraries(${PROJECT_NAME} PRIVATE network) endif() include(GenerateExportHeader) @@ -68,19 +89,17 @@ generate_export_header(${PROJECT_NAME} EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_D # include directories target_include_directories( ${PROJECT_NAME} - PUBLIC $ - $ + PUBLIC $ $ $ PRIVATE ${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/trantor/utils ${PROJECT_SOURCE_DIR}/trantor/net ${PROJECT_SOURCE_DIR}/trantor/net/inner - $) + $ +) if(MINGW) - target_compile_definitions( - ${PROJECT_NAME} - PUBLIC -D_WIN32_WINNT=0x0601) + target_compile_definitions(${PROJECT_NAME} PUBLIC -D_WIN32_WINNT=0x0601) endif(MINGW) set(TRANTOR_SOURCES @@ -104,12 +123,16 @@ set(TRANTOR_SOURCES trantor/net/inner/Connector.cc trantor/net/inner/Poller.cc trantor/net/inner/Socket.cc + trantor/net/inner/MemBufferNode.cc + trantor/net/inner/StreamBufferNode.cc + trantor/net/inner/AsyncStreamBufferNode.cc trantor/net/inner/TcpConnectionImpl.cc trantor/net/inner/Timer.cc trantor/net/inner/TimerQueue.cc trantor/net/inner/poller/EpollPoller.cc trantor/net/inner/poller/KQueue.cc - trantor/net/inner/poller/PollPoller.cc) + trantor/net/inner/poller/PollPoller.cc +) set(private_headers trantor/net/inner/Acceptor.h trantor/net/inner/Connector.h @@ -120,17 +143,19 @@ set(private_headers trantor/net/inner/TimerQueue.h trantor/net/inner/poller/EpollPoller.h trantor/net/inner/poller/KQueue.h - trantor/net/inner/poller/PollPoller.h) + trantor/net/inner/poller/PollPoller.h +) if(WIN32) set(TRANTOR_SOURCES ${TRANTOR_SOURCES} third_party/wepoll/Wepoll.c - trantor/utils/WindowsSupport.cc) - set(private_headers - ${private_headers} - third_party/wepoll/Wepoll.h - trantor/utils/WindowsSupport.h) + trantor/utils/WindowsSupport.cc + trantor/net/inner/FileBufferNodeWin.cc + ) + set(private_headers ${private_headers} third_party/wepoll/Wepoll.h trantor/utils/WindowsSupport.h) +else(WIN32) + set(TRANTOR_SOURCES ${TRANTOR_SOURCES} trantor/net/inner/FileBufferNodeUnix.cc) endif(WIN32) # Somehow the default value of TRANTOR_USE_TLS is OFF @@ -138,10 +163,19 @@ if(TRANTOR_USE_TLS STREQUAL OFF) set(TRANTOR_USE_TLS "") endif() set(VALID_TLS_PROVIDERS "openssl" "botan" "none") -list(FIND VALID_TLS_PROVIDERS "${TRANTOR_USE_TLS}" PREFERED_TLS_IDX) -if(PREFERED_TLS_IDX EQUAL -1 AND NOT TRANTOR_USE_TLS STREQUAL "") - message(FATAL_ERROR "Invalid TLS provider: ${TRANTOR_USE_TLS}\n" - "Valid TLS providers are: ${VALID_TLS_PROVIDERS}") +list( + FIND + VALID_TLS_PROVIDERS + "${TRANTOR_USE_TLS}" + PREFERED_TLS_IDX +) +if(PREFERED_TLS_IDX EQUAL -1 + AND NOT + TRANTOR_USE_TLS + STREQUAL + "" +) + message(FATAL_ERROR "Invalid TLS provider: ${TRANTOR_USE_TLS}\n" "Valid TLS providers are: ${VALID_TLS_PROVIDERS}") endif() set(TRANTOR_TLS_PROVIDER "None") @@ -152,27 +186,28 @@ if(TRANTOR_USE_TLS STREQUAL "openssl" OR TRANTOR_USE_TLS STREQUAL "") target_compile_definitions(${PROJECT_NAME} PRIVATE USE_OPENSSL) set(TRANTOR_TLS_PROVIDER "OpenSSL") - set(TRANTOR_SOURCES - ${TRANTOR_SOURCES} - trantor/net/inner/tlsprovider/OpenSSLProvider.cc - trantor/utils/crypto/openssl.cc) + set(TRANTOR_SOURCES ${TRANTOR_SOURCES} trantor/net/inner/tlsprovider/OpenSSLProvider.cc + trantor/utils/crypto/openssl.cc + ) elseif(TRANTOR_USE_TLS STREQUAL "openssl") message(FATAL_ERROR "Requested OpenSSL TLS provider but OpenSSL was not found") endif() endif() -if(TRANTOR_TLS_PROVIDER STREQUAL "None" - AND (TRANTOR_USE_TLS STREQUAL "botan" OR TRANTOR_USE_TLS STREQUAL "")) +if(TRANTOR_TLS_PROVIDER STREQUAL "None" AND (TRANTOR_USE_TLS STREQUAL "botan" OR TRANTOR_USE_TLS STREQUAL "")) find_package(Botan) if(Botan_FOUND) target_compile_definitions(${PROJECT_NAME} PRIVATE USE_BOTAN) target_link_libraries(${PROJECT_NAME} PRIVATE Botan::Botan) + if(CMAKE_CXX_COMPILER_ID MATCHES Clang|GNU) + # Trantor uses some features that are deprecated in C++20 but Botan3 needs C++20 + target_compile_options(${PROJECT_NAME} PRIVATE -Wno-deprecated) + endif() set(TRANTOR_TLS_PROVIDER "Botan") - set(TRANTOR_SOURCES - ${TRANTOR_SOURCES} - trantor/net/inner/tlsprovider/BotanTLSProvider.cc - trantor/utils/crypto/botan.cc) + set(TRANTOR_SOURCES ${TRANTOR_SOURCES} trantor/net/inner/tlsprovider/BotanTLSProvider.cc + trantor/utils/crypto/botan.cc + ) elseif(TRANTOR_USE_TLS STREQUAL "botan") message(FATAL_ERROR "Requested Botan TLS provider but Botan was not found") endif() @@ -185,13 +220,15 @@ if(TRANTOR_TLS_PROVIDER STREQUAL "None") trantor/utils/crypto/md5.cc trantor/utils/crypto/sha1.cc trantor/utils/crypto/sha256.cc - trantor/utils/crypto/blake2.cc) + trantor/utils/crypto/blake2.cc + ) set(private_headers ${private_headers} trantor/utils/crypto/sha3.h trantor/utils/crypto/md5.h trantor/utils/crypto/sha1.h - trantor/utils/crypto/sha256.h) + trantor/utils/crypto/sha256.h + ) endif() message(STATUS "Trantor using SSL library: ${TRANTOR_TLS_PROVIDER}") @@ -211,54 +248,49 @@ if(HAVE_SPDLOG) endif(HAVE_SPDLOG) set(HAVE_C-ARES NO) -if (BUILD_C-ARES) - find_package(c-ares) - if(c-ares_FOUND) - message(STATUS "c-ares found!") - set(HAVE_C-ARES TRUE) - endif() -endif () +if(BUILD_C-ARES) + find_package(c-ares) + if(c-ares_FOUND) + message(STATUS "c-ares found!") + set(HAVE_C-ARES TRUE) + endif() +endif() if(HAVE_C-ARES) + if(NOT BUILD_SHARED_LIBS) + target_compile_definitions(${PROJECT_NAME} PRIVATE CARES_STATICLIB) + endif() target_link_libraries(${PROJECT_NAME} PRIVATE c-ares_lib) - set(TRANTOR_SOURCES - ${TRANTOR_SOURCES} - trantor/net/inner/AresResolver.cc) - set(private_headers - ${private_headers} - trantor/net/inner/AresResolver.h) + set(TRANTOR_SOURCES ${TRANTOR_SOURCES} trantor/net/inner/AresResolver.cc) + set(private_headers ${private_headers} trantor/net/inner/AresResolver.h) if(APPLE) target_link_libraries(${PROJECT_NAME} PRIVATE resolv) elseif(WIN32) - target_link_libraries(${PROJECT_NAME} PRIVATE Iphlpapi) + target_link_libraries(${PROJECT_NAME} PRIVATE iphlpapi) endif() else() - set(TRANTOR_SOURCES - ${TRANTOR_SOURCES} - trantor/net/inner/NormalResolver.cc) - set(private_headers - ${private_headers} - trantor/net/inner/NormalResolver.h) + set(TRANTOR_SOURCES ${TRANTOR_SOURCES} trantor/net/inner/NormalResolver.cc) + set(private_headers ${private_headers} trantor/net/inner/NormalResolver.h) endif() find_package(Threads) target_link_libraries(${PROJECT_NAME} PUBLIC Threads::Threads) if(WIN32) - target_link_libraries(${PROJECT_NAME} PRIVATE ws2_32 Rpcrt4) + target_link_libraries(${PROJECT_NAME} PRIVATE ws2_32 rpcrt4) if(OpenSSL_FOUND) - target_link_libraries(${PROJECT_NAME} PRIVATE Crypt32 Secur32) + target_link_libraries(${PROJECT_NAME} PRIVATE crypt32 secur32) endif(OpenSSL_FOUND) -else(WIN32) +elseif(NOT ANDROID) target_link_libraries(${PROJECT_NAME} PRIVATE pthread $<$:socket>) endif(WIN32) -file(WRITE ${CMAKE_BINARY_DIR}/test_atomic.cpp - "#include \n" - "int main() { std::atomic i(0); i++; return 0; }\n") +file(WRITE ${CMAKE_BINARY_DIR}/test_atomic.cpp "#include \n" + "int main() { std::atomic i(0); i++; return 0; }\n" +) try_compile(ATOMIC_WITHOUT_LINKING ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/test_atomic.cpp) -if (NOT ATOMIC_WITHOUT_LINKING) - target_link_libraries(${PROJECT_NAME} PUBLIC atomic) -endif () +if(NOT ATOMIC_WITHOUT_LINKING) + target_link_libraries(${PROJECT_NAME} PUBLIC atomic) +endif() file(REMOVE ${CMAKE_BINARY_DIR}/test_atomic.cpp) set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 14) @@ -283,11 +315,13 @@ set(public_net_headers trantor/net/TcpClient.h trantor/net/TcpConnection.h trantor/net/TcpServer.h + trantor/net/AsyncStream.h trantor/net/callbacks.h trantor/net/Resolver.h trantor/net/Channel.h trantor/net/Certificate.h - trantor/net/TLSPolicy.h) + trantor/net/TLSPolicy.h +) set(public_utils_headers trantor/utils/AsyncFileLogger.h @@ -303,67 +337,68 @@ set(public_utils_headers trantor/utils/SerialTaskQueue.h trantor/utils/TaskQueue.h trantor/utils/TimingWheel.h - trantor/utils/Utilities.h) - -target_sources(${PROJECT_NAME} PRIVATE - ${TRANTOR_SOURCES} - ${CMAKE_CURRENT_BINARY_DIR}/exports/trantor/exports.h - ${public_net_headers} - ${public_utils_headers} - ${private_headers}) - -source_group("Public API" - FILES - ${CMAKE_CURRENT_BINARY_DIR}/exports/trantor/exports.h - ${public_net_headers} - ${public_utils_headers}) - -source_group("Private Headers" - FILES - ${private_headers}) - -install(TARGETS trantor - # IMPORTANT: Add the trantor library to the "export-set" - EXPORT TrantorTargets - RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin - ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" COMPONENT lib - LIBRARY DESTINATION "${INSTALL_LIB_DIR}" COMPONENT lib) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/exports/trantor/exports.h - DESTINATION ${INSTALL_INCLUDE_DIR}/trantor) -install(FILES ${public_net_headers} - DESTINATION ${INSTALL_INCLUDE_DIR}/trantor/net) -install(FILES ${public_utils_headers} - DESTINATION ${INSTALL_INCLUDE_DIR}/trantor/utils) + trantor/utils/Utilities.h +) + +target_sources( + ${PROJECT_NAME} + PRIVATE ${TRANTOR_SOURCES} + ${CMAKE_CURRENT_BINARY_DIR}/exports/trantor/exports.h + ${public_net_headers} + ${public_utils_headers} + ${private_headers} +) + +source_group( + "Public API" FILES ${CMAKE_CURRENT_BINARY_DIR}/exports/trantor/exports.h ${public_net_headers} + ${public_utils_headers} +) + +source_group("Private Headers" FILES ${private_headers}) + +install( + TARGETS trantor + # IMPORTANT: Add the trantor library to the "export-set" + EXPORT TrantorTargets + RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin + ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" COMPONENT lib + LIBRARY DESTINATION "${INSTALL_LIB_DIR}" COMPONENT lib +) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/exports/trantor/exports.h DESTINATION ${INSTALL_INCLUDE_DIR}/trantor) +install(FILES ${public_net_headers} DESTINATION ${INSTALL_INCLUDE_DIR}/trantor/net) +install(FILES ${public_utils_headers} DESTINATION ${INSTALL_INCLUDE_DIR}/trantor/utils) include(CMakePackageConfigHelpers) # ... for the install tree configure_package_config_file( - cmake/templates/TrantorConfig.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TrantorConfig.cmake - INSTALL_DESTINATION - ${INSTALL_TRANTOR_CMAKE_DIR}) + cmake/templates/TrantorConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TrantorConfig.cmake + INSTALL_DESTINATION ${INSTALL_TRANTOR_CMAKE_DIR} +) # version write_basic_package_version_file( ${CMAKE_CURRENT_BINARY_DIR}/TrantorConfigVersion.cmake VERSION ${TRANTOR_VERSION} - COMPATIBILITY SameMajorVersion) + COMPATIBILITY SameMajorVersion +) # Install the TrantorConfig.cmake and TrantorConfigVersion.cmake install( - FILES - "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TrantorConfig.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/TrantorConfigVersion.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/Findc-ares.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/FindBotan.cmake" + FILES "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TrantorConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/TrantorConfigVersion.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/Findc-ares.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/FindBotan.cmake" DESTINATION "${INSTALL_TRANTOR_CMAKE_DIR}" - COMPONENT dev) + COMPONENT dev +) # Install the export set for use with the install-tree -install(EXPORT TrantorTargets - DESTINATION "${INSTALL_TRANTOR_CMAKE_DIR}" - NAMESPACE Trantor:: - COMPONENT dev) +install( + EXPORT TrantorTargets + DESTINATION "${INSTALL_TRANTOR_CMAKE_DIR}" + NAMESPACE Trantor:: + COMPONENT dev +) # Doxygen documentation find_package(Doxygen OPTIONAL_COMPONENTS dot dia) @@ -373,27 +408,29 @@ if(DOXYGEN_FOUND) set(DOXYGEN_GENERATE_LATEX NO) set(DOXYGEN_BUILTIN_STL_SUPPORT YES) set(DOXYGEN_USE_MDFILE_AS_MAINPAGE README.md) - set(DOXYGEN_STRIP_FROM_INC_PATH ${PROJECT_SOURCE_DIR} - ${CMAKE_CURRENT_BINARY_DIR}/exports) - if (WIN32) + set(DOXYGEN_STRIP_FROM_INC_PATH ${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/exports) + if(WIN32) set(DOXYGEN_PREDEFINED _WIN32) endif(WIN32) - doxygen_add_docs(doc_${PROJECT_NAME} - README.md - ChangeLog.md - ${public_net_headers} - ${public_utils_headers} - COMMENT "Generate documentation") + doxygen_add_docs( + doc_${PROJECT_NAME} + README.md + ChangeLog.md + ${public_net_headers} + ${public_utils_headers} + COMMENT "Generate documentation" + ) if(NOT TARGET doc) add_custom_target(doc) endif() add_dependencies(doc doc_${PROJECT_NAME}) - if (BUILD_DOC) + if(BUILD_DOC) add_dependencies(${PROJECT_NAME} doc_${PROJECT_NAME}) # Don't install twice, so limit to Debug (assume developer) - install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/docs/${PROJECT_NAME} - TYPE DOC - CONFIGURATIONS Debug) + install( + DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/docs/${PROJECT_NAME} + TYPE DOC + CONFIGURATIONS Debug + ) endif(BUILD_DOC) endif(DOXYGEN_FOUND) - diff --git a/ChangeLog.md b/ChangeLog.md index 4e040d33..a00e5d67 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -4,6 +4,96 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [1.5.23] - 2025-02-20 + +### Changed + +- Replace ipv4 inet_ntop with a handrolled function. + +### Fixed + +- Fix some typos. + +## [1.5.22] - 2024-10-27 + +### Fixed + +- Fix a bug in the dtor of EventLoop. + +- Free leaked memory in ares resolver. + +## [1.5.21] - 2024-09-10 + +### API changes list + +- Add a method to reload the SSL certificate and private key on the fly. + +### Changed + +- Keep log level consistency. + +## [1.5.20] - 2024-07-20 + +### Changed + +- Add byte order detection for internal SHA1 implementation for OSX, POWER, RISC-V and s390. + +### Fixed + +- Fix Windows CI build fail by using the latest MSVC. + +- Fix the Botan TLS provider build on Linux. + +- Fix "pthread not found" build error when using Android NDK. + +## [1.5.19] - 2024-06-08 + +### changed + +- show forked repository build status. + +- Add cmake-format. + +- Some spelling corrections. + +## [1.5.18] - 2024-05-04 + +### Fixed + +- Fix data type conflict. + +- Fix build on latest c-ares. + +## [1.5.17] - 2024-02-09 + +### Changed + +- Make FileBufferNodeWin aware of UWP Win32 API. + +- Use ssize_t declared by toolchain when available. + +## [1.5.16] - 2024-01-18 + +### Changed + +- Add build badge for individual OS. + +- deinit libressl. + +- Remove mutex. + +### Fixed + +- Pile of fix for h2. + +- Fix a bug when sending data. + +- Fix c-ares CARES_EXTERN for static builds. + +- Fix header file name issue when cross-compiling on Windows. + +- Fix name issue when cross-compiling. + ## [1.5.15] - 2023-11-27 ### Changed @@ -606,7 +696,23 @@ All notable changes to this project will be documented in this file. ## [1.0.0-rc1] - 2019-06-11 -[Unreleased]: https://github.com/an-tao/trantor/compare/v1.5.15...HEAD +[Unreleased]: https://github.com/an-tao/trantor/compare/v1.5.23...HEAD + +[1.5.23]: https://github.com/an-tao/trantor/compare/v1.5.22...v1.5.23 + +[1.5.22]: https://github.com/an-tao/trantor/compare/v1.5.21...v1.5.22 + +[1.5.21]: https://github.com/an-tao/trantor/compare/v1.5.20...v1.5.21 + +[1.5.20]: https://github.com/an-tao/trantor/compare/v1.5.19...v1.5.20 + +[1.5.19]: https://github.com/an-tao/trantor/compare/v1.5.18...v1.5.19 + +[1.5.18]: https://github.com/an-tao/trantor/compare/v1.5.17...v1.5.18 + +[1.5.17]: https://github.com/an-tao/trantor/compare/v1.5.16...v1.5.17 + +[1.5.16]: https://github.com/an-tao/trantor/compare/v1.5.15...v1.5.16 [1.5.15]: https://github.com/an-tao/trantor/compare/v1.5.14...v1.5.15 diff --git a/README.md b/README.md index a76aa4e0..33bb7c2d 100755 --- a/README.md +++ b/README.md @@ -1,22 +1,21 @@ # TRANTOR - -[![Build Status](https://travis-ci.org/an-tao/trantor.svg?branch=master)](https://travis-ci.org/an-tao/trantor) -[![Build status](https://ci.appveyor.com/api/projects/status/yn8xunsubn37pi1u/branch/master?svg=true)](https://ci.appveyor.com/project/an-tao/trantor/branch/master) -[![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/an-tao/trantor.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/an-tao/trantor/context:cpp) - +[![Build Ubuntu gcc](../../actions/workflows/ubuntu-gcc.yml/badge.svg)](../../actions/workflows/ubuntu-gcc.yml/badge.svg) +[![Build Macos clang](../../actions/workflows/macos-clang.yml/badge.svg)](../../actions/workflows/macos-clang.yml/badge.svg) +[![Build RockyLinux gcc](../../actions/workflows/rockylinux-gcc.yml/badge.svg)](../../actions/workflows/rockylinux-gcc.yml/badge.svg) +[![Build Windows msvc](../../actions/workflows/windows-msvc.yml/badge.svg)](../../actions/workflows/windows-msvc.yml/badge.svg) ## Overview A non-blocking I/O cross-platform TCP network library, using C++14. Drawing on the design of Muduo Library -## suported platforms +## Supported platforms - Linux - MacOS - UNIX(BSD) - Windows ## Feature highlights -- non-blocking I/O +- Non-blocking I/O - cross-platform - Thread pool - Lock free design @@ -28,7 +27,7 @@ Drawing on the design of Muduo Library ```shell git clone https://github.com/an-tao/trantor.git cd trantor -cmake -Bbuild -H. +cmake -B build -H. cd build make -j ``` diff --git a/cmake/templates/TrantorConfig.cmake.in b/cmake/templates/TrantorConfig.cmake.in index 61007bdd..e9422ed9 100644 --- a/cmake/templates/TrantorConfig.cmake.in +++ b/cmake/templates/TrantorConfig.cmake.in @@ -1,3 +1,4 @@ +#[[ # - Config file for the Trantor package # It defines the following variables # TRANTOR_INCLUDE_DIRS - include directories for Trantor @@ -5,6 +6,7 @@ # Trantor_FOUND # This module defines the following IMPORTED target: # Trantor::Trantor +#]] @PACKAGE_INIT@ diff --git a/cmake_modules/FindBotan.cmake b/cmake_modules/FindBotan.cmake index b4b002ca..e09ce039 100644 --- a/cmake_modules/FindBotan.cmake +++ b/cmake_modules/FindBotan.cmake @@ -1,28 +1,39 @@ function(find_botan_pkgconfig package_name botan_ver) - if (TARGET Botan::Botan) + if(TARGET Botan::Botan) return() - endif () + endif() - pkg_check_modules(Botan QUIET IMPORTED_TARGET ${package_name}) - if (TARGET PkgConfig::Botan) + pkg_check_modules( + Botan + QUIET + IMPORTED_TARGET + ${package_name} + ) + if(TARGET PkgConfig::Botan) add_library(Botan::Botan ALIAS PkgConfig::Botan) if(botan_ver EQUAL 3) target_compile_features(PkgConfig::Botan INTERFACE cxx_std_20) endif() - endif () + endif() endfunction() function(find_botan_search package_name botan_ver) - if (TARGET Botan::Botan) + if(TARGET Botan::Botan) return() - endif () - find_path(Botan_INCLUDE_DIRS NAMES botan/botan.h - PATH_SUFFIXES ${package_name} - DOC "The Botan include directory") + endif() + find_path( + Botan_INCLUDE_DIRS + NAMES botan/botan.h + PATH_SUFFIXES ${package_name} + DOC "The Botan include directory" + ) - find_library(Botan_LIBRARIES NAMES botan ${package_name} - DOC "The Botan library") + find_library( + Botan_LIBRARIES + NAMES botan ${package_name} + DOC "The Botan library" + ) mark_as_advanced(Botan_INCLUDE_DIRS Botan_LIBRARIES) @@ -37,12 +48,11 @@ function(find_botan_search package_name botan_ver) target_compile_features(Botan::Botan INTERFACE cxx_std_20) endif() - if (WIN32) + if(WIN32) target_compile_definitions(Botan::Botan INTERFACE -DNOMINMAX=1) - endif () + endif() endfunction() - find_package(PkgConfig) if(NOT WIN32 AND PKG_CONFIG_FOUND) # find_botan_pkgconfig(botan-2 2) @@ -55,7 +65,4 @@ if(NOT TARGET Botan::Botan) endif() include(FindPackageHandleStandardArgs) -find_package_handle_standard_args( - Botan - REQUIRED_VARS Botan_LIBRARIES Botan_INCLUDE_DIRS -) +find_package_handle_standard_args(Botan REQUIRED_VARS Botan_LIBRARIES Botan_INCLUDE_DIRS) diff --git a/cmake_modules/Findc-ares.cmake b/cmake_modules/Findc-ares.cmake index 98821437..73334b72 100644 --- a/cmake_modules/Findc-ares.cmake +++ b/cmake_modules/Findc-ares.cmake @@ -1,27 +1,30 @@ +#[[ # Try to find c-ares library Once done this will define # -# c-ares_FOUND - system has c-ares -# C-ARES_INCLUDE_DIRS - The c-ares include directory +# c-ares_FOUND - system has c-ares +# C-ARES_INCLUDE_DIRS - The c-ares include directory # C-ARES_LIBRARIES - Link these to use c-ares # c-ares_lib - Imported Targets # # Copyright (c) 2020 antao -# +#]] find_path(C-ARES_INCLUDE_DIRS ares.h) find_library(C-ARES_LIBRARIES NAMES cares) if(C-ARES_INCLUDE_DIRS AND C-ARES_LIBRARIES) add_library(c-ares_lib INTERFACE IMPORTED) - set_target_properties(c-ares_lib - PROPERTIES INTERFACE_INCLUDE_DIRECTORIES - "${C-ARES_INCLUDE_DIRS}" - INTERFACE_LINK_LIBRARIES - "${C-ARES_LIBRARIES}") + set_target_properties( + c-ares_lib + PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${C-ARES_INCLUDE_DIRS}" INTERFACE_LINK_LIBRARIES "${C-ARES_LIBRARIES}" + ) endif() include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(c-ares - DEFAULT_MSG - C-ARES_INCLUDE_DIRS - C-ARES_LIBRARIES) +find_package_handle_standard_args( + c-ares + DEFAULT_MSG + C-ARES_INCLUDE_DIRS + C-ARES_LIBRARIES +) mark_as_advanced(C-ARES_INCLUDE_DIRS C-ARES_LIBRARIES) diff --git a/format.sh b/format.sh index a1bde595..9510d04b 100755 --- a/format.sh +++ b/format.sh @@ -4,3 +4,9 @@ clang-format --version find trantor -name *.h -o -name *.cc -exec dos2unix {} \; find trantor -name *.h -o -name *.cc|xargs clang-format -i -style=file + +cmake-format --version +find . -maxdepth 1 -name CMakeLists.txt|xargs cmake-format -i +find trantor -name CMakeLists.txt|xargs cmake-format -i +find cmake -name *.cmake -o -name *.cmake.in|xargs cmake-format -i +find cmake_modules -name *.cmake -o -name *.cmake.in|xargs cmake-format -i \ No newline at end of file diff --git a/third_party/wepoll/Wepoll.c b/third_party/wepoll/Wepoll.c index 5b8ee517..ed37d7f7 100644 --- a/third_party/wepoll/Wepoll.c +++ b/third_party/wepoll/Wepoll.c @@ -902,7 +902,7 @@ int init(void) /* `InitOnceExecuteOnce()` itself is infallible, and it doesn't set any * error code when the once-callback returns FALSE. We return -1 here to * indicate that global initialization failed; the failing init function - * is resposible for setting `errno` and calling `SetLastError()`. */ + * is responsible for setting `errno` and calling `SetLastError()`. */ return -1; return 0; diff --git a/trantor/net/AsyncStream.h b/trantor/net/AsyncStream.h new file mode 100644 index 00000000..c459f9ea --- /dev/null +++ b/trantor/net/AsyncStream.h @@ -0,0 +1,51 @@ +/** + * + * @file AsyncStream.h + * @author An Tao + * + * Public header file in trantor lib. + * + * Copyright 2023, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once + +#include +#include + +namespace trantor +{ +/** + * @brief This class represents a data stream that can be sent asynchronously. + * The data is sent in chunks, and the chunks are sent in order, and all the + * chunks are sent continuously. + */ +class TRANTOR_EXPORT AsyncStream : public NonCopyable +{ + public: + virtual ~AsyncStream() = default; + /** + * @brief Send data asynchronously. + * + * @param data The data to be sent + * @param len The length of the data + * @return true if the data is sent successfully or at least is put in the + * send buffer. + * @return false if the connection is closed. + */ + virtual bool send(const char *data, size_t len) = 0; + bool send(const std::string &data) + { + return send(data.data(), data.length()); + } + /** + * @brief Terminate the stream. + */ + virtual void close() = 0; +}; +using AsyncStreamPtr = std::unique_ptr; +} // namespace trantor \ No newline at end of file diff --git a/trantor/net/EventLoop.cc b/trantor/net/EventLoop.cc index acaa4713..17de454c 100644 --- a/trantor/net/EventLoop.cc +++ b/trantor/net/EventLoop.cc @@ -28,7 +28,9 @@ #include #include #include +#ifndef _SSIZE_T_DEFINED using ssize_t = long long; +#endif #else #include #endif @@ -43,6 +45,7 @@ using ssize_t = long long; #include #include #include +#include namespace trantor { @@ -136,7 +139,6 @@ EventLoop::~EventLoop() #endif } - t_loopInThisThread = nullptr; #ifdef __linux__ close(wakeupFd_); #elif defined _WIN32 @@ -254,7 +256,7 @@ void EventLoop::loop() { f(); } - + t_loopInThisThread = nullptr; // Throw the exception from the end if (loopException) { diff --git a/trantor/net/EventLoop.h b/trantor/net/EventLoop.h index fd0253d2..00b7ebf7 100644 --- a/trantor/net/EventLoop.h +++ b/trantor/net/EventLoop.h @@ -45,7 +45,7 @@ enum /** * @brief As the name implies, this class represents an event loop that runs in - * a perticular thread. The event loop can handle network I/O events and timers + * a particular thread. The event loop can handle network I/O events and timers * in asynchronous mode. * @note An event loop object always belongs to a separate thread, and there is * one event loop object at most in a thread. We can call an event loop object diff --git a/trantor/net/InetAddress.cc b/trantor/net/InetAddress.cc index 245f27ea..08a0aa42 100644 --- a/trantor/net/InetAddress.cc +++ b/trantor/net/InetAddress.cc @@ -196,16 +196,40 @@ bool InetAddress::isLoopbackIp() const return false; } +static void byteToChars(std::string::iterator &dst, unsigned char byte) +{ + *dst = byte / 100 + '0'; + dst += byte >= 100; + *dst = byte % 100 / 10 + '0'; + dst += byte >= 10; + *dst = byte % 10 + '0'; + ++dst; +} + +static std::string iptos(unsigned inet_addr) +{ + // Initialize with a static buffer to force the constructor of string to get + // fully inlined + constexpr char stringInitBuffer[15]{}; + std::string out(stringInitBuffer, 15); + std::string::iterator dst = out.begin(); + byteToChars(dst, inet_addr >> 0 & 0xff); + *(dst++) = '.'; + byteToChars(dst, inet_addr >> 8 & 0xff); + *(dst++) = '.'; + byteToChars(dst, inet_addr >> 16 & 0xff); + *(dst++) = '.'; + byteToChars(dst, inet_addr >> 24 & 0xff); + out.erase(dst, out.end()); + return out; +} + std::string InetAddress::toIp() const { - char buf[64]; + char buf[INET6_ADDRSTRLEN]{}; if (addr_.sin_family == AF_INET) { -#if defined _WIN32 - ::inet_ntop(AF_INET, (PVOID)&addr_.sin_addr, buf, sizeof(buf)); -#else - ::inet_ntop(AF_INET, &addr_.sin_addr, buf, sizeof(buf)); -#endif + return iptos(addr_.sin_addr.s_addr); } else if (addr_.sin_family == AF_INET6) { diff --git a/trantor/net/InetAddress.h b/trantor/net/InetAddress.h index 8f8d3687..a8f7eb66 100644 --- a/trantor/net/InetAddress.h +++ b/trantor/net/InetAddress.h @@ -219,7 +219,7 @@ class TRANTOR_EXPORT InetAddress } /** - * @brief Return true if the address is not initalized. + * @brief Return true if the address is not initialized. */ inline bool isUnspecified() const { diff --git a/trantor/net/TLSPolicy.h b/trantor/net/TLSPolicy.h index 87756a34..e59aca03 100644 --- a/trantor/net/TLSPolicy.h +++ b/trantor/net/TLSPolicy.h @@ -67,7 +67,7 @@ struct TRANTOR_EXPORT TLSPolicy final /** * @brief enables the use of the old TLS protocol (old meaning < TLS 1.2). - * TLS providres may not support old protocols even if this option is set + * TLS providers may not support old protocols even if this option is set */ TLSPolicy &setUseOldTLS(bool useOldTLS) { @@ -79,7 +79,7 @@ struct TRANTOR_EXPORT TLSPolicy final * @brief set the list of protocols to be used for ALPN. * * @note for servers, it selects matching protocol against the client's - * list. And the first matching protocol supplide in the parameter will be + * list. And the first matching protocol supplied in the parameter will be * selected. If no matching protocol is found, the connection will be * closed. * diff --git a/trantor/net/TcpConnection.h b/trantor/net/TcpConnection.h index 1284e6c9..41626bcd 100644 --- a/trantor/net/TcpConnection.h +++ b/trantor/net/TcpConnection.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -69,8 +70,8 @@ class TRANTOR_EXPORT TcpConnection * @param length */ virtual void sendFile(const char *fileName, - size_t offset = 0, - size_t length = 0) = 0; + long long offset = 0, + long long length = 0) = 0; /** * @brief Send a file to the peer. * @@ -79,8 +80,8 @@ class TRANTOR_EXPORT TcpConnection * @param length */ virtual void sendFile(const wchar_t *fileName, - size_t offset = 0, - size_t length = 0) = 0; + long long offset = 0, + long long length = 0) = 0; /** * @brief Send a stream to the peer. * @@ -95,11 +96,20 @@ class TRANTOR_EXPORT TcpConnection callback) = 0; // (buffer, buffer size) -> size // of data put in buffer + /** + * @brief Send a stream to the peer asynchronously. + * @param disableKickoff Disable the kickoff mechanism. If this parameter is + * enabled, the connection will not be closed after the inactive timeout. + * @note The subsequent data sent after the async stream will be sent after + * the stream is closed. + */ + virtual AsyncStreamPtr sendAsyncStream(bool disableKickoff = false) = 0; /** * @brief Get the local address of the connection. * * @return const InetAddress& */ + virtual const InetAddress &localAddr() const = 0; /** diff --git a/trantor/net/TcpServer.cc b/trantor/net/TcpServer.cc index 8f506092..d256b9c1 100644 --- a/trantor/net/TcpServer.cc +++ b/trantor/net/TcpServer.cc @@ -234,3 +234,23 @@ void TcpServer::enableSSL( .setValidate(caPath.empty() ? false : true); sslContextPtr_ = newSSLContext(*policyPtr_, true); } + +void TcpServer::reloadSSL() +{ + if (loop_->isInLoopThread()) + { + if (policyPtr_) + { + sslContextPtr_ = newSSLContext(*policyPtr_, true); + } + } + else + { + loop_->queueInLoop([this]() { + if (policyPtr_) + { + sslContextPtr_ = newSSLContext(*policyPtr_, true); + } + }); + } +} \ No newline at end of file diff --git a/trantor/net/TcpServer.h b/trantor/net/TcpServer.h index bf7aaf0d..33b041b8 100644 --- a/trantor/net/TcpServer.h +++ b/trantor/net/TcpServer.h @@ -260,6 +260,14 @@ class TRANTOR_EXPORT TcpServer : NonCopyable sslContextPtr_ = newSSLContext(*policyPtr_, true); } + /** + * @brief Reload the SSL context. + * @note Call this function when the certificate or private key is updated. + * The server will reload the SSL context and use the new certificate and + * private key. new connections will use the new SSL context. + */ + void reloadSSL(); + private: void handleCloseInLoop(const TcpConnectionPtr &connectionPtr); void newConnection(int fd, const InetAddress &peer); diff --git a/trantor/net/inner/Acceptor.cc b/trantor/net/inner/Acceptor.cc index 84e054c0..0b8249cb 100644 --- a/trantor/net/inner/Acceptor.cc +++ b/trantor/net/inner/Acceptor.cc @@ -83,7 +83,7 @@ void Acceptor::readCallback() } else { - LOG_SYSERR << "Accpetor::readCallback"; + LOG_SYSERR << "Acceptor::readCallback"; // Read the section named "The special problem of // accept()ing when you can't" in libev's doc. // By Marc Lehmann, author of libev. diff --git a/trantor/net/inner/AresResolver.cc b/trantor/net/inner/AresResolver.cc index 9ef19fc4..d950a575 100644 --- a/trantor/net/inner/AresResolver.cc +++ b/trantor/net/inner/AresResolver.cc @@ -52,10 +52,17 @@ bool Resolver::isCAresUsed() AresResolver::LibraryInitializer::LibraryInitializer() { ares_library_init(ARES_LIB_INIT_ALL); + + hints_ = new ares_addrinfo_hints; + hints_->ai_flags = 0; + hints_->ai_family = AF_INET; + hints_->ai_socktype = 0; + hints_->ai_protocol = 0; } AresResolver::LibraryInitializer::~LibraryInitializer() { ares_library_cleanup(); + delete hints_; } AresResolver::LibraryInitializer AresResolver::libraryInitializer_; @@ -124,11 +131,12 @@ void AresResolver::resolveInLoop(const std::string& hostname, #endif init(); QueryData* queryData = new QueryData(this, cb, hostname); - ares_gethostbyname(ctx_, - hostname.c_str(), - AF_INET, - &AresResolver::ares_hostcallback_, - queryData); + ares_getaddrinfo(ctx_, + hostname.c_str(), + NULL, + libraryInitializer_.hints_, + &AresResolver::ares_hostcallback_, + queryData); struct timeval tv; struct timeval* tvp = ares_timeout(ctx_, NULL, &tv); double timeout = getSeconds(tvp); @@ -166,7 +174,7 @@ void AresResolver::onTimer() } void AresResolver::onQueryResult(int status, - struct hostent* result, + struct ares_addrinfo* result, const std::string& hostname, const ResolverResultsCallback& callback) { @@ -174,16 +182,27 @@ void AresResolver::onQueryResult(int status, auto inets_ptr = std::make_shared>(); if (result) { - auto pptr = (struct in_addr**)result->h_addr_list; - for (; *pptr != nullptr; pptr++) + auto pptr = (struct ares_addrinfo_node*)result->nodes; + for (; pptr != NULL; pptr = pptr->ai_next) { - struct sockaddr_in addr; - memset(&addr, 0, sizeof addr); - addr.sin_family = AF_INET; - addr.sin_port = 0; - addr.sin_addr = *reinterpret_cast(*pptr); - inets_ptr->emplace_back(trantor::InetAddress{addr}); + trantor::InetAddress inet; + if (pptr->ai_family == AF_INET) + { + struct sockaddr_in* addr4 = (struct sockaddr_in*)pptr->ai_addr; + inets_ptr->emplace_back(trantor::InetAddress{*addr4}); + } + else if (pptr->ai_family == AF_INET6) + { + struct sockaddr_in6* addr6 = + (struct sockaddr_in6*)pptr->ai_addr; + inets_ptr->emplace_back(trantor::InetAddress{*addr6}); + } + else + { + // TODO: Handle unknown family? + } } + ares_freeaddrinfo(result); } if (inets_ptr->empty()) { @@ -237,7 +256,7 @@ void AresResolver::onSockStateChange(int sockfd, bool read, bool write) void AresResolver::ares_hostcallback_(void* data, int status, int timeouts, - struct hostent* hostent) + struct ares_addrinfo* hostent) { (void)timeouts; QueryData* query = static_cast(data); diff --git a/trantor/net/inner/AresResolver.h b/trantor/net/inner/AresResolver.h index 753f0be1..52515771 100644 --- a/trantor/net/inner/AresResolver.h +++ b/trantor/net/inner/AresResolver.h @@ -15,8 +15,9 @@ extern "C" { - struct hostent; + struct ares_addrinfo; struct ares_channeldata; + struct ares_addrinfo_hints; using ares_channel = struct ares_channeldata*; } namespace trantor @@ -157,7 +158,7 @@ class AresResolver : public Resolver, void onRead(int sockfd); void onTimer(); void onQueryResult(int status, - struct hostent* result, + struct ares_addrinfo* result, const std::string& hostname, const ResolverResultsCallback& callback); void onSockCreate(int sockfd, int type); @@ -166,7 +167,7 @@ class AresResolver : public Resolver, static void ares_hostcallback_(void* data, int status, int timeouts, - struct hostent* hostent); + struct ares_addrinfo* hostent); #ifdef _WIN32 static int ares_sock_createcallback_(SOCKET sockfd, int type, void* data); #else @@ -184,6 +185,7 @@ class AresResolver : public Resolver, { LibraryInitializer(); ~LibraryInitializer(); + ares_addrinfo_hints* hints_; }; static LibraryInitializer libraryInitializer_; }; diff --git a/trantor/net/inner/AsyncStreamBufferNode.cc b/trantor/net/inner/AsyncStreamBufferNode.cc new file mode 100644 index 00000000..c30db1fa --- /dev/null +++ b/trantor/net/inner/AsyncStreamBufferNode.cc @@ -0,0 +1,65 @@ +#include + +namespace trantor +{ +class AsyncBufferNode : public BufferNode +{ + public: + AsyncBufferNode() = default; + ~AsyncBufferNode() override = default; + bool isAsync() const override + { + return true; + } + bool isStream() const override + { + return true; + } + long long remainingBytes() const override + { + if (msgBufferPtr_) + return static_cast(msgBufferPtr_->readableBytes()); + return 0; + } + bool available() const override + { + return !isDone_; + } + void getData(const char *&data, size_t &len) override + { + if (msgBufferPtr_) + { + data = msgBufferPtr_->peek(); + len = msgBufferPtr_->readableBytes(); + } + else + { + data = nullptr; + len = 0; + } + } + void retrieve(size_t len) override + { + assert(msgBufferPtr_); + if (msgBufferPtr_) + { + msgBufferPtr_->retrieve(len); + } + } + void append(const char *data, size_t len) override + { + if (!msgBufferPtr_) + { + msgBufferPtr_ = std::make_unique(len); + } + msgBufferPtr_->append(data, len); + } + + private: + std::unique_ptr msgBufferPtr_; +}; +BufferNodePtr BufferNode::newAsyncStreamBufferNode() +{ + return std::make_shared(); +} +} // namespace trantor diff --git a/trantor/net/inner/BufferNode.h b/trantor/net/inner/BufferNode.h new file mode 100644 index 00000000..bbce40e8 --- /dev/null +++ b/trantor/net/inner/BufferNode.h @@ -0,0 +1,86 @@ +/** + * + * @file BufferNode.h + * @author An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once +#ifdef _WIN32 +#include +#endif +#include +#include +#include +#include +#include +#include + +namespace trantor +{ +class BufferNode; +using BufferNodePtr = std::shared_ptr; +using StreamCallback = std::function; +class BufferNode : public NonCopyable +{ + public: + virtual bool isFile() const + { + return false; + } + virtual ~BufferNode() = default; + virtual bool isStream() const + { + return false; + } + virtual void getData(const char *&data, size_t &len) = 0; + virtual void append(const char *, size_t) + { + LOG_FATAL << "Not a memory buffer node"; + } + virtual void retrieve(size_t len) = 0; + virtual long long remainingBytes() const = 0; + virtual int getFd() const + { + LOG_FATAL << "Not a file buffer node"; + return -1; + } + virtual bool available() const + { + return true; + } + virtual bool isAsync() const + { + return false; + } + + void done() + { + isDone_ = true; + } + static BufferNodePtr newMemBufferNode(); + + static BufferNodePtr newStreamBufferNode(StreamCallback &&cb); +#ifdef _WIN32 + static BufferNodePtr newFileBufferNode(const wchar_t *fileName, + long long offset, + long long length); +#else + static BufferNodePtr newFileBufferNode(const char *fileName, + long long offset, + long long length); +#endif + static BufferNodePtr newAsyncStreamBufferNode(); + + protected: + bool isDone_{false}; +}; + +} // namespace trantor \ No newline at end of file diff --git a/trantor/net/inner/FileBufferNodeUnix.cc b/trantor/net/inner/FileBufferNodeUnix.cc new file mode 100644 index 00000000..9850f3f9 --- /dev/null +++ b/trantor/net/inner/FileBufferNodeUnix.cc @@ -0,0 +1,150 @@ +#include +#include +#include +#include +#include + +namespace trantor +{ +static const size_t kMaxSendFileBufferSize = 16 * 1024; +class FileBufferNode : public BufferNode +{ + public: + FileBufferNode(const char *fileName, long long offset, long long length) + { + assert(offset >= 0); + if (offset < 0) + { + LOG_ERROR << "offset must be greater than or equal to 0"; + isDone_ = true; + return; + } + sendFd_ = open(fileName, O_RDONLY); + + if (sendFd_ < 0) + { + LOG_SYSERR << fileName << " open error"; + isDone_ = true; + return; + } + struct stat filestat; + if (stat(fileName, &filestat) < 0) + { + LOG_SYSERR << fileName << " stat error"; + close(sendFd_); + sendFd_ = -1; + isDone_ = true; + return; + } + if (length == 0) + { + if (offset >= filestat.st_size) + { + LOG_ERROR << "The file size is " << filestat.st_size + << " bytes, but the offset is " << offset + << " bytes and the length is " << length << " bytes"; + close(sendFd_); + sendFd_ = -1; + isDone_ = true; + return; + } + fileBytesToSend_ = filestat.st_size - offset; + } + else + { + if (length > filestat.st_size - offset) + { + LOG_ERROR << "The file size is " << filestat.st_size + << " bytes, but the offset is " << offset + << " bytes and the length is " << length << " bytes"; + close(sendFd_); + sendFd_ = -1; + isDone_ = true; + return; + } + fileBytesToSend_ = length; + } + lseek(sendFd_, offset, SEEK_SET); + } + bool isFile() const override + { + return true; + } + int getFd() const override + { + return sendFd_; + } + void getData(const char *&data, size_t &len) override + { + if (msgBufferPtr_ == nullptr) + { + msgBufferPtr_ = std::make_unique( + (std::min)(kMaxSendFileBufferSize, + static_cast(fileBytesToSend_))); + } + if (msgBufferPtr_->readableBytes() == 0 && fileBytesToSend_ > 0 && + sendFd_ >= 0) + { + msgBufferPtr_->ensureWritableBytes( + (std::min)(kMaxSendFileBufferSize, + static_cast(fileBytesToSend_))); + auto n = read(sendFd_, + msgBufferPtr_->beginWrite(), + msgBufferPtr_->writableBytes()); + if (n > 0) + { + msgBufferPtr_->hasWritten(n); + } + else if (n == 0) + { + LOG_TRACE << "Read the end of file."; + } + else + { + LOG_SYSERR << "FileBufferNode::getData()"; + } + } + data = msgBufferPtr_->peek(); + len = msgBufferPtr_->readableBytes(); + } + void retrieve(size_t len) override + { + if (msgBufferPtr_) + { + msgBufferPtr_->retrieve(len); + } + fileBytesToSend_ -= static_cast(len); + if (fileBytesToSend_ < 0) + fileBytesToSend_ = 0; + } + long long remainingBytes() const override + { + if (isDone_) + return 0; + return fileBytesToSend_; + } + ~FileBufferNode() override + { + if (sendFd_ >= 0) + { + close(sendFd_); + } + } + bool available() const override + { + return sendFd_ >= 0; + } + + private: + int sendFd_{-1}; + long long fileBytesToSend_{0}; + std::unique_ptr msgBufferPtr_; +}; + +BufferNodePtr BufferNode::newFileBufferNode(const char *fileName, + long long offset, + long long length) +{ + return std::make_shared(fileName, offset, length); +} +} // namespace trantor \ No newline at end of file diff --git a/trantor/net/inner/FileBufferNodeWin.cc b/trantor/net/inner/FileBufferNodeWin.cc new file mode 100644 index 00000000..d99b707a --- /dev/null +++ b/trantor/net/inner/FileBufferNodeWin.cc @@ -0,0 +1,171 @@ +#include +#include +#include +#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +#define UWP 1 +#else +#define UWP 0 +#endif + +namespace trantor +{ +static const size_t kMaxSendFileBufferSize = 16 * 1024; +class FileBufferNode : public BufferNode +{ + public: + FileBufferNode(const wchar_t *fileName, long long offset, long long length) + { +#if UWP + sendHandle_ = CreateFile2( + fileName, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr); + +#else + sendHandle_ = CreateFileW(fileName, + GENERIC_READ, + FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); +#endif + if (sendHandle_ == INVALID_HANDLE_VALUE) + { + LOG_SYSERR << fileName << " open error"; + isDone_ = true; + return; + } + LARGE_INTEGER fileSize; + if (!GetFileSizeEx(sendHandle_, &fileSize)) + { + LOG_SYSERR << fileName << " stat error"; + CloseHandle(sendHandle_); + sendHandle_ = INVALID_HANDLE_VALUE; + isDone_ = true; + return; + } + + if (length == 0) + { + if (offset >= fileSize.QuadPart) + { + LOG_ERROR << "The file size is " << fileSize.QuadPart + << " bytes, but the offset is " << offset + << " bytes and the length is " << length << " bytes"; + CloseHandle(sendHandle_); + sendHandle_ = INVALID_HANDLE_VALUE; + isDone_ = true; + return; + } + fileBytesToSend_ = fileSize.QuadPart - offset; + } + else + { + if (length + offset > fileSize.QuadPart) + { + LOG_ERROR << "The file size is " << fileSize.QuadPart + << " bytes, but the offset is " << offset + << " bytes and the length is " << length << " bytes"; + CloseHandle(sendHandle_); + sendHandle_ = INVALID_HANDLE_VALUE; + isDone_ = true; + return; + } + + fileBytesToSend_ = length; + } + LARGE_INTEGER li; + li.QuadPart = offset; + if (!SetFilePointerEx(sendHandle_, li, nullptr, FILE_BEGIN)) + { + LOG_SYSERR << fileName << " seek error"; + CloseHandle(sendHandle_); + sendHandle_ = INVALID_HANDLE_VALUE; + isDone_ = true; + return; + } + msgBufferPtr_ = std::make_unique( + kMaxSendFileBufferSize < fileBytesToSend_ ? kMaxSendFileBufferSize + : fileBytesToSend_); + } + + bool isFile() const override + { + return true; + } + + void getData(const char *&data, size_t &len) override + { + if (msgBufferPtr_->readableBytes() == 0 && fileBytesToSend_ > 0 && + sendHandle_ != INVALID_HANDLE_VALUE) + { + msgBufferPtr_->ensureWritableBytes(kMaxSendFileBufferSize < + fileBytesToSend_ + ? kMaxSendFileBufferSize + : fileBytesToSend_); + DWORD n = 0; + if (!ReadFile(sendHandle_, + msgBufferPtr_->beginWrite(), + (uint32_t)msgBufferPtr_->writableBytes(), + &n, + nullptr)) + { + LOG_SYSERR << "FileBufferNode::getData()"; + } + if (n > 0) + { + msgBufferPtr_->hasWritten(n); + } + else if (n == 0) + { + LOG_TRACE << "Read the end of file."; + } + else + { + LOG_SYSERR << "FileBufferNode::getData()"; + } + } + data = msgBufferPtr_->peek(); + len = msgBufferPtr_->readableBytes(); + } + void retrieve(size_t len) override + { + msgBufferPtr_->retrieve(len); + fileBytesToSend_ -= static_cast(len); + if (fileBytesToSend_ < 0) + fileBytesToSend_ = 0; + } + long long remainingBytes() const override + { + if (isDone_) + return 0; + return fileBytesToSend_; + } + ~FileBufferNode() override + { + if (sendHandle_ != INVALID_HANDLE_VALUE) + { + CloseHandle(sendHandle_); + } + } + int getFd() const override + { + LOG_ERROR << "getFd() is not supported on Windows"; + return 0; + } + bool available() const override + { + return sendHandle_ != INVALID_HANDLE_VALUE; + } + + private: + HANDLE sendHandle_{INVALID_HANDLE_VALUE}; + long long fileBytesToSend_{0}; + std::unique_ptr msgBufferPtr_; +}; +BufferNodePtr BufferNode::newFileBufferNode(const wchar_t *fileName, + long long offset, + long long length) +{ + return std::make_shared(fileName, offset, length); +} +} // namespace trantor \ No newline at end of file diff --git a/trantor/net/inner/MemBufferNode.cc b/trantor/net/inner/MemBufferNode.cc new file mode 100644 index 00000000..c6cd2479 --- /dev/null +++ b/trantor/net/inner/MemBufferNode.cc @@ -0,0 +1,36 @@ +#include +namespace trantor +{ +class MemBufferNode : public BufferNode +{ + public: + MemBufferNode() = default; + + void getData(const char *&data, size_t &len) override + { + data = buffer_.peek(); + len = buffer_.readableBytes(); + } + void retrieve(size_t len) override + { + buffer_.retrieve(len); + } + long long remainingBytes() const override + { + if (isDone_) + return 0; + return static_cast(buffer_.readableBytes()); + } + void append(const char *data, size_t len) override + { + buffer_.append(data, len); + } + + private: + trantor::MsgBuffer buffer_; +}; +BufferNodePtr BufferNode::newMemBufferNode() +{ + return std::make_shared(); +} +} // namespace trantor diff --git a/trantor/net/inner/StreamBufferNode.cc b/trantor/net/inner/StreamBufferNode.cc new file mode 100644 index 00000000..0376ac9c --- /dev/null +++ b/trantor/net/inner/StreamBufferNode.cc @@ -0,0 +1,67 @@ +#include +namespace trantor +{ +static const size_t kMaxSendFileBufferSize = 16 * 1024; +class StreamBufferNode : public BufferNode +{ + public: + StreamBufferNode(std::function &&callback) + : streamCallback_(std::move(callback)) + { + } + bool isStream() const override + { + return true; + } + void getData(const char *&data, size_t &len) override + { + if (msgBuffer_.readableBytes() == 0) + { + msgBuffer_.ensureWritableBytes(kMaxSendFileBufferSize); + auto n = streamCallback_(msgBuffer_.beginWrite(), + msgBuffer_.writableBytes()); + if (n > 0) + { + msgBuffer_.hasWritten(n); + } + else + { + isDone_ = true; + } + } + data = msgBuffer_.peek(); + len = msgBuffer_.readableBytes(); + } + void retrieve(size_t len) override + { + msgBuffer_.retrieve(len); +#ifndef NDEBUG + dataWritten_ += len; + LOG_TRACE << "send stream in loop: bytes written: " << dataWritten_ + << " / total bytes written: " << dataWritten_; +#endif + } + long long remainingBytes() const override + { + if (isDone_) + return 0; + return 1; + } + ~StreamBufferNode() override + { + if (streamCallback_) + streamCallback_(nullptr, 0); // cleanup callback internals + } + + private: + std::function streamCallback_; +#ifndef NDEBUG // defined by CMake for release build + std::size_t dataWritten_{0}; +#endif + MsgBuffer msgBuffer_; +}; +BufferNodePtr BufferNode::newStreamBufferNode(StreamCallback &&callback) +{ + return std::make_shared(std::move(callback)); +} +} // namespace trantor \ No newline at end of file diff --git a/trantor/net/inner/TLSProvider.h b/trantor/net/inner/TLSProvider.h index eb29f8a7..7d6899ad 100644 --- a/trantor/net/inner/TLSProvider.h +++ b/trantor/net/inner/TLSProvider.h @@ -36,6 +36,8 @@ struct TLSProvider /** * @brief Encrypt and send data via TLS + * @return the number of bytes sent, or -1 on error, or 0 if EAGAIN or + * EWOULDBLOCK. */ virtual ssize_t sendData(const char* ptr, size_t size) = 0; diff --git a/trantor/net/inner/TcpConnectionImpl.cc b/trantor/net/inner/TcpConnectionImpl.cc index 0acfc711..b786c762 100644 --- a/trantor/net/inner/TcpConnectionImpl.cc +++ b/trantor/net/inner/TcpConnectionImpl.cc @@ -23,13 +23,7 @@ #include #ifndef _WIN32 #include -#else -#include -#include -#include #endif -#include -#include using namespace trantor; @@ -43,8 +37,32 @@ using namespace trantor; #undef ECONNRESET #define ECONNRESET WSAECONNRESET #endif +static inline bool isEAGAIN() +{ + if (errno == EWOULDBLOCK || errno == EAGAIN || errno == 0) + { + LOG_TRACE << "write buffer is full"; + return true; + } + else if (errno == EPIPE || errno == ECONNRESET) + { +#ifdef _WIN32 + LOG_TRACE << "WSAENOTCONN or WSAECONNRESET, errno=" << errno; +#else + LOG_TRACE << "EPIPE or ECONNRESET, errno=" << errno; +#endif + LOG_TRACE << "send node in loop: return on connection closed"; + return false; + } + else + { + // TODO: any others? + LOG_SYSERR << "send node in loop: return on unexpected error(" << errno + << ")"; + return false; + } +} -static const int kMaxSendFileBufferSize = 16 * 1024; TcpConnectionImpl::TcpConnectionImpl(EventLoop *loop, int socketfd, const InetAddress &localAddr, @@ -59,20 +77,17 @@ TcpConnectionImpl::TcpConnectionImpl(EventLoop *loop, { LOG_TRACE << "new connection:" << peerAddr.toIpPort() << "->" << localAddr.toIpPort(); - ioChannelPtr_->setReadCallback( - std::bind(&TcpConnectionImpl::readCallback, this)); - ioChannelPtr_->setWriteCallback( - std::bind(&TcpConnectionImpl::writeCallback, this)); - ioChannelPtr_->setCloseCallback( - std::bind(&TcpConnectionImpl::handleClose, this)); - ioChannelPtr_->setErrorCallback( - std::bind(&TcpConnectionImpl::handleError, this)); + ioChannelPtr_->setReadCallback([this]() { readCallback(); }); + ioChannelPtr_->setWriteCallback([this]() { writeCallback(); }); + ioChannelPtr_->setCloseCallback([this]() { handleClose(); }); + ioChannelPtr_->setErrorCallback([this]() { handleError(); }); socketPtr_->setKeepAlive(true); name_ = localAddr.toIpPort() + "--" + peerAddr.toIpPort(); if (policy != nullptr) { - tlsProviderPtr_ = newTLSProvider(this, policy, ctx); + tlsProviderPtr_ = + newTLSProvider(this, std::move(policy), std::move(ctx)); tlsProviderPtr_->setWriteCallback(onSslWrite); tlsProviderPtr_->setErrorCallback(onSslError); tlsProviderPtr_->setHandshakeCallback(onHandshakeFinished); @@ -83,6 +98,28 @@ TcpConnectionImpl::TcpConnectionImpl(EventLoop *loop, } TcpConnectionImpl::~TcpConnectionImpl() { + std::size_t readableTlsBytes = 0; + if (tlsProviderPtr_) + { + readableTlsBytes = tlsProviderPtr_->getBufferedData().readableBytes(); + } + if (!writeBufferList_.empty()) + { + LOG_DEBUG << "write node list size: " << writeBufferList_.size() + << " first node is file? " + << writeBufferList_.front()->isFile() + << " first node is stream? " + << writeBufferList_.front()->isStream() + << " first node is async? " + << writeBufferList_.front()->isAsync() << " first node size: " + << writeBufferList_.front()->remainingBytes() + << " buffered TLS data size: " << readableTlsBytes; + } + else if (readableTlsBytes != 0) + { + LOG_DEBUG << "write node list size: 0 buffered TLS data size: " + << readableTlsBytes; + } // send a close alert to peer if we are still connected if (tlsProviderPtr_ && status_ == ConnStatus::Connected) tlsProviderPtr_->close(); @@ -167,7 +204,6 @@ void TcpConnectionImpl::extendLife() void TcpConnectionImpl::writeCallback() { loop_->assertInLoopThread(); - extendLife(); if (ioChannelPtr_->isWriting()) { if (tlsProviderPtr_) @@ -175,153 +211,44 @@ void TcpConnectionImpl::writeCallback() bool sentAll = tlsProviderPtr_->sendBufferedData(); if (!sentAll) { - ioChannelPtr_->enableWriting(); return; } } - assert(!writeBufferList_.empty()); - auto writeBuffer_ = writeBufferList_.front(); - if (!writeBuffer_->isFile()) + while (!writeBufferList_.empty()) { - // not a file - if (writeBuffer_->msgBuffer_->readableBytes() <= 0) + auto &nodePtr = writeBufferList_.front(); + if (nodePtr->remainingBytes() == 0) { - // finished sending - writeBufferList_.pop_front(); - if (writeBufferList_.empty()) + if (!nodePtr->isAsync() || !nodePtr->available()) { - // stop writing - ioChannelPtr_->disableWriting(); - if (writeCompleteCallback_) - writeCompleteCallback_(shared_from_this()); - if (status_ == ConnStatus::Disconnecting) - { - socketPtr_->closeWrite(); - } + // finished sending + writeBufferList_.pop_front(); } else { - // send next - // what if the next is not a file??? - auto fileNode = writeBufferList_.front(); - assert(fileNode->isFile()); - sendFileInLoop(fileNode); + // the first node is an async node and is available + ioChannelPtr_->disableWriting(); + return; } } else { // continue sending - auto n = writeInLoop(writeBuffer_->msgBuffer_->peek(), - writeBuffer_->msgBuffer_->readableBytes()); - if (n >= 0) - { - writeBuffer_->msgBuffer_->retrieve(n); - } - else - { -#ifdef _WIN32 - if (errno != 0 && errno != EWOULDBLOCK) -#else - if (errno != EWOULDBLOCK) -#endif - { - // TODO: any others? - if (errno == EPIPE || errno == ECONNRESET) - { -#ifdef _WIN32 - LOG_TRACE << "WSAENOTCONN or WSAECONNRESET, errno=" - << errno; -#else - LOG_TRACE << "EPIPE or ECONNRESET, errno=" << errno; -#endif - return; - } - LOG_SYSERR << "Unexpected error(" << errno << ")"; - return; - } - } + auto n = sendNodeInLoop(nodePtr); + if (nodePtr->remainingBytes() > 0 || n < 0) + return; } } - else + assert(writeBufferList_.empty()); + if (tlsProviderPtr_ == nullptr || + tlsProviderPtr_->getBufferedData().readableBytes() == 0) { - // is a file - if (writeBuffer_->fileBytesToSend_ <= 0) + ioChannelPtr_->disableWriting(); + if (closeOnEmpty_) { - // finished sending - writeBufferList_.pop_front(); - if (writeBufferList_.empty()) - { - // stop writing - ioChannelPtr_->disableWriting(); - if (writeCompleteCallback_) - writeCompleteCallback_(shared_from_this()); - if (status_ == ConnStatus::Disconnecting) - { - socketPtr_->closeWrite(); - } - } - else - { - // next is not a file - if (!writeBufferList_.front()->isFile()) - { - // There is data to be sent in the buffer. - auto n = writeInLoop( - writeBufferList_.front()->msgBuffer_->peek(), - writeBufferList_.front() - ->msgBuffer_->readableBytes()); - if (n >= 0) - { - writeBufferList_.front()->msgBuffer_->retrieve(n); - } - else - { -#ifdef _WIN32 - if (errno != 0 && errno != EWOULDBLOCK) -#else - if (errno != EWOULDBLOCK) -#endif - { - // TODO: any others? - if (errno == EPIPE || errno == ECONNRESET) - { -#ifdef _WIN32 - LOG_TRACE << "WSAENOTCONN or " - "WSAECONNRESET, errno=" - << errno; -#else - LOG_TRACE << "EPIPE or " - "ECONNRESET, erron=" - << errno; -#endif - return; - } - LOG_SYSERR << "Unexpected error(" << errno - << ")"; - return; - } - } - } - else - { - // next is a file - sendFileInLoop(writeBufferList_.front()); - } - } - } - else - { - sendFileInLoop(writeBuffer_); + shutdown(); } } - - if (closeOnEmpty_ && - (writeBufferList_.empty() || - (tlsProviderPtr_ == nullptr || - tlsProviderPtr_->getBufferedData().readableBytes() == 0))) - { - shutdown(); - } } else { @@ -347,6 +274,12 @@ void TcpConnectionImpl::connectEstablished() void TcpConnectionImpl::handleClose() { LOG_TRACE << "connection closed, fd=" << socketPtr_->fd(); + LOG_TRACE << "write buffer size: " << writeBufferList_.size(); + if (!writeBufferList_.empty()) + { + LOG_TRACE << writeBufferList_.front()->isFile(); + LOG_TRACE << writeBufferList_.front()->isStream(); + } loop_->assertInLoopThread(); status_ = ConnStatus::Disconnected; ioChannelPtr_->disableAll(); @@ -408,7 +341,7 @@ void TcpConnectionImpl::shutdown() // connection just yet if (thisPtr->tlsProviderPtr_->getBufferedData() .readableBytes() != 0 || - thisPtr->writeBufferList_.size() != 0) + !thisPtr->writeBufferList_.empty()) { thisPtr->closeOnEmpty_ = true; return; @@ -416,7 +349,7 @@ void TcpConnectionImpl::shutdown() thisPtr->tlsProviderPtr_->close(); } if (thisPtr->tlsProviderPtr_ == nullptr && - thisPtr->writeBufferList_.size() != 0) + !thisPtr->writeBufferList_.empty()) { thisPtr->closeOnEmpty_ = true; return; @@ -454,11 +387,9 @@ void TcpConnectionImpl::sendInLoop(const char *buffer, size_t length) loop_->assertInLoopThread(); if (status_ != ConnStatus::Connected) { - LOG_WARN << "Connection is not connected,give up sending"; + LOG_DEBUG << "Connection is not connected,give up sending"; return; } - extendLife(); - size_t remainLen = length; ssize_t sendLen = 0; if (!ioChannelPtr_->isWriting() && writeBufferList_.empty()) { @@ -466,55 +397,27 @@ void TcpConnectionImpl::sendInLoop(const char *buffer, size_t length) sendLen = writeInLoop(buffer, length); if (sendLen < 0) { - // error -#ifdef _WIN32 - if (errno != 0 && errno != EWOULDBLOCK) -#else - if (errno != EWOULDBLOCK) -#endif - { - if (errno == EPIPE || errno == ECONNRESET) // TODO: any others? - { -#ifdef _WIN32 - LOG_TRACE << "WSAENOTCONN or WSAECONNRESET, errno=" - << errno; -#else - LOG_TRACE << "EPIPE or ECONNRESET, errno=" << errno; -#endif - return; - } - LOG_SYSERR << "Unexpected error(" << errno << ")"; - return; - } - sendLen = 0; + LOG_TRACE << "write error"; + return; } - remainLen -= sendLen; + length -= sendLen; } - if (remainLen > 0 && status_ == ConnStatus::Connected) + if (length > 0 && status_ == ConnStatus::Connected) { - if (writeBufferList_.empty()) - { - BufferNodePtr node = std::make_shared(); - node->msgBuffer_ = std::make_shared(); - writeBufferList_.push_back(std::move(node)); - } - else if (writeBufferList_.back()->isFile()) + if (writeBufferList_.empty() || writeBufferList_.back()->isFile() || + writeBufferList_.back()->isStream()) { - BufferNodePtr node = std::make_shared(); - node->msgBuffer_ = std::make_shared(); - writeBufferList_.push_back(std::move(node)); + writeBufferList_.push_back(BufferNode::newMemBufferNode()); } - writeBufferList_.back()->msgBuffer_->append( - static_cast(buffer) + sendLen, remainLen); - if (!ioChannelPtr_->isWriting()) - ioChannelPtr_->enableWriting(); + writeBufferList_.back()->append(static_cast(buffer) + + sendLen, + length); if (highWaterMarkCallback_ && - writeBufferList_.back()->msgBuffer_->readableBytes() > - highWaterMarkLen_) + writeBufferList_.back()->remainingBytes() > + static_cast(highWaterMarkLen_)) { - highWaterMarkCallback_( - shared_from_this(), - writeBufferList_.back()->msgBuffer_->readableBytes()); + highWaterMarkCallback_(shared_from_this(), + writeBufferList_.back()->remainingBytes()); } if (highWaterMarkCallback_ && tlsProviderPtr_ && tlsProviderPtr_->getBufferedData().readableBytes() > @@ -531,31 +434,12 @@ void TcpConnectionImpl::send(const std::shared_ptr &msgPtr) { if (loop_->isInLoopThread()) { - std::lock_guard guard(sendNumMutex_); - if (sendNum_ == 0) - { - sendInLoop(msgPtr->data(), msgPtr->length()); - } - else - { - ++sendNum_; - auto thisPtr = shared_from_this(); - loop_->queueInLoop([thisPtr, msgPtr]() { - thisPtr->sendInLoop(msgPtr->data(), msgPtr->length()); - std::lock_guard guard1(thisPtr->sendNumMutex_); - --thisPtr->sendNum_; - }); - } + sendInLoop(msgPtr->data(), msgPtr->length()); } else { - auto thisPtr = shared_from_this(); - std::lock_guard guard(sendNumMutex_); - ++sendNum_; - loop_->queueInLoop([thisPtr, msgPtr]() { + loop_->queueInLoop([thisPtr = shared_from_this(), msgPtr]() { thisPtr->sendInLoop(msgPtr->data(), msgPtr->length()); - std::lock_guard guard1(thisPtr->sendNumMutex_); - --thisPtr->sendNum_; }); } } @@ -564,31 +448,12 @@ void TcpConnectionImpl::send(const std::shared_ptr &msgPtr) { if (loop_->isInLoopThread()) { - std::lock_guard guard(sendNumMutex_); - if (sendNum_ == 0) - { - sendInLoop(msgPtr->peek(), msgPtr->readableBytes()); - } - else - { - ++sendNum_; - auto thisPtr = shared_from_this(); - loop_->queueInLoop([thisPtr, msgPtr]() { - thisPtr->sendInLoop(msgPtr->peek(), msgPtr->readableBytes()); - std::lock_guard guard1(thisPtr->sendNumMutex_); - --thisPtr->sendNum_; - }); - } + sendInLoop(msgPtr->peek(), msgPtr->readableBytes()); } else { - auto thisPtr = shared_from_this(); - std::lock_guard guard(sendNumMutex_); - ++sendNum_; - loop_->queueInLoop([thisPtr, msgPtr]() { + loop_->queueInLoop([thisPtr = shared_from_this(), msgPtr]() { thisPtr->sendInLoop(msgPtr->peek(), msgPtr->readableBytes()); - std::lock_guard guard1(thisPtr->sendNumMutex_); - --thisPtr->sendNum_; }); } } @@ -596,106 +461,47 @@ void TcpConnectionImpl::send(const char *msg, size_t len) { if (loop_->isInLoopThread()) { - std::lock_guard guard(sendNumMutex_); - if (sendNum_ == 0) - { - sendInLoop(msg, len); - } - else - { - ++sendNum_; - auto buffer = std::make_shared(msg, len); - auto thisPtr = shared_from_this(); - loop_->queueInLoop([thisPtr, buffer]() { - thisPtr->sendInLoop(buffer->data(), buffer->length()); - std::lock_guard guard1(thisPtr->sendNumMutex_); - --thisPtr->sendNum_; - }); - } + sendInLoop(msg, len); } else { auto buffer = std::make_shared(msg, len); - auto thisPtr = shared_from_this(); - std::lock_guard guard(sendNumMutex_); - ++sendNum_; - loop_->queueInLoop([thisPtr, buffer]() { - thisPtr->sendInLoop(buffer->data(), buffer->length()); - std::lock_guard guard1(thisPtr->sendNumMutex_); - --thisPtr->sendNum_; - }); + loop_->queueInLoop( + [thisPtr = shared_from_this(), buffer = std::move(buffer)]() { + thisPtr->sendInLoop(buffer->data(), buffer->length()); + }); } } void TcpConnectionImpl::send(const void *msg, size_t len) { if (loop_->isInLoopThread()) { - std::lock_guard guard(sendNumMutex_); - if (sendNum_ == 0) - { #ifndef _WIN32 - sendInLoop(msg, len); + sendInLoop(msg, len); #else - sendInLoop(static_cast(msg), len); + sendInLoop(static_cast(msg), len); #endif - } - else - { - ++sendNum_; - auto buffer = - std::make_shared(static_cast(msg), - len); - auto thisPtr = shared_from_this(); - loop_->queueInLoop([thisPtr, buffer]() { - thisPtr->sendInLoop(buffer->data(), buffer->length()); - std::lock_guard guard1(thisPtr->sendNumMutex_); - --thisPtr->sendNum_; - }); - } } else { auto buffer = std::make_shared(static_cast(msg), len); - auto thisPtr = shared_from_this(); - std::lock_guard guard(sendNumMutex_); - ++sendNum_; - loop_->queueInLoop([thisPtr, buffer]() { - thisPtr->sendInLoop(buffer->data(), buffer->length()); - std::lock_guard guard1(thisPtr->sendNumMutex_); - --thisPtr->sendNum_; - }); + loop_->queueInLoop( + [thisPtr = shared_from_this(), buffer = std::move(buffer)]() { + thisPtr->sendInLoop(buffer->data(), buffer->length()); + }); } } void TcpConnectionImpl::send(const std::string &msg) { if (loop_->isInLoopThread()) { - std::lock_guard guard(sendNumMutex_); - if (sendNum_ == 0) - { - sendInLoop(msg.data(), msg.length()); - } - else - { - ++sendNum_; - auto thisPtr = shared_from_this(); - loop_->queueInLoop([thisPtr, msg]() { - thisPtr->sendInLoop(msg.data(), msg.length()); - std::lock_guard guard1(thisPtr->sendNumMutex_); - --thisPtr->sendNum_; - }); - } + sendInLoop(msg.data(), msg.length()); } else { - auto thisPtr = shared_from_this(); - std::lock_guard guard(sendNumMutex_); - ++sendNum_; - loop_->queueInLoop([thisPtr, msg]() { + loop_->queueInLoop([thisPtr = shared_from_this(), msg]() { thisPtr->sendInLoop(msg.data(), msg.length()); - std::lock_guard guard1(thisPtr->sendNumMutex_); - --thisPtr->sendNum_; }); } } @@ -703,32 +509,14 @@ void TcpConnectionImpl::send(std::string &&msg) { if (loop_->isInLoopThread()) { - std::lock_guard guard(sendNumMutex_); - if (sendNum_ == 0) - { - sendInLoop(msg.data(), msg.length()); - } - else - { - auto thisPtr = shared_from_this(); - ++sendNum_; - loop_->queueInLoop([thisPtr, msg = std::move(msg)]() { - thisPtr->sendInLoop(msg.data(), msg.length()); - std::lock_guard guard1(thisPtr->sendNumMutex_); - --thisPtr->sendNum_; - }); - } + sendInLoop(msg.data(), msg.length()); } else { - auto thisPtr = shared_from_this(); - std::lock_guard guard(sendNumMutex_); - ++sendNum_; - loop_->queueInLoop([thisPtr, msg = std::move(msg)]() { - thisPtr->sendInLoop(msg.data(), msg.length()); - std::lock_guard guard1(thisPtr->sendNumMutex_); - --thisPtr->sendNum_; - }); + loop_->queueInLoop( + [thisPtr = shared_from_this(), msg = std::move(msg)]() { + thisPtr->sendInLoop(msg.data(), msg.length()); + }); } } @@ -736,31 +524,12 @@ void TcpConnectionImpl::send(const MsgBuffer &buffer) { if (loop_->isInLoopThread()) { - std::lock_guard guard(sendNumMutex_); - if (sendNum_ == 0) - { - sendInLoop(buffer.peek(), buffer.readableBytes()); - } - else - { - ++sendNum_; - auto thisPtr = shared_from_this(); - loop_->queueInLoop([thisPtr, buffer]() { - thisPtr->sendInLoop(buffer.peek(), buffer.readableBytes()); - std::lock_guard guard1(thisPtr->sendNumMutex_); - --thisPtr->sendNum_; - }); - } + sendInLoop(buffer.peek(), buffer.readableBytes()); } else { - auto thisPtr = shared_from_this(); - std::lock_guard guard(sendNumMutex_); - ++sendNum_; - loop_->queueInLoop([thisPtr, buffer]() { + loop_->queueInLoop([thisPtr = shared_from_this(), buffer]() { thisPtr->sendInLoop(buffer.peek(), buffer.readableBytes()); - std::lock_guard guard1(thisPtr->sendNumMutex_); - --thisPtr->sendNum_; }); } } @@ -769,168 +538,84 @@ void TcpConnectionImpl::send(MsgBuffer &&buffer) { if (loop_->isInLoopThread()) { - std::lock_guard guard(sendNumMutex_); - if (sendNum_ == 0) - { - sendInLoop(buffer.peek(), buffer.readableBytes()); - } - else - { - ++sendNum_; - auto thisPtr = shared_from_this(); - loop_->queueInLoop([thisPtr, buffer = std::move(buffer)]() { - thisPtr->sendInLoop(buffer.peek(), buffer.readableBytes()); - std::lock_guard guard1(thisPtr->sendNumMutex_); - --thisPtr->sendNum_; - }); - } + sendInLoop(buffer.peek(), buffer.readableBytes()); } else { - auto thisPtr = shared_from_this(); - std::lock_guard guard(sendNumMutex_); - ++sendNum_; - loop_->queueInLoop([thisPtr, buffer = std::move(buffer)]() { - thisPtr->sendInLoop(buffer.peek(), buffer.readableBytes()); - std::lock_guard guard1(thisPtr->sendNumMutex_); - --thisPtr->sendNum_; - }); + loop_->queueInLoop( + [thisPtr = shared_from_this(), buffer = std::move(buffer)]() { + thisPtr->sendInLoop(buffer.peek(), buffer.readableBytes()); + }); } } void TcpConnectionImpl::sendFile(const char *fileName, - size_t offset, - size_t length) + long long offset, + long long length) { assert(fileName); #ifdef _WIN32 sendFile(utils::toNativePath(fileName).c_str(), offset, length); #else // _WIN32 - int fd = open(fileName, O_RDONLY); + auto fileNode = BufferNode::newFileBufferNode(fileName, offset, length); - if (fd < 0) + if (!fileNode->available()) { LOG_SYSERR << fileName << " open error"; return; } - if (length == 0) - { - struct stat filestat; - if (stat(fileName, &filestat) < 0) - { - LOG_SYSERR << fileName << " stat error"; - close(fd); - return; - } - length = filestat.st_size; - } - - sendFile(fd, offset, length); + sendFile(std::move(fileNode)); #endif // _WIN32 } void TcpConnectionImpl::sendFile(const wchar_t *fileName, - size_t offset, - size_t length) + long long offset, + long long length) { assert(fileName); #ifndef _WIN32 sendFile(utils::toNativePath(fileName).c_str(), offset, length); -#else // _WIN32 - FILE *fp; -#ifndef _MSC_VER - fp = _wfopen(fileName, L"rb"); -#else // _MSC_VER - if (_wfopen_s(&fp, fileName, L"rb") != 0) - fp = nullptr; -#endif // _MSC_VER - if (fp == nullptr) +#else + auto fileNode = BufferNode::newFileBufferNode(fileName, offset, length); + if (!fileNode->available()) { LOG_SYSERR << fileName << " open error"; return; } - - if (length == 0) - { - struct _stati64 filestat; - if (_wstati64(fileName, &filestat) < 0) - { - LOG_SYSERR << fileName << " stat error"; - fclose(fp); - return; - } - length = filestat.st_size; - } - - sendFile(fp, offset, length); + sendFile(std::move(fileNode)); #endif // _WIN32 } -#ifndef _WIN32 -void TcpConnectionImpl::sendFile(int sfd, size_t offset, size_t length) -#else -void TcpConnectionImpl::sendFile(FILE *fp, size_t offset, size_t length) -#endif +void TcpConnectionImpl::sendFile(BufferNodePtr &&fileNode) { - assert(length > 0); -#ifndef _WIN32 - assert(sfd >= 0); - BufferNodePtr node = std::make_shared(); - node->sendFd_ = sfd; -#else - assert(fp); - BufferNodePtr node = std::make_shared(); - node->sendFp_ = fp; -#endif - node->offset_ = offset; - node->fileBytesToSend_ = length; + assert(fileNode->isFile() && fileNode->remainingBytes() > 0); if (loop_->isInLoopThread()) { - std::lock_guard guard(sendNumMutex_); - if (sendNum_ == 0) + if (writeBufferList_.empty()) { - writeBufferList_.push_back(node); - if (writeBufferList_.size() == 1) - { - sendFileInLoop(writeBufferList_.front()); - return; - } + auto n = sendNodeInLoop(fileNode); + if (fileNode->remainingBytes() > 0 && n >= 0) + writeBufferList_.push_back(std::move(fileNode)); + return; } else { - ++sendNum_; - auto thisPtr = shared_from_this(); - loop_->queueInLoop([thisPtr, node]() { - thisPtr->writeBufferList_.push_back(node); - { - std::lock_guard guard1(thisPtr->sendNumMutex_); - --thisPtr->sendNum_; - } - - if (thisPtr->writeBufferList_.size() == 1) - { - thisPtr->sendFileInLoop(thisPtr->writeBufferList_.front()); - } - }); + writeBufferList_.push_back(std::move(fileNode)); } } else { - auto thisPtr = shared_from_this(); - std::lock_guard guard(sendNumMutex_); - ++sendNum_; - loop_->queueInLoop([thisPtr, node]() { - LOG_TRACE << "Push sendfile to list"; - thisPtr->writeBufferList_.push_back(node); - + loop_->queueInLoop([thisPtr = shared_from_this(), + node = std::move(fileNode)]() mutable { + if (thisPtr->writeBufferList_.empty()) { - std::lock_guard guard1(thisPtr->sendNumMutex_); - --thisPtr->sendNum_; + auto n = thisPtr->sendNodeInLoop(node); + if (node->remainingBytes() > 0 && n >= 0) + thisPtr->writeBufferList_.push_back(std::move(node)); } - - if (thisPtr->writeBufferList_.size() == 1) + else { - thisPtr->sendFileInLoop(thisPtr->writeBufferList_.front()); + thisPtr->writeBufferList_.push_back(std::move(node)); } }); } @@ -939,303 +624,110 @@ void TcpConnectionImpl::sendFile(FILE *fp, size_t offset, size_t length) void TcpConnectionImpl::sendStream( std::function callback) { - BufferNodePtr node = std::make_shared(); - node->offset_ = - 0; // not used, the offset should be handled by the callback - node->fileBytesToSend_ = 1; // force to > 0 until stream sent - node->streamCallback_ = std::move(callback); + auto node = BufferNode::newStreamBufferNode(std::move(callback)); if (loop_->isInLoopThread()) { - std::lock_guard guard(sendNumMutex_); - if (sendNum_ == 0) + if (writeBufferList_.empty()) { - writeBufferList_.push_back(node); - if (writeBufferList_.size() == 1) - { - sendFileInLoop(writeBufferList_.front()); - return; - } + auto n = sendNodeInLoop(node); + if (node->remainingBytes() > 0 && n >= 0) + writeBufferList_.push_back(std::move(node)); + return; } else { - ++sendNum_; - auto thisPtr = shared_from_this(); - loop_->queueInLoop([thisPtr, node]() { - thisPtr->writeBufferList_.push_back(node); + writeBufferList_.push_back(std::move(node)); + } + } + else + { + loop_->queueInLoop( + [thisPtr = shared_from_this(), node = std::move(node)]() mutable { + LOG_TRACE << "Push send stream to list"; + if (thisPtr->writeBufferList_.empty()) { - std::lock_guard guard1(thisPtr->sendNumMutex_); - --thisPtr->sendNum_; + auto n = thisPtr->sendNodeInLoop(node); + if (node->remainingBytes() > 0 && n >= 0) + thisPtr->writeBufferList_.push_back(std::move(node)); } - - if (thisPtr->writeBufferList_.size() == 1) + else { - thisPtr->sendFileInLoop(thisPtr->writeBufferList_.front()); + thisPtr->writeBufferList_.push_back(std::move(node)); } }); - } - } - else - { - auto thisPtr = shared_from_this(); - std::lock_guard guard(sendNumMutex_); - ++sendNum_; - loop_->queueInLoop([thisPtr, node]() { - LOG_TRACE << "Push sendstream to list"; - thisPtr->writeBufferList_.push_back(node); - - { - std::lock_guard guard1(thisPtr->sendNumMutex_); - --thisPtr->sendNum_; - } - - if (thisPtr->writeBufferList_.size() == 1) - { - thisPtr->sendFileInLoop(thisPtr->writeBufferList_.front()); - } - }); } } -void TcpConnectionImpl::sendFileInLoop(const BufferNodePtr &filePtr) +ssize_t TcpConnectionImpl::sendNodeInLoop(const BufferNodePtr &nodePtr) { loop_->assertInLoopThread(); - assert(filePtr->isFile()); #ifdef __linux__ - if (!filePtr->streamCallback_ && !tlsProviderPtr_) + if (nodePtr->isFile() && !tlsProviderPtr_) { + static const long long kMaxSendBytes = 0x7ffff000; LOG_TRACE << "send file in loop using linux kernel sendfile()"; - auto bytesSent = sendfile(socketPtr_->fd(), - filePtr->sendFd_, - &filePtr->offset_, - filePtr->fileBytesToSend_); - if (bytesSent < 0) - { - if (errno != EAGAIN) - { - LOG_SYSERR << "TcpConnectionImpl::sendFileInLoop"; - if (ioChannelPtr_->isWriting()) - ioChannelPtr_->disableWriting(); - } - return; - } - if (bytesSent < filePtr->fileBytesToSend_) - { - if (bytesSent == 0) - { - LOG_SYSERR << "TcpConnectionImpl::sendFileInLoop"; - return; - } - } - LOG_TRACE << "sendfile() " << bytesSent << " bytes sent"; - filePtr->fileBytesToSend_ -= bytesSent; - if (!ioChannelPtr_->isWriting()) - { - ioChannelPtr_->enableWriting(); + auto toSend = nodePtr->remainingBytes(); + if (toSend <= 0) + { + LOG_ERROR << "0 or negative bytes to send"; + return -1; + } + auto bytesSent = + sendfile(socketPtr_->fd(), + nodePtr->getFd(), + nullptr, + static_cast( + toSend < kMaxSendBytes ? toSend : kMaxSendBytes)); + if (bytesSent > 0) + { + nodePtr->retrieve(bytesSent); + bytesSent_ += bytesSent; + } + else if (!isEAGAIN()) + return -1; + extendLife(); + if (bytesSent < toSend) + { + LOG_TRACE << "bytesSent = " << bytesSent << " toSend = " << toSend; + if (!ioChannelPtr_->isWriting()) + ioChannelPtr_->enableWriting(); } - return; + return bytesSent; } #endif // Send stream - if (filePtr->streamCallback_) + + LOG_TRACE << "send node in loop"; + const char *data; + size_t len; + ssize_t hasSent = 0; + while ((nodePtr->remainingBytes() > 0)) { - LOG_TRACE << "send stream in loop"; - if (!fileBufferPtr_) - { - fileBufferPtr_ = std::make_unique>(); - fileBufferPtr_->reserve(kMaxSendFileBufferSize); - } - while ((filePtr->fileBytesToSend_ > 0) || !fileBufferPtr_->empty()) + // get next chunk + nodePtr->getData(data, len); + if (len == 0) { - // get next chunk - if (fileBufferPtr_->empty()) - { - // LOG_TRACE << "send stream in loop: fetch data - // on buffer empty"; - fileBufferPtr_->resize(kMaxSendFileBufferSize); - std::size_t nData; - nData = filePtr->streamCallback_(fileBufferPtr_->data(), - fileBufferPtr_->size()); - fileBufferPtr_->resize(nData); - if (nData == 0) // no more data! - { - LOG_TRACE << "send stream in loop: no more data"; - filePtr->fileBytesToSend_ = 0; - } - } - if (fileBufferPtr_->empty()) - { - LOG_TRACE << "send stream in loop: break on buffer empty"; - break; - } - auto nToWrite = fileBufferPtr_->size(); - auto nWritten = writeInLoop(fileBufferPtr_->data(), nToWrite); - if (nWritten >= 0) - { -#ifndef NDEBUG // defined by CMake for release build - filePtr->nDataWritten_ += nWritten; - LOG_TRACE << "send stream in loop: bytes written: " << nWritten - << " / total bytes written: " - << filePtr->nDataWritten_; -#endif - if (static_cast(nWritten) < nToWrite) - { - // Partial write - return and wait for next call to continue - fileBufferPtr_->erase(fileBufferPtr_->begin(), - fileBufferPtr_->begin() + nWritten); - if (!ioChannelPtr_->isWriting()) - ioChannelPtr_->enableWriting(); - LOG_TRACE << "send stream in loop: return on partial write " - "(socket buffer full?)"; - return; - } - // LOG_TRACE << "send stream in loop: continue on - // data written"; - fileBufferPtr_->resize(0); - continue; - } - // nWritten < 0 -#ifdef _WIN32 - if (errno != 0 && errno != EWOULDBLOCK) -#else - if (errno != EWOULDBLOCK) -#endif - { - if (errno == EPIPE || errno == ECONNRESET) - { -#ifdef _WIN32 - LOG_TRACE << "WSAENOTCONN or WSAECONNRESET, errno=" - << errno; -#else - LOG_TRACE << "EPIPE or ECONNRESET, errno=" << errno; -#endif - // abort - LOG_TRACE - << "send stream in loop: return on connection closed"; - filePtr->fileBytesToSend_ = 0; - return; - } - // TODO: any others? - LOG_SYSERR << "send stream in loop: return on unexpected error(" - << errno << ")"; - filePtr->fileBytesToSend_ = 0; - return; - } - // Socket buffer full - return and wait for next call - LOG_TRACE << "send stream in loop: break on socket buffer full (?)"; + nodePtr->done(); break; } - if (!ioChannelPtr_->isWriting()) - ioChannelPtr_->enableWriting(); - LOG_TRACE << "send stream in loop: return on loop exit"; - return; - } - // Send file - LOG_TRACE << "send file in loop"; - if (!fileBufferPtr_) - { - fileBufferPtr_ = - std::make_unique>(kMaxSendFileBufferSize); - } - if (fileBufferPtr_->size() < kMaxSendFileBufferSize) - { - fileBufferPtr_->resize(kMaxSendFileBufferSize); - } -#ifndef _WIN32 - lseek(filePtr->sendFd_, filePtr->offset_, SEEK_SET); - while (filePtr->fileBytesToSend_ > 0) - { - auto n = read(filePtr->sendFd_, - &(*fileBufferPtr_)[0], - std::min(fileBufferPtr_->size(), - static_castsize())>( - filePtr->fileBytesToSend_))); -#else - _fseeki64(filePtr->sendFp_, filePtr->offset_, SEEK_SET); - while (filePtr->fileBytesToSend_ > 0) - { - // LOG_TRACE << "send file in loop: fetch more remaining data"; - auto bytes = static_castsize())>( - filePtr->fileBytesToSend_); - auto n = fread(&(*fileBufferPtr_)[0], - 1, - (fileBufferPtr_->size() < bytes ? fileBufferPtr_->size() - : bytes), - filePtr->sendFp_); -#endif - if (n > 0) + auto nWritten = writeInLoop(data, len); + if (nWritten >= 0) { - auto nSend = writeInLoop(&(*fileBufferPtr_)[0], n); - if (nSend >= 0) + hasSent += nWritten; + nodePtr->retrieve(nWritten); + if (static_cast(nWritten) < len) { - filePtr->fileBytesToSend_ -= nSend; - filePtr->offset_ += nSend; - if (static_cast(nSend) < static_cast(n)) - { - if (!ioChannelPtr_->isWriting()) - { - ioChannelPtr_->enableWriting(); - } - LOG_TRACE << "send file in loop: return on partial write " - "(socket buffer full?)"; - return; - } - else if (nSend == n) - { - // LOG_TRACE << "send file in loop: - // continue on data written"; - continue; - } - } - if (nSend < 0) - { -#ifdef _WIN32 - if (errno != 0 && errno != EWOULDBLOCK) -#else - if (errno != EWOULDBLOCK) -#endif - { - // TODO: any others? - if (errno == EPIPE || errno == ECONNRESET) - { -#ifdef _WIN32 - LOG_TRACE << "WSAENOTCONN or WSAECONNRESET, errno=" - << errno; -#else - LOG_TRACE << "EPIPE or ECONNRESET, errno=" << errno; -#endif - LOG_TRACE - << "send file in loop: return on connection closed"; - return; - } - LOG_SYSERR - << "send file in loop: return on unexpected error(" - << errno << ")"; - return; - } - LOG_TRACE - << "send file in loop: break on socket buffer full (?)"; break; } + continue; } - if (n < 0) - { - LOG_SYSERR << "send file in loop: return on read error"; - if (ioChannelPtr_->isWriting()) - ioChannelPtr_->disableWriting(); - return; - } - if (n == 0) + else { - LOG_SYSERR - << "send file in loop: return on read 0 (file truncated)"; - return; + LOG_TRACE << "error(" << errno << ") on send Node in loop"; + return -1; } } - LOG_TRACE << "send file in loop: return on loop exit"; - if (!ioChannelPtr_->isWriting()) - { - ioChannelPtr_->enableWriting(); - } + return hasSent; } #ifndef _WIN32 ssize_t TcpConnectionImpl::writeRaw(const void *buffer, size_t length) @@ -1253,6 +745,19 @@ ssize_t TcpConnectionImpl::writeRaw(const char *buffer, size_t length) #endif if (nWritten > 0) bytesSent_ += nWritten; + else if (!isEAGAIN()) + return nWritten; + if (nWritten < 0) + { + nWritten = 0; + } + if (nWritten < static_cast(length)) + { + LOG_TRACE << "nWritten = " << nWritten << " length = " << length; + if (!ioChannelPtr_->isWriting()) + ioChannelPtr_->enableWriting(); + } + extendLife(); return nWritten; } @@ -1298,7 +803,8 @@ void TcpConnectionImpl::startEncryption( return; } auto sslContextPtr = newSSLContext(*policy, isServer); - tlsProviderPtr_ = newTLSProvider(this, policy, sslContextPtr); + tlsProviderPtr_ = + newTLSProvider(this, std::move(policy), std::move(sslContextPtr)); tlsProviderPtr_->setWriteCallback(onSslWrite); tlsProviderPtr_->setErrorCallback(onSslError); tlsProviderPtr_->setHandshakeCallback(onHandshakeFinished); @@ -1311,9 +817,9 @@ void TcpConnectionImpl::startEncryption( void TcpConnectionImpl::onSslError(TcpConnection *self, SSLError err) { - self->forceClose(); if (self->sslErrorCallback_) self->sslErrorCallback_(err); + self->forceClose(); } void TcpConnectionImpl::onHandshakeFinished(TcpConnection *self) { @@ -1343,3 +849,174 @@ void TcpConnectionImpl::onSslCloseAlert(TcpConnection *self) { self->shutdown(); } +class AsyncStreamImpl : public AsyncStream +{ + public: + explicit AsyncStreamImpl(std::function callback) + : callback_(std::move(callback)) + { + } + AsyncStreamImpl() = delete; + bool send(const char *data, size_t len) override + { + return callback_(data, len); + } + void close() override + { + callback_(nullptr, 0); + callback_ = nullptr; + } + ~AsyncStreamImpl() override + { + if (callback_) + callback_(nullptr, 0); + } + + private: + std::function callback_; +}; +AsyncStreamPtr TcpConnectionImpl::sendAsyncStream(bool disableKickoff) +{ + auto asyncStreamNode = BufferNode::newAsyncStreamBufferNode(); + std::weak_ptr weakPtr = shared_from_this(); + auto asyncStream = std::make_unique( + [asyncStreamNode, weakPtr = std::move(weakPtr)](const char *data, + size_t len) -> bool { + auto thisPtr = weakPtr.lock(); + if (!thisPtr) + { + LOG_DEBUG << "Connection is closed,give up sending"; + return false; + } + if (thisPtr->status_ != ConnStatus::Connected) + { + LOG_DEBUG << "Connection is not connected,give up sending"; + return false; + } + if (thisPtr->loop_->isInLoopThread()) + { + thisPtr->sendAsyncDataInLoop(asyncStreamNode, data, len); + } + else + { + if (data) + { + std::string buffer(data, len); + thisPtr->loop_->queueInLoop([thisPtr, + asyncStreamNode, + buffer = std::move(buffer)]() { + thisPtr->sendAsyncDataInLoop(asyncStreamNode, + buffer.data(), + buffer.length()); + }); + } + else + { + thisPtr->loop_->queueInLoop([thisPtr, asyncStreamNode]() { + thisPtr->sendAsyncDataInLoop(asyncStreamNode, + nullptr, + 0); + }); + } + } + return true; + }); + if (loop_->isInLoopThread()) + { + if (disableKickoff) + { + auto entry = kickoffEntry_.lock(); + if (entry) + { + entry->reset(); + kickoffEntry_.reset(); + } + idleTimeoutBackup_ = idleTimeout_; + idleTimeout_ = 0; + } + + writeBufferList_.push_back(asyncStreamNode); + } + else + { + loop_->queueInLoop([this, + thisPtr = shared_from_this(), + node = std::move(asyncStreamNode), + disableKickoff]() mutable { + if (disableKickoff) + { + auto entry = kickoffEntry_.lock(); + if (entry) + { + entry->reset(); + kickoffEntry_.reset(); + } + idleTimeoutBackup_ = idleTimeout_; + idleTimeout_ = 0; + } + + if (thisPtr->writeBufferList_.empty() && node->remainingBytes() > 0) + { + auto n = thisPtr->sendNodeInLoop(node); + if (n >= 0 && (node->remainingBytes() > 0 || node->available())) + thisPtr->writeBufferList_.push_back(std::move(node)); + } + else + { + thisPtr->writeBufferList_.push_back(std::move(node)); + } + }); + } + return asyncStream; +} +void TcpConnectionImpl::sendAsyncDataInLoop(const BufferNodePtr &node, + const char *data, + size_t len) +{ + loop_->assertInLoopThread(); + if (data) + { + if (len > 0) + { + if (!writeBufferList_.empty() && node == writeBufferList_.front() && + node->remainingBytes() == 0) + { + auto nWritten = writeInLoop(data, len); + if (nWritten < 0) + { + LOG_TRACE << "write error"; + return; + } + if (static_cast(nWritten) < len) + { + node->append(data + nWritten, len - nWritten); + } + } + else + { + node->append(data, len); + } + } + } + else + { + // stream is closed + node->done(); + if (!writeBufferList_.empty() && node == writeBufferList_.front() && + !ioChannelPtr_->isWriting()) + ioChannelPtr_->enableWriting(); + + if (idleTimeoutBackup_ > 0) + { + auto timingWheel = timingWheelWeakPtr_.lock(); + if (timingWheel) + { + auto entry = std::make_shared(shared_from_this()); + kickoffEntry_ = entry; + idleTimeout_ = idleTimeoutBackup_; + idleTimeoutBackup_ = 0; + timingWheel->insertEntry(idleTimeout_, std::move(entry)); + } + } + } +} diff --git a/trantor/net/inner/TcpConnectionImpl.h b/trantor/net/inner/TcpConnectionImpl.h index 2f0ca9a0..f19729a0 100644 --- a/trantor/net/inner/TcpConnectionImpl.h +++ b/trantor/net/inner/TcpConnectionImpl.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #ifndef _WIN32 @@ -30,15 +31,12 @@ namespace trantor class Channel; class Socket; class TcpServer; -void removeConnection(EventLoop *loop, const TcpConnectionPtr &conn); class TcpConnectionImpl : public TcpConnection, public NonCopyable, public std::enable_shared_from_this { friend class TcpServer; friend class TcpClient; - friend void trantor::removeConnection(EventLoop *loop, - const TcpConnectionPtr &conn); public: class KickoffEntry @@ -71,38 +69,38 @@ class TcpConnectionImpl : public TcpConnection, const InetAddress &peerAddr, TLSPolicyPtr policy = nullptr, SSLContextPtr ctx = nullptr); - virtual ~TcpConnectionImpl(); - virtual void send(const char *msg, size_t len) override; - virtual void send(const void *msg, size_t len) override; - virtual void send(const std::string &msg) override; - virtual void send(std::string &&msg) override; - virtual void send(const MsgBuffer &buffer) override; - virtual void send(MsgBuffer &&buffer) override; - virtual void send(const std::shared_ptr &msgPtr) override; - virtual void send(const std::shared_ptr &msgPtr) override; - virtual void sendFile(const char *fileName, - size_t offset = 0, - size_t length = 0) override; - virtual void sendFile(const wchar_t *fileName, - size_t offset = 0, - size_t length = 0) override; - virtual void sendStream( + ~TcpConnectionImpl() override; + void send(const char *msg, size_t len) override; + void send(const void *msg, size_t len) override; + void send(const std::string &msg) override; + void send(std::string &&msg) override; + void send(const MsgBuffer &buffer) override; + void send(MsgBuffer &&buffer) override; + void send(const std::shared_ptr &msgPtr) override; + void send(const std::shared_ptr &msgPtr) override; + void sendFile(const char *fileName, + long long offset, + long long length) override; + void sendFile(const wchar_t *fileName, + long long offset, + long long length) override; + void sendStream( std::function callback) override; - virtual const InetAddress &localAddr() const override + const InetAddress &localAddr() const override { return localAddr_; } - virtual const InetAddress &peerAddr() const override + const InetAddress &peerAddr() const override { return peerAddr_; } - virtual bool connected() const override + bool connected() const override { return status_ == ConnStatus::Connected; } - virtual bool disconnected() const override + bool disconnected() const override { return status_ == ConnStatus::Disconnected; } @@ -113,14 +111,14 @@ class TcpConnectionImpl : public TcpConnection, // return &readBuffer_; // } // set callbacks - virtual void setHighWaterMarkCallback(const HighWaterMarkCallback &cb, - size_t markLen) override + void setHighWaterMarkCallback(const HighWaterMarkCallback &cb, + size_t markLen) override { highWaterMarkCallback_ = cb; highWaterMarkLen_ = markLen; } - virtual void keepAlive() override + void keepAlive() override { idleTimeout_ = 0; auto entry = kickoffEntry_.lock(); @@ -129,66 +127,67 @@ class TcpConnectionImpl : public TcpConnection, entry->reset(); } } - virtual bool isKeepAlive() override + bool isKeepAlive() override { return idleTimeout_ == 0; } - virtual void setTcpNoDelay(bool on) override; - virtual void shutdown() override; - virtual void forceClose() override; - virtual EventLoop *getLoop() override + void setTcpNoDelay(bool on) override; + void shutdown() override; + void forceClose() override; + EventLoop *getLoop() override { return loop_; } - virtual size_t bytesSent() const override + size_t bytesSent() const override { return bytesSent_; } - virtual size_t bytesReceived() const override + size_t bytesReceived() const override { return bytesReceived_; } - virtual bool isSSLConnection() const override + bool isSSLConnection() const override { return tlsProviderPtr_ != nullptr; } - virtual void connectEstablished() override; - virtual void connectDestroyed() override; + void connectEstablished() override; + void connectDestroyed() override; - virtual MsgBuffer *getRecvBuffer() override + MsgBuffer *getRecvBuffer() override { if (tlsProviderPtr_) return &tlsProviderPtr_->getRecvBuffer(); return &readBuffer_; } - virtual std::string applicationProtocol() const override + std::string applicationProtocol() const override { if (tlsProviderPtr_) return tlsProviderPtr_->applicationProtocol(); return ""; } - virtual CertificatePtr peerCertificate() const override + CertificatePtr peerCertificate() const override { if (tlsProviderPtr_) return tlsProviderPtr_->peerCertificate(); return nullptr; } - virtual std::string sniName() const override + std::string sniName() const override { if (tlsProviderPtr_) return tlsProviderPtr_->sniName(); return ""; } - virtual void startEncryption(TLSPolicyPtr policy, - bool isServer, - std::function - upgradeCallback = nullptr) override; + void startEncryption( + TLSPolicyPtr policy, + bool isServer, + std::function upgradeCallback) override; + AsyncStreamPtr sendAsyncStream(bool disableKickoff) override; void enableKickingOff( size_t timeout, @@ -210,61 +209,12 @@ class TcpConnectionImpl : public TcpConnection, std::weak_ptr kickoffEntry_; std::weak_ptr timingWheelWeakPtr_; size_t idleTimeout_{0}; + size_t idleTimeoutBackup_{0}; Date lastTimingWheelUpdateTime_; void extendLife(); -#ifndef _WIN32 - void sendFile(int sfd, size_t offset = 0, size_t length = 0); -#else - void sendFile(FILE *fp, size_t offset = 0, size_t length = 0); -#endif + void sendFile(BufferNodePtr &&fileNode); protected: - struct BufferNode - { - // sendFile() specific -#ifndef _WIN32 - int sendFd_{-1}; - off_t offset_{0}; -#else - FILE *sendFp_{nullptr}; - long long offset_{0}; -#endif - ssize_t fileBytesToSend_{0}; - // sendStream() specific - std::function streamCallback_; -#ifndef NDEBUG // defined by CMake for release build - std::size_t nDataWritten_{0}; -#endif - // generic - std::shared_ptr msgBuffer_; - bool isFile() const - { - if (streamCallback_) - return true; -#ifndef _WIN32 - if (sendFd_ >= 0) - return true; -#else - if (sendFp_) - return true; -#endif - return false; - } - ~BufferNode() - { -#ifndef _WIN32 - if (sendFd_ >= 0) - close(sendFd_); -#else - if (sendFp_) - fclose(sendFp_); -#endif - if (streamCallback_) - streamCallback_(nullptr, 0); // cleanup callback internals - } - bool closeConnection_ = false; - }; - using BufferNodePtr = std::shared_ptr; enum class ConnStatus { Disconnected, @@ -284,27 +234,29 @@ class TcpConnectionImpl : public TcpConnection, void handleClose(); void handleError(); // virtual void sendInLoop(const std::string &msg); - - void sendFileInLoop(const BufferNodePtr &file); + void sendAsyncDataInLoop(const BufferNodePtr &node, + const char *data, + size_t len); + // -1: error, 0: EAGAIN, >0: bytes sent + ssize_t sendNodeInLoop(const BufferNodePtr &node); #ifndef _WIN32 void sendInLoop(const void *buffer, size_t length); ssize_t writeRaw(const void *buffer, size_t length); ssize_t writeInLoop(const void *buffer, size_t length); #else void sendInLoop(const char *buffer, size_t length); + // -1: error, 0: EAGAIN, >0: bytes sent ssize_t writeRaw(const char *buffer, size_t length); + // -1: error, 0: EAGAIN, >0: bytes sent ssize_t writeInLoop(const char *buffer, size_t length); #endif - size_t highWaterMarkLen_; + size_t highWaterMarkLen_{0}; std::string name_; - uint64_t sendNum_{0}; - std::mutex sendNumMutex_; - size_t bytesSent_{0}; size_t bytesReceived_{0}; - std::unique_ptr> fileBufferPtr_; + // std::unique_ptr> fileBufferPtr_; std::shared_ptr tlsProviderPtr_; std::function upgradeCallback_; diff --git a/trantor/net/inner/poller/EpollPoller.cc b/trantor/net/inner/poller/EpollPoller.cc index 8be13aa5..c9c4f7d4 100644 --- a/trantor/net/inner/poller/EpollPoller.cc +++ b/trantor/net/inner/poller/EpollPoller.cc @@ -53,7 +53,7 @@ const int kDeleted = 2; EpollPoller::EpollPoller(EventLoop *loop) : Poller(loop), #ifdef _WIN32 - // wepoll does not suppor flags + // wepoll does not support flags epollfd_(::epoll_create1(0)), #else epollfd_(::epoll_create1(EPOLL_CLOEXEC)), @@ -85,7 +85,7 @@ void EpollPoller::poll(int timeoutMs, ChannelList *activeChannels) // Timestamp now(Timestamp::now()); if (numEvents > 0) { - // LOG_TRACE << numEvents << " events happended"; + // LOG_TRACE << numEvents << " events happened"; fillActiveChannels(numEvents, activeChannels); if (static_cast(numEvents) == events_.size()) { @@ -94,7 +94,7 @@ void EpollPoller::poll(int timeoutMs, ChannelList *activeChannels) } else if (numEvents == 0) { - // std::cout << "nothing happended" << std::endl; + // std::cout << "nothing happened" << std::endl; } else { diff --git a/trantor/net/inner/poller/KQueue.cc b/trantor/net/inner/poller/KQueue.cc index acf4ae78..98636ef2 100644 --- a/trantor/net/inner/poller/KQueue.cc +++ b/trantor/net/inner/poller/KQueue.cc @@ -59,7 +59,7 @@ void KQueue::poll(int timeoutMs, ChannelList *activeChannels) // Timestamp now(Timestamp::now()); if (numEvents > 0) { - // LOG_TRACE << numEvents << " events happended"; + // LOG_TRACE << numEvents << " events happened"; fillActiveChannels(numEvents, activeChannels); if (static_cast(numEvents) == events_.size()) { @@ -68,7 +68,7 @@ void KQueue::poll(int timeoutMs, ChannelList *activeChannels) } else if (numEvents == 0) { - // std::cout << "nothing happended" << std::endl; + // std::cout << "nothing happened" << std::endl; } else { diff --git a/trantor/net/inner/poller/PollPoller.cc b/trantor/net/inner/poller/PollPoller.cc index 5223433a..c3cb5ab4 100644 --- a/trantor/net/inner/poller/PollPoller.cc +++ b/trantor/net/inner/poller/PollPoller.cc @@ -30,7 +30,7 @@ PollPoller::PollPoller(EventLoop* loop) : Poller(loop) { std::call_once(warning_flag, []() { LOG_WARN << "Creating a PollPoller. This poller is slow and should " - "only be used when no other pollers are avaliable"; + "only be used when no other pollers are available"; }); } diff --git a/trantor/net/inner/tlsprovider/BotanTLSProvider.cc b/trantor/net/inner/tlsprovider/BotanTLSProvider.cc index b463c18e..93c1ee4d 100644 --- a/trantor/net/inner/tlsprovider/BotanTLSProvider.cc +++ b/trantor/net/inner/tlsprovider/BotanTLSProvider.cc @@ -32,6 +32,19 @@ static std::once_flag systemCertStoreInitFlag; using namespace trantor; +static std::string join(const std::vector &vec, + const std::string &delim) +{ + std::string ret; + for (auto const &str : vec) + { + if (ret.empty() == false) + ret += delim; + ret += str; + } + return ret; +} + class Credentials : public Botan::Credentials_Manager { public: @@ -206,25 +219,31 @@ struct BotanTLSProvider : public TLSProvider, if (getBufferedData().readableBytes() != 0) { errno = EAGAIN; - return -1; + return 0; } // Limit the size of the data we send in one go to avoid holding massive // buffers in memory. constexpr size_t maxSend = 64 * 1024; - if (size > maxSend) - size = maxSend; - channel_->send((const uint8_t *)ptr, size); - - // HACK: Botan doesn't provide a way to know how much raw data has been - // written to the underlying transport. So we have to assume that all - // data has been written. And cache the unwritten data in writeBuffer_. - // Then "fake" the consumed size in sendData() to make the caller think - // that all data has been written. Then return -1 if the underlying - // socket is not writable at all (i.e. write is all or nothing) - if (lastWriteSize_ == -1) - return -1; - return size; + size_t hasSent = 0; + while (hasSent < size && getBufferedData().readableBytes() == 0) + { + auto trunkLen = size - hasSent; + if (trunkLen > maxSend) + trunkLen = maxSend; + channel_->send((const uint8_t *)ptr, size); + // HACK: Botan doesn't provide a way to know how much raw data has + // been written to the underlying transport. So we have to assume + // that all data has been written. And cache the unwritten data in + // writeBuffer_. Then "fake" the consumed size in sendData() to make + // the caller think that all data has been written. Then return -1 + // if the underlying socket is not writable at all (i.e. write is + // all or nothing) + if (lastWriteSize_ == -1) + return -1; + hasSent += trunkLen; + } + return static_cast(hasSent); } virtual void close() override @@ -328,6 +347,28 @@ struct BotanTLSProvider : public TLSProvider, messageCallback_(conn_, &recvBuffer_); } + std::string tls_server_choose_app_protocol( + const std::vector &client_protos) override + { + assert(contextPtr_->isServer); + if (policyPtr_->getAlpnProtocols().empty() || client_protos.empty()) + return ""; + + for (auto const &proto : client_protos) + { + if (std::find(policyPtr_->getAlpnProtocols().begin(), + policyPtr_->getAlpnProtocols().end(), + proto) != policyPtr_->getAlpnProtocols().end()) + return proto; + } + + throw Botan::TLS::TLS_Exception( + Botan::TLS::Alert::NoApplicationProtocol, + "No supported application protocol found. Client offered: " + + join(client_protos, ", ") + " but we support: " + + join(policyPtr_->getAlpnProtocols(), ", ")); + } + void tls_alert(Botan::TLS::Alert alert) override { if (alert.type() == Botan::TLS::Alert::CloseNotify) @@ -446,6 +487,6 @@ SSLContextPtr trantor::newSSLContext(const TLSPolicy &policy, bool server) if (policy.getUseOldTLS()) LOG_WARN << "SSLPloicy have set useOldTLS to true. BUt Botan does not " - "support TLS/SSL below TLS 1.2. Ignoreing this option."; + "support TLS/SSL below TLS 1.2. Ignoring this option."; return ctx; } diff --git a/trantor/net/inner/tlsprovider/OpenSSLProvider.cc b/trantor/net/inner/tlsprovider/OpenSSLProvider.cc index 94e916d1..f91e9cde 100644 --- a/trantor/net/inner/tlsprovider/OpenSSLProvider.cc +++ b/trantor/net/inner/tlsprovider/OpenSSLProvider.cc @@ -26,6 +26,9 @@ static bool sslInitFlag = []() { SSL_load_error_strings(); ERR_load_BIO_strings(); ERR_load_crypto_strings(); +#elif defined(LIBRESSL_VERSION_NUMBER) + // LibreSSL needs explicit de-init + atexit(OPENSSL_cleanup); #endif return true; }(); @@ -130,7 +133,7 @@ static bool validatePeerCertificate(SSL *ssl, { assert(ssl != nullptr); assert(cert != nullptr); - LOG_TRACE << "Validating peer cerificate"; + LOG_TRACE << "Validating peer certificate"; if (isServer) { @@ -573,24 +576,29 @@ struct OpenSSLProvider : public TLSProvider, public NonCopyable if (getBufferedData().readableBytes() != 0) { errno = EAGAIN; - return -1; + return 0; } // Limit the size of the data we send in one go to avoid holding massive // buffers in memory. constexpr size_t maxSend = 64 * 1024; - if (len > maxSend) - len = maxSend; - - int n = SSL_write(ssl_, data, (int)len); - if (n <= 0 && len != 0) + size_t hasSent = 0; + while (hasSent < len && getBufferedData().readableBytes() == 0) { - handleSSLError(SSLError::kSSLProtocolError); - return -1; + auto trunkLen = len - hasSent; + if (trunkLen > maxSend) + trunkLen = maxSend; + int n = SSL_write(ssl_, data + hasSent, (int)trunkLen); + if (n <= 0 && len != 0) + { + handleSSLError(SSLError::kSSLProtocolError); + return -1; + } + auto num = sendTLSData(); + if (num == -1) + return -1; + hasSent += trunkLen; } - auto num = sendTLSData(); - if (num == -1) - return -1; - return len; + return static_cast(hasSent); } bool processHandshake() @@ -762,11 +770,13 @@ struct OpenSSLProvider : public TLSProvider, public NonCopyable return 0; int n = writeCallback_(conn_, data, len); - int offset = n; - if (n == -1) - offset = 0; - appendToWriteBuffer((char *)data + offset, len - offset); + if (n >= 0) + { + appendToWriteBuffer((char *)data + n, len - n); + } BIO_reset(wbio_); + if (n < 0) + return -1; return len; } diff --git a/trantor/tests/CMakeLists.txt b/trantor/tests/CMakeLists.txt index 047571dd..90f75511 100644 --- a/trantor/tests/CMakeLists.txt +++ b/trantor/tests/CMakeLists.txt @@ -22,6 +22,7 @@ add_executable(path_conversion_test PathConversionTest.cc) add_executable(logger_macro_test LoggerMacroTest.cc) add_executable(delayed_ssl_server_test DelayedSSLServerTest.cc) add_executable(delayed_ssl_client_test DelayedSSLClientTest.cc) +add_executable(tcp_asyncstream_server_test TcpAsyncStreamServerTest.cc) set(targets_list ssl_server_test ssl_client_test @@ -46,7 +47,9 @@ set(targets_list path_conversion_test logger_macro_test delayed_ssl_server_test - delayed_ssl_client_test) + delayed_ssl_client_test + tcp_asyncstream_server_test +) if(HAVE_SPDLOG) add_executable(spdlogger_test SpdLoggerTest.cc) @@ -59,4 +62,4 @@ set_property(TARGET ${targets_list} PROPERTY CXX_EXTENSIONS OFF) foreach(T ${targets_list}) target_link_libraries(${T} PRIVATE trantor) -endforeach() \ No newline at end of file +endforeach() diff --git a/trantor/tests/RunInLoopTest2.cc b/trantor/tests/RunInLoopTest2.cc index ce969a37..0b68e845 100644 --- a/trantor/tests/RunInLoopTest2.cc +++ b/trantor/tests/RunInLoopTest2.cc @@ -8,8 +8,8 @@ int main() { - std::atomic counter; - counter = 0; + // Local variable to be used within the loopThread + uint64_t counter = 0; std::promise pro; auto ft = pro.get_future(); trantor::EventLoopThread loopThread; @@ -20,7 +20,7 @@ int main() { loop->queueInLoop([&counter, &pro]() { ++counter; - if (counter.load() == 110000) + if (counter == 110000) pro.set_value(1); }); } @@ -32,7 +32,7 @@ int main() { loop->runInLoop([&counter, &pro]() { ++counter; - if (counter.load() == 110000) + if (counter == 110000) pro.set_value(1); }); } @@ -40,5 +40,5 @@ int main() } loopThread.run(); ft.get(); - std::cout << "counter=" << counter.load() << std::endl; + std::cout << "counter=" << counter << std::endl; } diff --git a/trantor/tests/SSLClientTest.cc b/trantor/tests/SSLClientTest.cc index 783a694f..397f92d1 100644 --- a/trantor/tests/SSLClientTest.cc +++ b/trantor/tests/SSLClientTest.cc @@ -14,7 +14,7 @@ int main() #if USE_IPV6 InetAddress serverAddr("::1", 8888, true); #else - InetAddress serverAddr("127.0.0.1", 443); + InetAddress serverAddr("127.0.0.1", 8888); #endif std::shared_ptr client[10]; std::atomic_int connCount; diff --git a/trantor/tests/SendfileTest.cc b/trantor/tests/SendfileTest.cc index c0abb5b5..2b158088 100644 --- a/trantor/tests/SendfileTest.cc +++ b/trantor/tests/SendfileTest.cc @@ -63,10 +63,10 @@ int main(int argc, char *argv[]) for (int i = 0; i < 5; ++i) { connPtr->sendFile(argv[1]); - char str[64]; ++counter; - sprintf(str, "\n%d files sent!\n", counter); - connPtr->send(str, strlen(str)); + std::string str = + "\n" + std::to_string(counter) + " files sent!\n"; + connPtr->send(std::move(str)); } }); t.detach(); @@ -74,10 +74,10 @@ int main(int argc, char *argv[]) for (int i = 0; i < 3; ++i) { connPtr->sendFile(argv[1]); - char str[64]; ++counter; - sprintf(str, "\n%d files sent!\n", counter); - connPtr->send(str, strlen(str)); + std::string str = + "\n" + std::to_string(counter) + " files sent!\n"; + connPtr->send(std::move(str)); } } else if (connPtr->disconnected()) diff --git a/trantor/tests/SendstreamTest.cc b/trantor/tests/SendstreamTest.cc index 144adc71..49eacb1b 100644 --- a/trantor/tests/SendstreamTest.cc +++ b/trantor/tests/SendstreamTest.cc @@ -79,10 +79,10 @@ int main(int argc, char *argv[]) std::placeholders::_1, std::placeholders::_2); connPtr->sendStream(callback); - char str[64]; ++counter; - sprintf(str, "\n%d streams sent!\n", counter); - connPtr->send(str, strlen(str)); + std::string str = + "\n" + std::to_string(counter) + " streams sent!\n"; + connPtr->send(std::move(str)); } }); t.detach(); @@ -101,10 +101,10 @@ int main(int argc, char *argv[]) std::placeholders::_1, std::placeholders::_2); connPtr->sendStream(callback); - char str[64]; ++counter; - sprintf(str, "\n%d streams sent!\n", counter); - connPtr->send(str, strlen(str)); + std::string str = + "\n" + std::to_string(counter) + " streams sent!\n"; + connPtr->send(std::move(str)); } } else if (connPtr->disconnected()) diff --git a/trantor/tests/TcpAsyncStreamServerTest.cc b/trantor/tests/TcpAsyncStreamServerTest.cc new file mode 100644 index 00000000..d9761763 --- /dev/null +++ b/trantor/tests/TcpAsyncStreamServerTest.cc @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include +using namespace trantor; +#define USE_IPV6 0 +int main() +{ + LOG_DEBUG << "test start"; + Logger::setLogLevel(Logger::kTrace); + EventLoopThread loopThread; + loopThread.run(); +#if USE_IPV6 + InetAddress addr(8888, true, true); +#else + InetAddress addr(8888); +#endif + TcpServer server(loopThread.getLoop(), addr, "test"); + + server.setRecvMessageCallback( + [](const TcpConnectionPtr &connectionPtr, MsgBuffer *buffer) { + // LOG_DEBUG<<"recv callback!"; + std::cout << std::string(buffer->peek(), buffer->readableBytes()); + connectionPtr->send(buffer->peek(), buffer->readableBytes()); + buffer->retrieveAll(); + // connectionPtr->forceClose(); + }); + server.setConnectionCallback([](const TcpConnectionPtr &connPtr) { + if (connPtr->connected()) + { + LOG_DEBUG << "New connection"; + auto stream = connPtr->sendAsyncStream(); + stream->send("hello world 1..."); + std::thread([stream = std::move(stream)] { + for (int i = 2; i < 10; i++) + { + std::this_thread::sleep_for(std::chrono::seconds(1)); + stream->send("hello world " + std::to_string(i) + "..."); + } + stream->close(); + }).detach(); + connPtr->send("hello world"); + } + else if (connPtr->disconnected()) + { + LOG_DEBUG << "connection disconnected"; + } + }); + server.setIoLoopNum(3); + server.start(); + loopThread.wait(); +} diff --git a/trantor/tests/TcpClientTest.cc b/trantor/tests/TcpClientTest.cc index 2530a957..bbbb33f1 100644 --- a/trantor/tests/TcpClientTest.cc +++ b/trantor/tests/TcpClientTest.cc @@ -26,7 +26,7 @@ int main() std::shared_ptr client[10]; std::atomic_int connCount; connCount = 10; - for (int i = 0; i < 1; ++i) + for (int i = 0; i < 10; ++i) { client[i] = std::make_shared(&loop, serverAddr, @@ -59,8 +59,7 @@ int main() if (conn->connected()) { LOG_DEBUG << i << " connected!"; - char tmp[20]; - sprintf(tmp, "%d client!!", i); + std::string tmp = std::to_string(i) + " client!!"; conn->send(tmp); } else diff --git a/trantor/unittests/CMakeLists.txt b/trantor/unittests/CMakeLists.txt index 61ac67a4..cdbc9ff9 100644 --- a/trantor/unittests/CMakeLists.txt +++ b/trantor/unittests/CMakeLists.txt @@ -13,7 +13,8 @@ set(UNITTEST_TARGETS split_string_unittest string_encoding_unittest ssl_name_verify_unittest - hash_unittest) + hash_unittest +) set_property(TARGET ${UNITTEST_TARGETS} PROPERTY CXX_STANDARD 14) set_property(TARGET ${UNITTEST_TARGETS} PROPERTY CXX_STANDARD_REQUIRED ON) set_property(TARGET ${UNITTEST_TARGETS} PROPERTY CXX_EXTENSIONS OFF) diff --git a/trantor/unittests/DateUnittest.cc b/trantor/unittests/DateUnittest.cc index bdb55fd4..4583bc98 100644 --- a/trantor/unittests/DateUnittest.cc +++ b/trantor/unittests/DateUnittest.cc @@ -7,20 +7,20 @@ TEST(Date, constructorTest) { EXPECT_STREQ("1985-01-01 00:00:00", trantor::Date(1985, 1, 1) - .toCustomedFormattedStringLocal("%Y-%m-%d %H:%M:%S") + .toCustomFormattedStringLocal("%Y-%m-%d %H:%M:%S") .c_str()); EXPECT_STREQ("2004-02-29 00:00:00.000000", trantor::Date(2004, 2, 29) - .toCustomedFormattedStringLocal("%Y-%m-%d %H:%M:%S", true) + .toCustomFormattedStringLocal("%Y-%m-%d %H:%M:%S", true) .c_str()); EXPECT_STRNE("2001-02-29 00:00:00.000000", trantor::Date(2001, 2, 29) - .toCustomedFormattedStringLocal("%Y-%m-%d %H:%M:%S", true) + .toCustomFormattedStringLocal("%Y-%m-%d %H:%M:%S", true) .c_str()); EXPECT_STREQ("2018-01-01 00:00:00.000000", trantor::Date(2018, 1, 1, 12, 12, 12, 2321) .roundDay() - .toCustomedFormattedStringLocal("%Y-%m-%d %H:%M:%S", true) + .toCustomFormattedStringLocal("%Y-%m-%d %H:%M:%S", true) .c_str()); } TEST(Date, DatabaseStringTest) diff --git a/trantor/utils/AsyncFileLogger.cc b/trantor/utils/AsyncFileLogger.cc index afcc6a79..8aa034d6 100755 --- a/trantor/utils/AsyncFileLogger.cc +++ b/trantor/utils/AsyncFileLogger.cc @@ -22,7 +22,7 @@ #include #endif #else -#include +#include #endif #include #include @@ -64,7 +64,7 @@ AsyncFileLogger::~AsyncFileLogger() } while (!writeBuffers_.empty()) { - StringPtr tmpPtr = (StringPtr &&) writeBuffers_.front(); + StringPtr tmpPtr = (StringPtr &&)writeBuffers_.front(); writeBuffers_.pop(); writeLogToFile(tmpPtr); } @@ -162,7 +162,7 @@ void AsyncFileLogger::logThreadFunc() while (!tmpBuffers_.empty()) { - StringPtr tmpPtr = (StringPtr &&) tmpBuffers_.front(); + StringPtr tmpPtr = (StringPtr &&)tmpBuffers_.front(); tmpBuffers_.pop(); writeLogToFile(tmpPtr); tmpPtr->clear(); @@ -269,7 +269,7 @@ void AsyncFileLogger::LoggerFile::switchLog(bool openNewOne) // NOTE: Remember to update initFilenameQueue() if name format changes std::string newName = filePath_ + fileBaseName_ + "." + - creationDate_.toCustomedFormattedString("%y%m%d-%H%M%S") + + creationDate_.toCustomFormattedString("%y%m%d-%H%M%S") + std::string(seq) + fileExtName_; #if !defined(_WIN32) || defined(__MINGW32__) rename(fileFullName_.c_str(), newName.c_str()); diff --git a/trantor/utils/Date.cc b/trantor/utils/Date.cc index dd9c284f..0da38731 100644 --- a/trantor/utils/Date.cc +++ b/trantor/utils/Date.cc @@ -21,7 +21,7 @@ #include #include #ifdef _WIN32 -#include +#include #include #endif @@ -142,8 +142,8 @@ std::string Date::toFormattedString(bool showMicroseconds) const } return buf; } -std::string Date::toCustomedFormattedString(const std::string &fmtStr, - bool showMicroseconds) const +std::string Date::toCustomFormattedString(const std::string &fmtStr, + bool showMicroseconds) const { char buf[256] = {0}; time_t seconds = @@ -163,9 +163,9 @@ std::string Date::toCustomedFormattedString(const std::string &fmtStr, snprintf(decimals, sizeof(decimals), ".%06d", microseconds); return std::string(buf) + decimals; } -void Date::toCustomedFormattedString(const std::string &fmtStr, - char *str, - size_t len) const +void Date::toCustomFormattedString(const std::string &fmtStr, + char *str, + size_t len) const { // not safe time_t seconds = @@ -323,8 +323,8 @@ Date Date::fromDbString(const std::string &datetime) static_cast(timezoneOffset())); } -std::string Date::toCustomedFormattedStringLocal(const std::string &fmtStr, - bool showMicroseconds) const +std::string Date::toCustomFormattedStringLocal(const std::string &fmtStr, + bool showMicroseconds) const { char buf[256] = {0}; time_t seconds = diff --git a/trantor/utils/Date.h b/trantor/utils/Date.h index 7ea7c974..4b1c8e60 100644 --- a/trantor/utils/Date.h +++ b/trantor/utils/Date.h @@ -202,8 +202,9 @@ class TRANTOR_EXPORT Date */ std::string toFormattedString(bool showMicroseconds) const; + /* clang-format off */ /** - * @brief Generate a UTC time string formated by the @p fmtStr + * @brief Generate a UTC time string formatted by the @p fmtStr * @param fmtStr is the format string for the function strftime() * @param showMicroseconds whether the microseconds are returned. * @note Examples: @@ -211,27 +212,60 @@ class TRANTOR_EXPORT Date * @p showMicroseconds is false * - "2018-01-01 10:10:25:102414" if the @p fmtStr is "%Y-%m-%d %H:%M:%S" * and the @p showMicroseconds is true + * @deprecated Replaced by toCustomFormattedString */ + [[deprecated("Replaced by toCustomFormattedString")]] std::string toCustomedFormattedString(const std::string &fmtStr, - bool showMicroseconds = false) const; - + bool showMicroseconds = false) const + { + return toCustomFormattedString(fmtStr, showMicroseconds); + }; + /* clang-format on */ + /** + * @brief Generate a UTC time string formatted by the @p fmtStr + * @param fmtStr is the format string for the function strftime() + * @param showMicroseconds whether the microseconds are returned. + * @note Examples: + * - "2018-01-01 10:10:25" if the @p fmtStr is "%Y-%m-%d %H:%M:%S" and the + * @p showMicroseconds is false + * - "2018-01-01 10:10:25:102414" if the @p fmtStr is "%Y-%m-%d %H:%M:%S" + * and the @p showMicroseconds is true + */ + std::string toCustomFormattedString(const std::string &fmtStr, + bool showMicroseconds = false) const; /** * @brief Generate a local time zone string, the format of the string is - * same as the mothed toFormattedString + * same as the method toFormattedString * * @param showMicroseconds * @return std::string */ std::string toFormattedStringLocal(bool showMicroseconds) const; + /* clang-format off */ /** - * @brief Generate a local time zone string formated by the @p fmtStr + * @brief Generate a local time zone string formatted by the @p fmtStr * * @param fmtStr * @param showMicroseconds * @return std::string + * @deprecated Replaced by toCustomFormattedString */ - std::string toCustomedFormattedStringLocal( + [[deprecated("Replaced by toCustomFormattedStringLocal")]] + std::string toCustomedFormattedStringLocal(const std::string &fmtStr, + bool showMicroseconds = false) const + { + return toCustomFormattedStringLocal(fmtStr, showMicroseconds); + } + /* clang-format on */ + /** + * @brief Generate a local time zone string formatted by the @p fmtStr + * + * @param fmtStr + * @param showMicroseconds + * @return std::string + */ + std::string toCustomFormattedStringLocal( const std::string &fmtStr, bool showMicroseconds = false) const; @@ -261,16 +295,34 @@ class TRANTOR_EXPORT Date */ static Date fromDbString(const std::string &datetime); + /* clang-format off */ /** * @brief Generate a UTC time string. * * @param fmtStr The format string. * @param str The string buffer for the generated time string. * @param len The length of the string buffer. + * @deprecated Replaced by toCustomFormattedString */ + [[deprecated("Replaced by toCustomFormattedString")]] void toCustomedFormattedString(const std::string &fmtStr, char *str, - size_t len) const; // UTC + size_t len) const + { + toCustomFormattedString(fmtStr, str, len); + } + /* clang-format on */ + + /** + * @brief Generate a UTC time string. + * + * @param fmtStr The format string. + * @param str The string buffer for the generated time string. + * @param len The length of the string buffer. + */ + void toCustomFormattedString(const std::string &fmtStr, + char *str, + size_t len) const; // UTC /** * @brief Return true if the time point is in a same second as another. diff --git a/trantor/utils/Logger.h b/trantor/utils/Logger.h index 705c60b5..16d040aa 100644 --- a/trantor/utils/Logger.h +++ b/trantor/utils/Logger.h @@ -288,8 +288,8 @@ class TRANTOR_EXPORT Logger : public NonCopyable #endif return logLevel; } - static std::function - &outputFunc_() + static std::function & + outputFunc_() { static std::function outputFunc = Logger::defaultOutputFunction; @@ -300,8 +300,8 @@ class TRANTOR_EXPORT Logger : public NonCopyable static std::function flushFunc = Logger::defaultFlushFunction; return flushFunc; } - static std::function - &outputFunc_(size_t index) + static std::function & + outputFunc_(size_t index) { static std::vector< std::function> diff --git a/trantor/utils/MsgBuffer.h b/trantor/utils/MsgBuffer.h index 9b29d0f3..4e8600cf 100644 --- a/trantor/utils/MsgBuffer.h +++ b/trantor/utils/MsgBuffer.h @@ -22,7 +22,7 @@ #include #include #include -#ifdef _WIN32 +#if defined(_WIN32) && !defined(_SSIZE_T_DEFINED) using ssize_t = std::intptr_t; #endif @@ -207,7 +207,7 @@ class TRANTOR_EXPORT MsgBuffer void appendInt32(const uint32_t i); /** - * @brief Appaend a unsigned int64 value to the end of the buffer. + * @brief Append a unsigned int64 value to the end of the buffer. * * @param l */ diff --git a/trantor/utils/SerialTaskQueue.cc b/trantor/utils/SerialTaskQueue.cc index 4b989ccb..71a7103a 100644 --- a/trantor/utils/SerialTaskQueue.cc +++ b/trantor/utils/SerialTaskQueue.cc @@ -20,7 +20,7 @@ namespace trantor { SerialTaskQueue::SerialTaskQueue(const std::string &name) - : queueName_(name.empty() ? "SerailTaskQueue" : name), + : queueName_(name.empty() ? "SerialTaskQueue" : name), loopThread_(queueName_) { loopThread_.run(); diff --git a/trantor/utils/SerialTaskQueue.h b/trantor/utils/SerialTaskQueue.h index 04742bb2..f0165728 100644 --- a/trantor/utils/SerialTaskQueue.h +++ b/trantor/utils/SerialTaskQueue.h @@ -58,7 +58,7 @@ class TRANTOR_EXPORT SerialTaskQueue : public TaskQueue SerialTaskQueue() = delete; /** - * @brief Construct a new serail task queue instance. + * @brief Construct a new serial task queue instance. * * @param name */ @@ -66,13 +66,28 @@ class TRANTOR_EXPORT SerialTaskQueue : public TaskQueue virtual ~SerialTaskQueue(); + /* clang-format off */ /** * @brief Check whether a task is running in the queue. * * @return true * @return false + * @deprecated Use isRunningTask instead */ + [[deprecated("Use isRunningTask instead")]] bool isRuningTask() + { + return isRunningTask(); + } + /* clang-format on */ + + /** + * @brief Check whether a task is running in the queue. + * + * @return true + * @return false + */ + bool isRunningTask() { return loopThread_.getLoop() ? loopThread_.getLoop()->isCallingFunctions() diff --git a/trantor/utils/TaskQueue.h b/trantor/utils/TaskQueue.h index fb656d5c..b5309718 100644 --- a/trantor/utils/TaskQueue.h +++ b/trantor/utils/TaskQueue.h @@ -36,7 +36,7 @@ class TaskQueue : public NonCopyable }; /** - * @brief Run a task in the queue sychronously. This means that the task is + * @brief Run a task in the queue synchronously. This means that the task is * executed before the method returns. * * @param task diff --git a/trantor/utils/Utilities.cc b/trantor/utils/Utilities.cc index bfaf61ab..665253d2 100644 --- a/trantor/utils/Utilities.cc +++ b/trantor/utils/Utilities.cc @@ -14,7 +14,7 @@ #include "Utilities.h" #ifdef _WIN32 -#include +#include #include #include #else // _WIN32 @@ -366,10 +366,10 @@ Hash128 md5(const void *data, size_t len) Hash160 sha1(const void *data, size_t len) { SHA1_CTX ctx; - TrantorSHA1Init(&ctx); - TrantorSHA1Update(&ctx, (const unsigned char *)data, len); + trantor_sha1_init(&ctx); + trantor_sha1_update(&ctx, (const unsigned char *)data, len); Hash160 hash; - TrantorSHA1Final((unsigned char *)&hash, &ctx); + trantor_sha1_final((unsigned char *)&hash, &ctx); return hash; } diff --git a/trantor/utils/Utilities.h b/trantor/utils/Utilities.h index 1a4390b0..c21c2aeb 100644 --- a/trantor/utils/Utilities.h +++ b/trantor/utils/Utilities.h @@ -172,7 +172,7 @@ inline std::string fromNativePath(const std::wstring &strPath) } /** - * @brief Check if the name supplied by the SSL Cert matchs a FQDN + * @brief Check if the name supplied by the SSL Cert matches a FQDN * @param certName The name supplied by the SSL Cert * @param hostName The FQDN to match * @@ -243,7 +243,7 @@ inline Hash256 sha3(const std::string &str) /** * @brief Compute the BLAKE2b hash of the given data * @note When in doubt, use SHA3 or BLAKE2b. Both are safe and SHA3 is faster if - * you are using OpenSSL and it has SHA3 in hardware mode. Owtherwise BLAKE2b is + * you are using OpenSSL and it has SHA3 in hardware mode. Otherwise BLAKE2b is * faster in software. */ TRANTOR_EXPORT Hash256 blake2b(const void *data, size_t len); @@ -255,7 +255,7 @@ inline Hash256 blake2b(const std::string &str) /** * @brief hex encode the given data * @note When in doubt, use SHA3 or BLAKE2b. Both are safe and SHA3 is faster if - * you are using OpenSSL and it has SHA3 in hardware mode. Owtherwise BLAKE2b is + * you are using OpenSSL and it has SHA3 in hardware mode. Otherwise BLAKE2b is * faster in software. */ TRANTOR_EXPORT std::string toHexString(const void *data, size_t len); @@ -280,7 +280,7 @@ inline std::string toHexString(const Hash256 &hash) * @param size Size of the buffer * @return true if successful, false otherwise * - * @note This function really sholdn't fail, but it's possible that + * @note This function really shouldn't fail, but it's possible that * * - OpenSSL can't access /dev/urandom * - Compiled with glibc that supports getentropy() but the kernel doesn't diff --git a/trantor/utils/crypto/botan.cc b/trantor/utils/crypto/botan.cc index a63e8d3b..32a0370d 100644 --- a/trantor/utils/crypto/botan.cc +++ b/trantor/utils/crypto/botan.cc @@ -1,6 +1,8 @@ #include #include +#include + namespace trantor { namespace utils @@ -36,6 +38,7 @@ Hash256 sha3(const void* data, size_t len) { Hash256 hash; auto sha3 = Botan::HashFunction::create("SHA-3(256)"); + assert(sha3 != nullptr); sha3->update((const unsigned char*)data, len); sha3->final((unsigned char*)&hash); return hash; @@ -45,6 +48,7 @@ Hash256 blake2b(const void* data, size_t len) { Hash256 hash; auto blake2b = Botan::HashFunction::create("BLAKE2b(256)"); + assert(blake2b != nullptr); blake2b->update((const unsigned char*)data, len); blake2b->final((unsigned char*)&hash); return hash; diff --git a/trantor/utils/crypto/md5.cc b/trantor/utils/crypto/md5.cc index 60558664..d9a72bdc 100644 --- a/trantor/utils/crypto/md5.cc +++ b/trantor/utils/crypto/md5.cc @@ -45,9 +45,9 @@ order. } /*********************** FUNCTION DEFINITIONS ***********************/ -void trantor_md5_transform(MD5_CTX *ctx, const BYTE data[]) +void trantor_md5_transform(MD5_CTX *ctx, const uint8_t data[]) { - WORD a, b, c, d, m[16], i, j; + uint32_t a, b, c, d, m[16], i, j; // MD5 specifies big endian byte order, but this implementation assumes a // little endian byte order CPU. Reverse all the bytes upon input, and @@ -145,7 +145,7 @@ void trantor_md5_init(MD5_CTX *ctx) ctx->state[3] = 0x10325476; } -void trantor_md5_update(MD5_CTX *ctx, const BYTE data[], size_t len) +void trantor_md5_update(MD5_CTX *ctx, const uint8_t data[], size_t len) { size_t i; @@ -162,7 +162,7 @@ void trantor_md5_update(MD5_CTX *ctx, const BYTE data[], size_t len) } } -void trantor_md5_final(MD5_CTX *ctx, BYTE hash[]) +void trantor_md5_final(MD5_CTX *ctx, uint8_t hash[]) { size_t i; @@ -186,14 +186,14 @@ void trantor_md5_final(MD5_CTX *ctx, BYTE hash[]) // Append to the padding the total message's length in bits and transform. ctx->bitlen += ctx->datalen * 8; - ctx->data[56] = ctx->bitlen; - ctx->data[57] = ctx->bitlen >> 8; - ctx->data[58] = ctx->bitlen >> 16; - ctx->data[59] = ctx->bitlen >> 24; - ctx->data[60] = ctx->bitlen >> 32; - ctx->data[61] = ctx->bitlen >> 40; - ctx->data[62] = ctx->bitlen >> 48; - ctx->data[63] = ctx->bitlen >> 56; + ctx->data[56] = (uint8_t)ctx->bitlen; + ctx->data[57] = (uint8_t)(ctx->bitlen >> 8); + ctx->data[58] = (uint8_t)(ctx->bitlen >> 16); + ctx->data[59] = (uint8_t)(ctx->bitlen >> 24); + ctx->data[60] = (uint8_t)(ctx->bitlen >> 32); + ctx->data[61] = (uint8_t)(ctx->bitlen >> 40); + ctx->data[62] = (uint8_t)(ctx->bitlen >> 48); + ctx->data[63] = (uint8_t)(ctx->bitlen >> 56); trantor_md5_transform(ctx, ctx->data); // Since this implementation uses little endian byte ordering and MD uses diff --git a/trantor/utils/crypto/md5.h b/trantor/utils/crypto/md5.h index a522ce72..0979a75c 100644 --- a/trantor/utils/crypto/md5.h +++ b/trantor/utils/crypto/md5.h @@ -11,30 +11,22 @@ /*************************** HEADER FILES ***************************/ #include +#include /****************************** MACROS ******************************/ -#define MD5_BLOCK_SIZE 16 // MD5 outputs a 16 byte digest - -/**************************** DATA TYPES ****************************/ - -#ifndef _WIN32 -typedef unsigned char BYTE; // 8-bit byte -typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines -#else -#include -#endif +#define MD5_BLOCK_SIZE 32 // MD5 outputs a 32 byte digest typedef struct { - BYTE data[64]; - WORD datalen; - unsigned long long bitlen; - WORD state[4]; + uint8_t data[64]; + uint32_t datalen; + uint64_t bitlen; + uint32_t state[4]; } MD5_CTX; /*********************** FUNCTION DECLARATIONS **********************/ void trantor_md5_init(MD5_CTX *ctx); -void trantor_md5_update(MD5_CTX *ctx, const BYTE data[], size_t len); -void trantor_md5_final(MD5_CTX *ctx, BYTE hash[]); +void trantor_md5_update(MD5_CTX *ctx, const uint8_t data[], size_t len); +void trantor_md5_final(MD5_CTX *ctx, uint8_t hash[]); #endif // MD5_H \ No newline at end of file diff --git a/trantor/utils/crypto/sha1.cc b/trantor/utils/crypto/sha1.cc index faf1650c..7ddb886f 100644 --- a/trantor/utils/crypto/sha1.cc +++ b/trantor/utils/crypto/sha1.cc @@ -21,7 +21,6 @@ A million repetitions of "a" #include #include -#include /* for u_int*_t */ #if defined(__sun) #include "solarisfixes.h" #endif @@ -41,7 +40,9 @@ A million repetitions of "a" #if defined(vax) || defined(ns32000) || defined(sun386) || \ defined(__i386__) || defined(MIPSEL) || defined(_MIPSEL) || \ defined(BIT_ZERO_ON_RIGHT) || defined(__alpha__) || defined(__alpha) || \ - defined(__CYGWIN32__) || defined(_WIN64) || defined(_WIN32) + defined(__CYGWIN32__) || defined(_WIN64) || defined(_WIN32) || \ + defined(__arm64e__) || defined(__arm64__) || defined(__aarch64__) || \ + defined(__riscv) || defined(_M_ARM64) #define BYTE_ORDER LITTLE_ENDIAN #endif @@ -51,7 +52,7 @@ A million repetitions of "a" defined(apollo) || defined(__convex__) || defined(_CRAY) || \ defined(__hppa) || defined(__hp9000) || defined(__hp9000s300) || \ defined(__hp9000s700) || defined(BIT_ZERO_ON_LEFT) || defined(m68k) || \ - defined(__sparc) + defined(__sparc) || defined(__s390__) || defined(__ppc__) #define BYTE_ORDER BIG_ENDIAN #endif #endif /* linux */ @@ -114,17 +115,13 @@ A million repetitions of "a" /* Hash a single 512-bit block. This is the core of the algorithm. */ -#ifdef _WIN32 -using u_int32_t = uint32_t; -#endif - -void TrantorSHA1Transform(u_int32_t state[5], const unsigned char buffer[64]) +void trantor_sha1_transform(uint32_t state[5], const unsigned char buffer[64]) { - u_int32_t a, b, c, d, e; + uint32_t a, b, c, d, e; typedef union { unsigned char c[64]; - u_int32_t l[16]; + uint32_t l[16]; } CHAR64LONG16; #ifdef SHA1HANDSOFF CHAR64LONG16 block[1]; /* use array to appear as a pointer */ @@ -237,9 +234,9 @@ void TrantorSHA1Transform(u_int32_t state[5], const unsigned char buffer[64]) #endif } -/* TrantorSHA1Init - Initialize new context */ +/* trantor_sha1_init - Initialize new context */ -void TrantorSHA1Init(SHA1_CTX* context) +void trantor_sha1_init(SHA1_CTX* context) { /* SHA1 initialization constants */ context->state[0] = 0x67452301; @@ -252,7 +249,9 @@ void TrantorSHA1Init(SHA1_CTX* context) /* Run your data through this. */ -void TrantorSHA1Update(SHA1_CTX* context, const unsigned char* data, size_t len) +void trantor_sha1_update(SHA1_CTX* context, + const unsigned char* data, + size_t len) { size_t i; size_t j; @@ -265,10 +264,10 @@ void TrantorSHA1Update(SHA1_CTX* context, const unsigned char* data, size_t len) if ((j + len) > 63) { memcpy(&context->buffer[j], data, (i = 64 - j)); - TrantorSHA1Transform(context->state, context->buffer); + trantor_sha1_transform(context->state, context->buffer); for (; i + 63 < len; i += 64) { - TrantorSHA1Transform(context->state, &data[i]); + trantor_sha1_transform(context->state, &data[i]); } j = 0; } @@ -279,7 +278,7 @@ void TrantorSHA1Update(SHA1_CTX* context, const unsigned char* data, size_t len) /* Add padding and return the message digest. */ -void TrantorSHA1Final(unsigned char digest[20], SHA1_CTX* context) +void trantor_sha1_final(unsigned char digest[20], SHA1_CTX* context) { unsigned i; unsigned char finalcount[8]; @@ -295,7 +294,7 @@ void TrantorSHA1Final(unsigned char digest[20], SHA1_CTX* context) for (i = 0; i < 2; i++) { - u_int32_t t = context->count[i]; + uint32_t t = context->count[i]; int j; for (j = 0; j < 4; t >>= 8, j++) @@ -310,15 +309,15 @@ void TrantorSHA1Final(unsigned char digest[20], SHA1_CTX* context) } #endif c = 0200; - TrantorSHA1Update(context, &c, 1); + trantor_sha1_update(context, &c, 1); while ((context->count[0] & 504) != 448) { c = 0000; - TrantorSHA1Update(context, &c, 1); + trantor_sha1_update(context, &c, 1); } - TrantorSHA1Update(context, - finalcount, - 8); /* Should cause a TrantorSHA1Transform() */ + trantor_sha1_update(context, + finalcount, + 8); /* Should cause a TrantorSHA1Transform() */ for (i = 0; i < 20; i++) { digest[i] = diff --git a/trantor/utils/crypto/sha1.h b/trantor/utils/crypto/sha1.h index f0ea6a12..f7344cb5 100644 --- a/trantor/utils/crypto/sha1.h +++ b/trantor/utils/crypto/sha1.h @@ -18,9 +18,9 @@ typedef struct unsigned char buffer[64]; } SHA1_CTX; -void TrantorSHA1Transform(uint32_t state[5], const unsigned char buffer[64]); -void TrantorSHA1Init(SHA1_CTX* context); -void TrantorSHA1Update(SHA1_CTX* context, - const unsigned char* data, - size_t len); -void TrantorSHA1Final(unsigned char digest[20], SHA1_CTX* context); +void trantor_sha1_transform(uint32_t state[5], const unsigned char buffer[64]); +void trantor_sha1_init(SHA1_CTX* context); +void trantor_sha1_update(SHA1_CTX* context, + const unsigned char* data, + size_t len); +void trantor_sha1_final(unsigned char digest[20], SHA1_CTX* context); diff --git a/trantor/utils/crypto/sha256.cc b/trantor/utils/crypto/sha256.cc index 7fdd9940..b5f36515 100644 --- a/trantor/utils/crypto/sha256.cc +++ b/trantor/utils/crypto/sha256.cc @@ -29,7 +29,7 @@ #define SIG1(x) (ROTRIGHT(x, 17) ^ ROTRIGHT(x, 19) ^ ((x) >> 10)) /**************************** VARIABLES *****************************/ -static const unsigned int k[64] = { +static const uint32_t k[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, @@ -43,9 +43,9 @@ static const unsigned int k[64] = { 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; /*********************** FUNCTION DEFINITIONS ***********************/ -void trantor_sha256_transform(SHA256_CTX *ctx, const BYTE data[]) +void trantor_sha256_transform(SHA256_CTX *ctx, const uint8_t data[]) { - WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; + uint32_t a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; for (i = 0, j = 0; i < 16; ++i, j += 4) m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | @@ -100,9 +100,9 @@ void trantor_sha256_init(SHA256_CTX *ctx) ctx->state[7] = 0x5be0cd19; } -void trantor_sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len) +void trantor_sha256_update(SHA256_CTX *ctx, const uint8_t data[], size_t len) { - WORD i; + uint32_t i; for (i = 0; i < len; ++i) { @@ -117,9 +117,9 @@ void trantor_sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len) } } -void trantor_sha256_final(SHA256_CTX *ctx, BYTE hash[]) +void trantor_sha256_final(SHA256_CTX *ctx, uint8_t hash[]) { - WORD i; + uint32_t i; i = ctx->datalen; @@ -141,14 +141,14 @@ void trantor_sha256_final(SHA256_CTX *ctx, BYTE hash[]) // Append to the padding the total message's length in bits and transform. ctx->bitlen += ctx->datalen * 8; - ctx->data[63] = ctx->bitlen; - ctx->data[62] = ctx->bitlen >> 8; - ctx->data[61] = ctx->bitlen >> 16; - ctx->data[60] = ctx->bitlen >> 24; - ctx->data[59] = ctx->bitlen >> 32; - ctx->data[58] = ctx->bitlen >> 40; - ctx->data[57] = ctx->bitlen >> 48; - ctx->data[56] = ctx->bitlen >> 56; + ctx->data[63] = (uint8_t)ctx->bitlen; + ctx->data[62] = (uint8_t)(ctx->bitlen >> 8); + ctx->data[61] = (uint8_t)(ctx->bitlen >> 16); + ctx->data[60] = (uint8_t)(ctx->bitlen >> 24); + ctx->data[59] = (uint8_t)(ctx->bitlen >> 32); + ctx->data[58] = (uint8_t)(ctx->bitlen >> 40); + ctx->data[57] = (uint8_t)(ctx->bitlen >> 48); + ctx->data[56] = (uint8_t)(ctx->bitlen >> 56); trantor_sha256_transform(ctx, ctx->data); // Since this implementation uses little endian byte ordering and SHA uses diff --git a/trantor/utils/crypto/sha256.h b/trantor/utils/crypto/sha256.h index 28a90eed..6a470faa 100644 --- a/trantor/utils/crypto/sha256.h +++ b/trantor/utils/crypto/sha256.h @@ -10,33 +10,22 @@ #define SHA256_H /*************************** HEADER FILES ***************************/ -#ifndef _WIN32 -#include -#else -#include -#endif +#include /****************************** MACROS ******************************/ #define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest -/**************************** DATA TYPES ****************************/ - -#ifndef _WIN32 -typedef unsigned char BYTE; // 8-bit byte -typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines -#endif - typedef struct { - BYTE data[64]; - WORD datalen; - unsigned long long bitlen; - WORD state[8]; + uint8_t data[64]; + uint32_t datalen; + uint64_t bitlen; + uint32_t state[8]; } SHA256_CTX; /*********************** FUNCTION DECLARATIONS **********************/ void trantor_sha256_init(SHA256_CTX *ctx); -void trantor_sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len); -void trantor_sha256_final(SHA256_CTX *ctx, BYTE hash[]); +void trantor_sha256_update(SHA256_CTX *ctx, const uint8_t data[], size_t len); +void trantor_sha256_final(SHA256_CTX *ctx, uint8_t hash[]); #endif // SHA256_H \ No newline at end of file diff --git a/trantor/utils/crypto/sha3.cc b/trantor/utils/crypto/sha3.cc index 4cd0659a..dcb27b95 100644 --- a/trantor/utils/crypto/sha3.cc +++ b/trantor/utils/crypto/sha3.cc @@ -33,7 +33,7 @@ void trantor_sha3_keccakf(uint64_t st[25]) #if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ uint8_t *v; - // endianess conversion. this is redundant on little-endian targets + // endianness conversion. this is redundant on little-endian targets for (i = 0; i < 25; i++) { v = (uint8_t *)&st[i]; @@ -82,7 +82,7 @@ void trantor_sha3_keccakf(uint64_t st[25]) } #if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ - // endianess conversion. this is redundant on little-endian targets + // endianness conversion. this is redundant on little-endian targets for (i = 0; i < 25; i++) { v = (uint8_t *)&st[i]; @@ -193,4 +193,4 @@ void trantor_shake_out(sha3_ctx_t *c, void *out, size_t len) ((uint8_t *)out)[i] = c->st.b[j++]; } c->pt = j; -} \ No newline at end of file +} diff --git a/trantor/utils/crypto/sha3.h b/trantor/utils/crypto/sha3.h index b3c89aff..a96f1122 100644 --- a/trantor/utils/crypto/sha3.h +++ b/trantor/utils/crypto/sha3.h @@ -27,9 +27,9 @@ typedef struct } sha3_ctx_t; // Compression function. -void trnator_sha3_keccakf(uint64_t st[25]); +void trantor_sha3_keccakf(uint64_t st[25]); -// OpenSSL - like interfece +// OpenSSL - like interface int trantor_sha3_init(sha3_ctx_t *c, int mdlen); // mdlen = hash output in bytes int trantor_sha3_update(sha3_ctx_t *c, const void *data, size_t len); @@ -39,11 +39,11 @@ int trantor_sha3_final(void *md, sha3_ctx_t *c); // digest goes to md void *trantor_sha3(const void *in, size_t inlen, void *md, int mdlen); // SHAKE128 and SHAKE256 extensible-output functions -#define trantor_shake128_init(c) trnator_sha3_init(c, 16) -#define trantor_shake256_init(c) trnator_sha3_init(c, 32) -#define trantor_shake_update trnator_sha3_update +#define trantor_shake128_init(c) trantor_sha3_init(c, 16) +#define trantor_shake256_init(c) trantor_sha3_init(c, 32) +#define trantor_shake_update trantor_sha3_update -void trnator_shake_xof(sha3_ctx_t *c); -void trnator_shake_out(sha3_ctx_t *c, void *out, size_t len); +void trantor_shake_xof(sha3_ctx_t *c); +void trantor_shake_out(sha3_ctx_t *c, void *out, size_t len); #endif