This is an automated email from the ASF dual-hosted git repository.
HTHou pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/master by this push:
new 280c0189aa5 Support standard TLS and mTLS in the C++ client (#18601)
280c0189aa5 is described below
commit 280c0189aa505e0d78967aa4c2b0da6ef4566a30
Author: Hongzhi Gao <[email protected]>
AuthorDate: Tue Sep 15 09:43:32 2026 +0800
Support standard TLS and mTLS in the C++ client (#18601)
---
.github/workflows/client-cpp-package.yml | 38 ++--
.github/workflows/multi-language-client.yml | 7 +-
iotdb-client/client-cpp/CMakeLists.txt | 14 ++
iotdb-client/client-cpp/README.md | 53 +++---
iotdb-client/client-cpp/README_zh.md | 12 +-
iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake | 201 ++++++++++++++++-----
.../client-cpp/cmake/InstallOpenSSLRuntime.cmake | 4 +-
iotdb-client/client-cpp/pom.xml | 62 ++-----
.../package-metadata/third_party/DEPENDENCIES.md | 2 +-
.../src/include/AbstractSessionBuilder.h | 15 +-
iotdb-client/client-cpp/src/include/Session.h | 3 +
.../client-cpp/src/include/SessionBuilder.h | 14 +-
iotdb-client/client-cpp/src/include/SessionC.h | 15 ++
iotdb-client/client-cpp/src/include/SessionPool.h | 21 ++-
.../src/include/{TableSession.h => SslConfig.h} | 32 +---
iotdb-client/client-cpp/src/include/TableSession.h | 3 +-
.../client-cpp/src/include/TableSessionBuilder.h | 14 +-
iotdb-client/client-cpp/src/rpc/NodesSupplier.cpp | 33 ++--
iotdb-client/client-cpp/src/rpc/NodesSupplier.h | 13 +-
.../TableSession.h => rpc/RpcSslUtils.cpp} | 36 ++--
.../{include/TableSession.h => rpc/RpcSslUtils.h} | 33 ++--
.../client-cpp/src/rpc/SessionConnection.cpp | 13 +-
.../client-cpp/src/rpc/SessionConnection.h | 3 +-
iotdb-client/client-cpp/src/rpc/SessionImpl.h | 3 +-
.../client-cpp/src/rpc/ThriftConnection.cpp | 11 +-
iotdb-client/client-cpp/src/rpc/ThriftConnection.h | 6 +-
iotdb-client/client-cpp/src/session/Session.cpp | 27 +--
iotdb-client/client-cpp/src/session/SessionC.cpp | 69 +++++++
.../client-cpp/src/session/SessionPool.cpp | 17 +-
.../TableSession.h => session/SslConfig.cpp} | 37 ++--
.../client-cpp/src/session/TableSession.cpp | 5 +-
iotdb-client/client-cpp/test/CMakeLists.txt | 36 +++-
.../client-cpp/test/cpp/RpcSslIotdbE2eTest.cpp | 194 ++++++++++++++++++++
iotdb-client/client-cpp/test/cpp/sessionIT.cpp | 3 +-
.../client-cpp/test/cpp/sessionUtilsTest.cpp | 18 +-
iotdb-client/client-cpp/test/fixtures/tls/ca.crt | 19 ++
.../client-cpp/test/fixtures/tls/client.crt | 17 ++
.../client-cpp/test/fixtures/tls/client.key | 28 +++
.../client-cpp/test/fixtures/tls/tls-server.p12 | Bin 0 -> 2608 bytes
.../TableSession.h => test/main_rpc_ssl.cpp} | 29 +--
.../test/scripts/configure_iotdb_ssl_it.py | 117 ++++++++++++
.../client-cpp/test/scripts/run_cpp_it_phases.py | 130 +++++++++++++
iotdb-client/client-cpp/third-party/README.md | 6 +-
pom.xml | 4 +
scripts/sbin/windows/start-confignode.bat | 4 +-
scripts/sbin/windows/start-datanode.bat | 4 +-
scripts/sbin/windows/start-standalone.bat | 7 +-
47 files changed, 1089 insertions(+), 343 deletions(-)
diff --git a/.github/workflows/client-cpp-package.yml
b/.github/workflows/client-cpp-package.yml
index 38eac3fbcbc..70f2bffdddd 100644
--- a/.github/workflows/client-cpp-package.yml
+++ b/.github/workflows/client-cpp-package.yml
@@ -309,14 +309,10 @@ jobs:
shell: bash
run: |
set -euxo pipefail
- # Pin openssl@3 (Apache-2.0): the default 'openssl' formula will
move to
- # OpenSSL 4.0, which drops the legacy TLS-method APIs Thrift still
uses.
- brew install boost openssl@3 llvm@17 bison
+ brew install boost llvm@17 bison
ln -sf "$(brew --prefix llvm@17)/bin/clang-format" "$(brew
--prefix)/bin/clang-format"
echo "$(brew --prefix bison)/bin" >> "$GITHUB_PATH"
echo "$(brew --prefix llvm@17)/bin" >> "$GITHUB_PATH"
- # Homebrew OpenSSL is keg-only, so point find_package(OpenSSL) at it.
- echo "OPENSSL_ROOT_DIR=$(brew --prefix openssl@3)" >> "$GITHUB_ENV"
clang-format --version
bison --version
- name: Cache Maven packages
@@ -373,6 +369,22 @@ jobs:
unzip -q -o "${PKG_TARBALL}" -d "${PKG_UNPACK}"
PKG_ROOT=$(find "${PKG_UNPACK}" -mindepth 1 -maxdepth 1 -type d
-name 'iotdb-session-cpp-*' -print -quit)
test -n "${PKG_ROOT}"
+
OPENSSL_BUILD_INSTALL="${GITHUB_WORKSPACE}/iotdb-client/client-cpp/target/build/_deps/openssl/install"
+ test -d "${OPENSSL_BUILD_INSTALL}"
+ rm -rf "${RUNNER_TEMP}/openssl-build-install-hidden"
+ mv "${OPENSSL_BUILD_INSTALL}"
"${RUNNER_TEMP}/openssl-build-install-hidden"
+ SSL_DYLIB=$(find "${PKG_ROOT}/lib" -type f -name 'libssl*.dylib'
-print -quit)
+ CRYPTO_DYLIB=$(find "${PKG_ROOT}/lib" -type f -name
'libcrypto*.dylib' -print -quit)
+ test -n "${SSL_DYLIB}"
+ test -n "${CRYPTO_DYLIB}"
+ otool -D "${SSL_DYLIB}" | grep -F "@rpath/$(basename "${SSL_DYLIB}")"
+ otool -D "${CRYPTO_DYLIB}" | grep -F "@rpath/$(basename
"${CRYPTO_DYLIB}")"
+ otool -L "${SSL_DYLIB}" | grep -F "@rpath/$(basename
"${CRYPTO_DYLIB}")"
+ if otool -L "${PKG_ROOT}/lib/libiotdb_session.dylib" "${SSL_DYLIB}"
"${CRYPTO_DYLIB}" \
+ | grep -F "${GITHUB_WORKSPACE}"; then
+ echo "Packaged dylibs still reference the build workspace"
+ exit 1
+ fi
EXAMPLE_BUILD="${RUNNER_TEMP}/client-cpp-example-smoke"
cmake -S "${PKG_ROOT}/examples" -B "${EXAMPLE_BUILD}" \
-DCMAKE_BUILD_TYPE=Release \
@@ -419,7 +431,8 @@ jobs:
- name: Install C++ dependencies (Windows)
shell: pwsh
run: |
- choco install winflexbison3 -y --no-progress
+ choco install winflexbison3 strawberryperl -y --no-progress
+ echo 'C:\Strawberry\perl\bin' >> $env:GITHUB_PATH
$boostArgs = @('install', '${{ matrix.boost_choco }}', '-y',
'--no-progress')
if ('${{ matrix.boost_choco_version }}' -ne '') {
$boostArgs += @("--version=${{ matrix.boost_choco_version }}")
@@ -433,18 +446,6 @@ jobs:
throw "Boost not found under C:\local after installing ${{
matrix.boost_choco }}"
}
echo $boostDir.FullName >> $env:GITHUB_PATH
- # Use a pinned OpenSSL 3.x (Apache-2.0). 'choco install openssl' now
- # installs OpenSSL 4.0, which removed the legacy TLS-method APIs that
- # Apache Thrift's TSSLSocket still calls. The FireDaemon zip is a
clean
- # prebuilt OpenSSL 3.5.x that keeps them.
- $sslZip = "$env:RUNNER_TEMP\openssl-3.5.3.zip"
- $sslDir = "$env:RUNNER_TEMP\openssl-3"
- curl.exe -L --fail --retry 3 -o $sslZip
'https://download.firedaemon.com/FireDaemon-OpenSSL/openssl-3.5.3.zip'
- Expand-Archive -Path $sslZip -DestinationPath $sslDir -Force
- $sslPath = (Get-ChildItem $sslDir -Recurse -Directory -Filter 'x64'
| Select-Object -First 1).FullName
- if (-not $sslPath) { throw "OpenSSL x64 dir not found under $sslDir"
}
- echo "$sslPath\bin" >> $env:GITHUB_PATH
- echo "OPENSSL_ROOT_DIR=$sslPath" >> $env:GITHUB_ENV
- name: Cache Maven packages
uses: actions/cache@v5
with:
@@ -536,6 +537,7 @@ jobs:
fi
cmake "${CMAKE_ARGS[@]}"
cmake --build "${EXAMPLE_BUILD}" --config Release
+ export PATH="${PKG_ROOT}/lib:${PATH}"
./mvnw -pl distribution -am -DskipTests -Dspotless.skip=true package
if [ "${RUNNER_OS}" = "Windows" ]; then
WORKSPACE_BASE="$(cygpath -u "${GITHUB_WORKSPACE}")"
diff --git a/.github/workflows/multi-language-client.yml
b/.github/workflows/multi-language-client.yml
index 5437a654985..61a947c3e08 100644
--- a/.github/workflows/multi-language-client.yml
+++ b/.github/workflows/multi-language-client.yml
@@ -198,9 +198,12 @@ jobs:
# (was causing problems on windows, but could cause problem on linux,
when updating the thrift module)
run: |
if [[ "${{ matrix.os }}" == "windows-2025-vs2026" ]]; then
- ./mvnw clean verify -P with-cpp -pl iotdb-client/client-cpp -am
-Dcmake.generator="Visual Studio 18 2026"
- else
+ ./mvnw clean verify -P with-cpp -pl iotdb-client/client-cpp -am
-Diotdb.openssl.from.source=OFF -Dcmake.generator="Visual Studio 18 2026"
+ elif [[ "${{ runner.os }}" == "macOS" ]]; then
+ # Exercise the default source build, including bundled dylib
relocation, on pull requests.
./mvnw clean verify -P with-cpp -pl iotdb-client/client-cpp -am
+ else
+ ./mvnw clean verify -P with-cpp -pl iotdb-client/client-cpp -am
-Diotdb.openssl.from.source=OFF
fi
- name: Upload Artifact
if: failure()
diff --git a/iotdb-client/client-cpp/CMakeLists.txt
b/iotdb-client/client-cpp/CMakeLists.txt
index 084396bc9b5..f6178d183d8 100644
--- a/iotdb-client/client-cpp/CMakeLists.txt
+++ b/iotdb-client/client-cpp/CMakeLists.txt
@@ -217,6 +217,7 @@ include(GNUInstallDirs)
set(IOTDB_PUBLIC_HEADERS
Export.h
SessionConfig.h
+ SslConfig.h
Session.h
Common.h
Optional.h
@@ -284,6 +285,17 @@ set(IOTDB_SESSION_CI_BUILD_ID "$ENV{GITHUB_RUN_ID}")
if(NOT IOTDB_SESSION_CI_BUILD_ID)
set(IOTDB_SESSION_CI_BUILD_ID "local")
endif()
+if(WITH_SSL)
+ set(IOTDB_SESSION_OPENSSL_VERSION "${OPENSSL_VERSION}")
+ if(IOTDB_OPENSSL_FROM_SOURCE)
+ set(IOTDB_SESSION_OPENSSL_PROVIDER "pinned-source")
+ else()
+ set(IOTDB_SESSION_OPENSSL_PROVIDER "system")
+ endif()
+else()
+ set(IOTDB_SESSION_OPENSSL_VERSION "disabled")
+ set(IOTDB_SESSION_OPENSSL_PROVIDER "disabled")
+endif()
file(WRITE "${CMAKE_BINARY_DIR}/package-metadata/VERSION"
"${IOTDB_SESSION_VERSION}\n")
file(WRITE "${CMAKE_BINARY_DIR}/package-metadata/BUILD-INFO.txt"
@@ -296,6 +308,8 @@ file(WRITE
"${CMAKE_BINARY_DIR}/package-metadata/BUILD-INFO.txt"
"cmake.generator=${CMAKE_GENERATOR}\n"
"cmake.build.type=${CMAKE_BUILD_TYPE}\n"
"with.ssl=${WITH_SSL}\n"
+ "openssl.version=${IOTDB_SESSION_OPENSSL_VERSION}\n"
+ "openssl.provider=${IOTDB_SESSION_OPENSSL_PROVIDER}\n"
"iotdb.offline=${IOTDB_OFFLINE}\n"
"iotdb.use.cxx11.abi=${IOTDB_USE_CXX11_ABI}\n"
"iotdb.extra.cxx.flags=${IOTDB_EXTRA_CXX_FLAGS}\n")
diff --git a/iotdb-client/client-cpp/README.md
b/iotdb-client/client-cpp/README.md
index 852c38d447a..d4fdb8de2ef 100644
--- a/iotdb-client/client-cpp/README.md
+++ b/iotdb-client/client-cpp/README.md
@@ -386,7 +386,8 @@ etc. directly.
| `BOOST_VERSION` | `1.60.0` (`1.84.0` on macOS) | Boost version
that CMake will look for / download.
|
| `THRIFT_VERSION` | `0.24.0` | Apache Thrift
version to build from source.
|
| `BOOST_ROOT` | (unset) | Existing Boost
install to reuse, equivalent to `-Dboost.include.dir=...` from the legacy
build. |
-| `OPENSSL_ROOT_DIR` | (unset) | Existing OpenSSL
install when `WITH_SSL=ON`.
|
+| `IOTDB_OPENSSL_FROM_SOURCE` | `ON` | Build the
checksum-pinned OpenSSL source release; set `OFF` to opt into system OpenSSL.
|
+| `OPENSSL_ROOT_DIR` | (unset) | Existing OpenSSL
install used only when `IOTDB_OPENSSL_FROM_SOURCE=OFF`.
|
| `CMAKE_INSTALL_PREFIX`| `<build>/install` | Install location.
|
| `CMAKE_BUILD_TYPE` | `Release` | Single-config
generator build type. Use `Debug` to produce a debug library.
|
@@ -427,9 +428,9 @@ cmake --build build --config Release --target install
| Platform | Required files
|
|------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
- | `linux/` | `thrift-0.24.0.tar.gz`, `boost_1_60_0.tar.gz`,
`m4-1.4.19.tar.gz`, `flex-2.6.4.tar.gz`, `bison-3.8.tar.gz` (and
`openssl-3.5.0.tar.gz` only when `WITH_SSL=ON` and no system OpenSSL is
present) |
- | `mac/` | `thrift-0.24.0.tar.gz`, `boost_1_84_0.tar.gz` (newer Boost
for Xcode/Clang; Apple ships m4/flex/bison; `openssl-3.5.0.tar.gz` optional)
|
- | `windows/` | `thrift-0.24.0.tar.gz`, `boost_1_60_0.tar.gz` (Boost headers
only - no `b2` build required for `iotdb_session`)
|
+ | `linux/` | `thrift-0.24.0.tar.gz`, `boost_1_60_0.tar.gz`,
`m4-1.4.19.tar.gz`, `flex-2.6.4.tar.gz`, `bison-3.8.tar.gz`,
`openssl-3.5.8.tar.gz` |
+ | `mac/` | `thrift-0.24.0.tar.gz`, `boost_1_84_0.tar.gz`,
`openssl-3.5.8.tar.gz` |
+ | `windows/` | `thrift-0.24.0.tar.gz`, `boost_1_60_0.tar.gz`,
`openssl-3.5.8.tar.gz` |
Reference URLs (the configure step uses the same):
- Apache Thrift 0.24.0:
<https://archive.apache.org/dist/thrift/0.24.0/thrift-0.24.0.tar.gz>
@@ -437,7 +438,7 @@ cmake --build build --config Release --target install
- GNU m4 1.4.19: <https://ftp.gnu.org/gnu/m4/m4-1.4.19.tar.gz>
- GNU flex 2.6.4:
<https://github.com/westes/flex/releases/download/v2.6.4/flex-2.6.4.tar.gz>
- GNU bison 3.8: <https://ftp.gnu.org/gnu/bison/bison-3.8.tar.gz>
- - OpenSSL 3.5.0: <https://www.openssl.org/source/openssl-3.5.0.tar.gz>
+ - OpenSSL 3.5.8:
<https://github.com/openssl/openssl/releases/download/openssl-3.5.8/openssl-3.5.8.tar.gz>
2. Run the build with offline mode enabled:
@@ -492,11 +493,9 @@ Prerequisites:
2. **flex / bison.** Install <https://sourceforge.net/projects/winflexbison/>
and rename `win_flex.exe`→`flex.exe`, `win_bison.exe`→`bison.exe` on
`PATH`.
-3. **OpenSSL** *(`WITH_SSL=ON` is the default)*: install OpenSSL — e.g.
- `choco install openssl`, or a Win64 OpenSSL installer from
- <https://slproweb.com/products/Win32OpenSSL.html> — then pass
- `-DOPENSSL_ROOT_DIR=...` to CMake if it is not auto-detected. Pass
- `-DWITH_SSL=OFF` to build without SSL.
+3. **Perl** *(`WITH_SSL=ON` is the default)*: install Strawberry Perl so the
+ pinned OpenSSL source release can be built (`choco install strawberryperl`).
+ Pass `-DWITH_SSL=OFF` to build without SSL.
On Windows the SDK ships as **`iotdb_session.dll`** plus an import library
**`iotdb_session.lib`**, built with **`/MD`** (dynamic CRT, same as a
@@ -516,20 +515,30 @@ OpenSSL **3.x** is used (Apache-2.0 licensed). Note that
**OpenSSL 4.0 removed**
the legacy TLS-method APIs (`TLSv1_method`, `SSLv3_method`, …) that Apache
Thrift's `TSSLSocket` still calls, so install/point at a 3.x build, not 4.0.
-CMake calls `find_package(OpenSSL)` and uses the system OpenSSL it finds. Its
-shared libraries are **bundled into the package `lib/` directory** (next to
-`iotdb_session`, which records an `$ORIGIN`/`@loader_path` runtime path) so the
-published SDK is self-contained.
+By default CMake downloads OpenSSL 3.5.8, verifies its SHA-256 checksum, and
+builds shared libraries from source on Linux, macOS, and Windows. The runtime
+libraries are bundled into the package `lib/` directory so the published SDK is
+self-contained. Set `-DIOTDB_OPENSSL_FROM_SOURCE=OFF` to opt into a compatible
+system OpenSSL 3.x instead.
-Fallbacks:
+Enable authenticated TLS by configuring a PEM CA certificate. Add a PEM client
+certificate chain and an unencrypted PEM private key for mutual TLS:
-- **Linux / macOS** – when no system OpenSSL is found (or
- `-DIOTDB_OPENSSL_FROM_SOURCE=ON`, which the Linux packaging build uses so the
- AlmaLinux 8 baseline's OpenSSL 1.1.1 is never redistributed), build
- `openssl-3.5.0.tar.gz` from source as **shared** libraries and bundle them.
-- **Windows** – fail with a friendly message; install a prebuilt OpenSSL 3.x
- (e.g. the FireDaemon or slproweb 3.5.x zip) and set `-DOPENSSL_ROOT_DIR=...`.
- Building OpenSSL from source via MSVC is out of scope.
+```cpp
+auto session = SessionBuilder()
+ .host("127.0.0.1")
+ ->rpcPort(6667)
+ ->useSSL(true)
+ ->trustCertFilePath("ca.crt")
+ ->clientCertificateFilePath("client.crt")
+ ->clientPrivateKeyFilePath("client.key")
+ ->build();
+```
+
+The client certificate and private key must either both be configured or both
+be omitted. The same methods are available on `TableSessionBuilder` and
+`SessionPoolBuilder`; the C API provides `ts_session_set_ssl_config()` and
+`ts_table_session_new_with_ssl()`.
## Tests
diff --git a/iotdb-client/client-cpp/README_zh.md
b/iotdb-client/client-cpp/README_zh.md
index 7cd060c7d7c..f2c591c2b2c 100644
--- a/iotdb-client/client-cpp/README_zh.md
+++ b/iotdb-client/client-cpp/README_zh.md
@@ -243,11 +243,13 @@ Maven 构建会把 SDK 安装到 `target/install/`,并生成
| `BOOST_INCLUDEDIR` | `boost.include.dir` |
| `CMAKE_BUILD_TYPE` | `cmake.build.type`,例如 `-Dcmake.build.type=Debug` |
-SSL 默认开启(`WITH_SSL=ON`)。所捆绑的 Apache Thrift 0.24 同时支持 OpenSSL 1.x
-与 3.x,因此直接使用系统的 OpenSSL(任意版本)。CMake 通过 `find_package(OpenSSL)`
-解析系统 OpenSSL,找不到时回退到从源码构建 OpenSSL 3.5.0;并会把所用的 OpenSSL
-动态库一并复制到产物 `lib/` 目录。Windows 可用 `choco install openssl` 安装。
-直接使用 CMake 时传入 `-DWITH_SSL=OFF`、`-DIOTDB_OFFLINE=ON` 等即可。
+SSL 默认开启(`WITH_SSL=ON`)。默认构建会下载固定版本的 OpenSSL 3.5.8 源码,
+校验 SHA-256 后在 Linux、macOS 和 Windows 上编译,并将动态库复制到 SDK 的
+`lib/` 目录。设置 `-DIOTDB_OPENSSL_FROM_SOURCE=OFF` 可改用系统 OpenSSL 3.x。
+
+标准 TLS 使用 `useSSL(true)` 和 `trustCertFilePath("ca.crt")`;mTLS 再同时设置
+`clientCertificateFilePath("client.crt")` 与
+`clientPrivateKeyFilePath("client.key")`。客户端证书和未加密 PEM 私钥必须成对配置。
Debug 构建请在配置阶段传入 `-DCMAKE_BUILD_TYPE=Debug`。Windows 使用 Visual
Studio 生成器时也需要传入该选项,以便内置 Thrift 静态库使用 Debug MSVC 运行时;
随后用 `cmake --build build --config Debug --target install` 构建安装。
diff --git a/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake
b/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake
index 26c24ba6c2f..165736dd160 100644
--- a/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake
+++ b/iotdb-client/client-cpp/cmake/FetchOpenSSL.cmake
@@ -18,33 +18,29 @@
# =============================================================================
# FetchOpenSSL.cmake (only included when WITH_SSL=ON)
#
-# Apache Thrift 0.24 (bundled by this client) builds against OpenSSL 1.x and
3.x,
-# so any system OpenSSL is used as-is, whatever its version.
+# Apache Thrift 0.24 (bundled by this client) builds against OpenSSL 3.x.
#
-# Resolution order:
-# 1. find_package(OpenSSL) - any system / vendor install is taken as-is.
-# 2. On Linux/macOS, when no system OpenSSL is present:
-# use tarball
${IOTDB_OS_DEPS_DIR}/openssl-${OPENSSL_FALLBACK_VERSION}.tar.gz
-# or download from openssl.org when not in offline mode, then
-# ./config && make && make install_sw into
${CMAKE_BINARY_DIR}/_deps/openssl.
-# 3. On Windows: emit a FATAL_ERROR asking for a prebuilt OpenSSL; building
-# OpenSSL from source on MSVC is out of scope.
+# By default, a fixed OpenSSL source release is downloaded, checksum-verified,
+# built as shared libraries, and installed under ${CMAKE_BINARY_DIR}/_deps.
+# Set IOTDB_OPENSSL_FROM_SOURCE=OFF to opt into a compatible system OpenSSL.
#
# Side effects:
# Defines imported targets OpenSSL::SSL / OpenSSL::Crypto via find_package
# so callers can just link against them.
# =============================================================================
-# Version built from source when no system OpenSSL is found. Named distinctly
+# Version built from source by default. Named distinctly
# from find_package's OPENSSL_VERSION output variable to avoid collisions.
-set(OPENSSL_FALLBACK_VERSION "3.5.0"
+set(OPENSSL_FALLBACK_VERSION "3.5.8"
CACHE STRING "OpenSSL version built from source when no system OpenSSL is
found")
+set(OPENSSL_FALLBACK_SHA256
+ "a8f84a39918ec6415ce765d9b429d313ba97b8143169c172e734b9514464f5b2"
+ CACHE STRING "SHA-256 checksum of the pinned OpenSSL source archive")
-# Build OpenSSL from source even if a system one exists. Used by the Linux
-# packaging build, whose AlmaLinux 8 baseline ships OpenSSL 1.1.1 (EOL, not
-# Apache-2.0, must not be redistributed) - we build 3.x there instead.
+# Build OpenSSL from source even if a system one exists, making release
+# packages independent of the build host's OpenSSL installation.
option(IOTDB_OPENSSL_FROM_SOURCE
- "Ignore any system OpenSSL and build OpenSSL
${OPENSSL_FALLBACK_VERSION} from source" OFF)
+ "Ignore any system OpenSSL and build OpenSSL
${OPENSSL_FALLBACK_VERSION} from source" ON)
if(NOT IOTDB_OPENSSL_FROM_SOURCE)
find_package(OpenSSL QUIET)
@@ -54,27 +50,36 @@ if(NOT IOTDB_OPENSSL_FROM_SOURCE)
endif()
endif()
-if(WIN32)
- message(FATAL_ERROR
- "[OpenSSL] WITH_SSL=ON but no OpenSSL was found on Windows. "
- "Please install a prebuilt OpenSSL (e.g. 'choco install openssl'),
"
- "then re-run the configure step with
-DOPENSSL_ROOT_DIR=<install_path>. "
- "Pass -DWITH_SSL=OFF to build without SSL.")
-endif()
-
-# --- Linux / macOS: build OpenSSL ${OPENSSL_FALLBACK_VERSION} from source -
+# --- Build the pinned OpenSSL source release ---------------------------------
set(_ossl_tarname "openssl-${OPENSSL_FALLBACK_VERSION}.tar.gz")
set(_ossl_tarball "${IOTDB_OS_DEPS_DIR}/${_ossl_tarname}")
-if(NOT EXISTS "${_ossl_tarball}")
+set(_ossl_download_required ON)
+if(EXISTS "${_ossl_tarball}")
+ file(SHA256 "${_ossl_tarball}" _ossl_existing_sha256)
+ if(_ossl_existing_sha256 STREQUAL "${OPENSSL_FALLBACK_SHA256}")
+ set(_ossl_download_required OFF)
+ elseif(IOTDB_OFFLINE)
+ message(FATAL_ERROR
+ "[OpenSSL] checksum mismatch for offline archive
${_ossl_tarball}: "
+ "expected ${OPENSSL_FALLBACK_SHA256}, got
${_ossl_existing_sha256}")
+ else()
+ message(STATUS "[OpenSSL] replacing archive with an invalid checksum")
+ file(REMOVE "${_ossl_tarball}")
+ endif()
+endif()
+
+if(_ossl_download_required)
if(IOTDB_OFFLINE)
message(FATAL_ERROR
"[OpenSSL] IOTDB_OFFLINE=ON but ${_ossl_tarname} is missing in
${IOTDB_OS_DEPS_DIR}.")
endif()
- set(_ossl_url "https://www.openssl.org/source/${_ossl_tarname}")
+ set(_ossl_url
+
"https://github.com/openssl/openssl/releases/download/openssl-${OPENSSL_FALLBACK_VERSION}/${_ossl_tarname}")
message(STATUS "[OpenSSL] downloading ${_ossl_url}")
file(DOWNLOAD "${_ossl_url}" "${_ossl_tarball}"
- SHOW_PROGRESS TLS_VERIFY ON STATUS _st)
+ SHOW_PROGRESS TLS_VERIFY ON
+ EXPECTED_HASH "SHA256=${OPENSSL_FALLBACK_SHA256}" STATUS _st)
list(GET _st 0 _code)
if(NOT _code EQUAL 0)
list(GET _st 1 _msg)
@@ -94,41 +99,137 @@ if(NOT EXISTS "${_ossl_stamp}")
message(STATUS "[OpenSSL] extracting ${_ossl_tarball}")
file(ARCHIVE_EXTRACT INPUT "${_ossl_tarball}" DESTINATION
"${_ossl_root}/src")
- include(ProcessorCount)
- ProcessorCount(_jobs)
- if(_jobs LESS 1)
- set(_jobs 1)
+ message(STATUS "[OpenSSL] configuring -> ${_ossl_inst}")
+ if(WIN32)
+ # Git for Windows also ships a minimal Perl, but it lacks modules
required by OpenSSL.
+ find_program(_ossl_perl NAMES perl.exe perl
+ PATHS "C:/Strawberry/perl/bin" NO_DEFAULT_PATH)
+ if(NOT _ossl_perl)
+ find_program(_ossl_perl NAMES perl.exe perl REQUIRED)
+ endif()
+ find_program(_vswhere NAMES vswhere.exe
+ PATHS "$ENV{ProgramFiles}/Microsoft Visual Studio/Installer"
+ "C:/Program Files (x86)/Microsoft Visual
Studio/Installer")
+ if(NOT _vswhere)
+ message(FATAL_ERROR "[OpenSSL] vswhere.exe was not found")
+ endif()
+ if(CMAKE_GENERATOR MATCHES "Visual Studio ([0-9]+)")
+ set(_vs_major "${CMAKE_MATCH_1}")
+ math(EXPR _vs_next_major "${_vs_major} + 1")
+ set(_vs_range "[${_vs_major}.0,${_vs_next_major}.0)")
+ else()
+ set(_vs_range "[15.0,19.0)")
+ endif()
+ execute_process(
+ COMMAND "${_vswhere}" -latest -products * -version
"${_vs_range}"
+ -requires
Microsoft.VisualStudio.Component.VC.Tools.x86.x64
+ -property installationPath
+ OUTPUT_VARIABLE _vs_install OUTPUT_STRIP_TRAILING_WHITESPACE
+ RESULT_VARIABLE _rc)
+ if(NOT _rc EQUAL 0 OR NOT _vs_install)
+ message(FATAL_ERROR "[OpenSSL] a matching Visual Studio C++
toolchain was not found")
+ endif()
+ file(TO_NATIVE_PATH "${_vs_install}/VC/Auxiliary/Build/vcvars64.bat"
_vcvars)
+ file(TO_NATIVE_PATH "${_ossl_inst}" _ossl_inst_native)
+ file(TO_NATIVE_PATH "${_ossl_src}" _ossl_src_native)
+ file(TO_NATIVE_PATH "${_ossl_perl}" _ossl_perl_native)
+ set(_ossl_build_script "${_ossl_root}/build-openssl.cmd")
+ file(WRITE "${_ossl_build_script}"
+ "@echo on\r\n"
+ "call \"${_vcvars}\"\r\n"
+ "if errorlevel 1 exit /b %errorlevel%\r\n"
+ "cd /d \"${_ossl_src_native}\"\r\n"
+ "\"${_ossl_perl_native}\" Configure VC-WIN64A
--prefix=\"${_ossl_inst_native}\" --openssldir=\"${_ossl_inst_native}\\ssl\"
shared no-tests no-asm\r\n"
+ "if errorlevel 1 exit /b %errorlevel%\r\n"
+ "nmake\r\n"
+ "if errorlevel 1 exit /b %errorlevel%\r\n"
+ "nmake install_sw\r\n")
+ execute_process(COMMAND cmd /d /c "${_ossl_build_script}"
RESULT_VARIABLE _rc)
+ if(NOT _rc EQUAL 0)
+ message(FATAL_ERROR "[OpenSSL] Windows source build failed
(rc=${_rc})")
+ endif()
+ else()
+ include(ProcessorCount)
+ ProcessorCount(_jobs)
+ if(_jobs LESS 1)
+ set(_jobs 1)
+ endif()
+ execute_process(
+ COMMAND ./config --prefix=${_ossl_inst}
--openssldir=${_ossl_inst}/ssl shared no-tests
+ WORKING_DIRECTORY "${_ossl_src}"
+ RESULT_VARIABLE _rc)
+ if(NOT _rc EQUAL 0)
+ message(FATAL_ERROR "[OpenSSL] config failed (rc=${_rc})")
+ endif()
+ message(STATUS "[OpenSSL] building (-j${_jobs})")
+ execute_process(
+ COMMAND make -j${_jobs}
+ WORKING_DIRECTORY "${_ossl_src}"
+ RESULT_VARIABLE _rc)
+ if(NOT _rc EQUAL 0)
+ message(FATAL_ERROR "[OpenSSL] make failed (rc=${_rc})")
+ endif()
+ execute_process(
+ COMMAND make install_sw
+ WORKING_DIRECTORY "${_ossl_src}"
+ RESULT_VARIABLE _rc)
+ if(NOT _rc EQUAL 0)
+ message(FATAL_ERROR "[OpenSSL] make install_sw failed (rc=${_rc})")
+ endif()
endif()
+ file(TOUCH "${_ossl_stamp}")
+endif()
+
+if(APPLE)
+ # OpenSSL's Darwin build records its absolute installation prefix in each
dylib. Rewrite the
+ # IDs before iotdb_session links against them so both the client and
libssl resolve the bundled
+ # libraries relative to the package's lib/ directory.
+ find_program(_install_name_tool NAMES install_name_tool REQUIRED)
+ find_program(_otool NAMES otool REQUIRED)
+ find_library(_ossl_ssl_dylib NAMES ssl
+ PATHS "${_ossl_inst}/lib" "${_ossl_inst}/lib64" NO_DEFAULT_PATH)
+ find_library(_ossl_crypto_dylib NAMES crypto
+ PATHS "${_ossl_inst}/lib" "${_ossl_inst}/lib64" NO_DEFAULT_PATH)
+ if(NOT _ossl_ssl_dylib OR NOT _ossl_crypto_dylib)
+ message(FATAL_ERROR "[OpenSSL] built Darwin dylibs were not found")
+ endif()
+ get_filename_component(_ossl_ssl_real "${_ossl_ssl_dylib}" REALPATH)
+ get_filename_component(_ossl_crypto_real "${_ossl_crypto_dylib}" REALPATH)
+ get_filename_component(_ossl_ssl_name "${_ossl_ssl_real}" NAME)
+ get_filename_component(_ossl_crypto_name "${_ossl_crypto_real}" NAME)
- message(STATUS "[OpenSSL] configuring -> ${_ossl_inst}")
- # ./config auto-detects the platform target. Build SHARED libraries
- # (libssl.so.3 / libcrypto.so.3) so they can be bundled next to
- # libiotdb_session and shipped as the SDK's OpenSSL runtime.
execute_process(
- COMMAND ./config --prefix=${_ossl_inst}
--openssldir=${_ossl_inst}/ssl shared
- WORKING_DIRECTORY "${_ossl_src}"
+ COMMAND "${_install_name_tool}" -id "@rpath/${_ossl_ssl_name}"
"${_ossl_ssl_real}"
RESULT_VARIABLE _rc)
if(NOT _rc EQUAL 0)
- message(FATAL_ERROR "[OpenSSL] config failed (rc=${_rc})")
+ message(FATAL_ERROR "[OpenSSL] failed to make the libssl install name
relocatable")
endif()
-
- message(STATUS "[OpenSSL] building (-j${_jobs})")
execute_process(
- COMMAND make -j${_jobs}
- WORKING_DIRECTORY "${_ossl_src}"
+ COMMAND "${_install_name_tool}" -id "@rpath/${_ossl_crypto_name}"
"${_ossl_crypto_real}"
RESULT_VARIABLE _rc)
if(NOT _rc EQUAL 0)
- message(FATAL_ERROR "[OpenSSL] make failed (rc=${_rc})")
+ message(FATAL_ERROR "[OpenSSL] failed to make the libcrypto install
name relocatable")
endif()
- execute_process(
- COMMAND make install_sw
- WORKING_DIRECTORY "${_ossl_src}"
- RESULT_VARIABLE _rc)
+ execute_process(COMMAND "${_otool}" -L "${_ossl_ssl_real}"
+ OUTPUT_VARIABLE _ossl_ssl_dependencies RESULT_VARIABLE _rc)
if(NOT _rc EQUAL 0)
- message(FATAL_ERROR "[OpenSSL] make install_sw failed (rc=${_rc})")
+ message(FATAL_ERROR "[OpenSSL] failed to inspect libssl dependencies")
+ endif()
+ string(REGEX MATCH "[^\n\t ]*libcrypto[^\n\t ]*\\.dylib"
+ _ossl_crypto_dependency "${_ossl_ssl_dependencies}")
+ if(NOT _ossl_crypto_dependency)
+ message(FATAL_ERROR "[OpenSSL] libssl does not reference the expected
libcrypto dylib")
+ endif()
+ if(NOT _ossl_crypto_dependency STREQUAL "@rpath/${_ossl_crypto_name}")
+ execute_process(
+ COMMAND "${_install_name_tool}" -change
"${_ossl_crypto_dependency}"
+ "@rpath/${_ossl_crypto_name}" "${_ossl_ssl_real}"
+ RESULT_VARIABLE _rc)
+ if(NOT _rc EQUAL 0)
+ message(FATAL_ERROR "[OpenSSL] failed to make the libssl
dependency relocatable")
+ endif()
endif()
- file(TOUCH "${_ossl_stamp}")
endif()
set(OPENSSL_ROOT_DIR "${_ossl_inst}" CACHE PATH "OpenSSL root" FORCE)
diff --git a/iotdb-client/client-cpp/cmake/InstallOpenSSLRuntime.cmake
b/iotdb-client/client-cpp/cmake/InstallOpenSSLRuntime.cmake
index f3e181b8e8f..42e99d5a9bd 100644
--- a/iotdb-client/client-cpp/cmake/InstallOpenSSLRuntime.cmake
+++ b/iotdb-client/client-cpp/cmake/InstallOpenSSLRuntime.cmake
@@ -26,8 +26,8 @@
# OPENSSL_SSL_LIBRARY / OPENSSL_CRYPTO_LIBRARY / OPENSSL_ROOT_DIR /
# OPENSSL_VERSION_MAJOR.
#
-# When OpenSSL was linked statically (the from-source fallback uses no-shared),
-# there is nothing to bundle: those objects are already inside
libiotdb_session.
+# When a user opts into a statically linked system OpenSSL, there is nothing to
+# bundle because those objects are already inside libiotdb_session.
# =============================================================================
# Windows: find_package resolves the import .lib; the runtime DLLs live in
diff --git a/iotdb-client/client-cpp/pom.xml b/iotdb-client/client-cpp/pom.xml
index 288acfcb558..656f0349974 100644
--- a/iotdb-client/client-cpp/pom.xml
+++ b/iotdb-client/client-cpp/pom.xml
@@ -34,7 +34,7 @@
The C++ client build is now driven entirely by the top-level
CMakeLists.txt in this directory. Maven only:
1. Runs cmake configure + build (cmake-maven-plugin)
- 2. Optionally starts/stops an IoTDB server
(process-exec-maven-plugin)
+ 2. Runs plain/TLS/mTLS integration-test phases (exec-maven-plugin)
3. Packages the produced install tree (maven-assembly-plugin)
Everything else - thrift download, code generation, Boost/m4/flex/bison
@@ -50,7 +50,7 @@
<iotdb.deps.dir>${project.basedir}/third-party</iotdb.deps.dir>
<iotdb.offline>OFF</iotdb.offline>
<with.ssl>ON</with.ssl>
- <iotdb.openssl.from.source>OFF</iotdb.openssl.from.source>
+ <iotdb.openssl.from.source>ON</iotdb.openssl.from.source>
<iotdb.cxx11.abi/>
<!-- Switched to OFF by the .skipTests profile below. -->
<build.tests>ON</build.tests>
@@ -63,6 +63,7 @@
<client.cpp.package.classifier>${os.classifier}</client.cpp.package.classifier>
<client.cpp.package.name>iotdb-session-cpp-${project.version}-${client.cpp.package.classifier}</client.cpp.package.name>
<client.cpp.ci.build.id>${env.GITHUB_RUN_ID}</client.cpp.ci.build.id>
+
<iotdb.dist.root>${project.basedir}/../../distribution/target/apache-iotdb-${project.version}-all-bin/apache-iotdb-${project.version}-all-bin</iotdb.dist.root>
</properties>
<dependencies>
<dependency>
@@ -144,51 +145,30 @@
<goals>
<goal>test</goal>
</goals>
- <phase>integration-test</phase>
- <configuration>
- <config>${cmake.build.type}</config>
-
<projectDirectory>${cmake.project.dir}</projectDirectory>
- <skipTests>${maven.test.skip}</skipTests>
- </configuration>
+ <phase>none</phase>
</execution>
</executions>
</plugin>
- <!-- Start a local IoTDB server before integration-test, stop it
after. -->
<plugin>
- <groupId>com.bazaarvoice.maven.plugins</groupId>
- <artifactId>process-exec-maven-plugin</artifactId>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
- <id>start-iotdb</id>
+ <id>run-cpp-it-phases</id>
<goals>
- <goal>start</goal>
+ <goal>exec</goal>
</goals>
- <phase>pre-integration-test</phase>
- <configuration>
- <skip>${ctest.skip.tests}</skip>
- <name>iotdb-server</name>
- <waitForInterrupt>false</waitForInterrupt>
- <waitAfterLaunch>45</waitAfterLaunch>
-
<processLogFile>${cmake.project.dir}/test.log</processLogFile>
- <arguments>
-
<argument>${project.basedir}/../../distribution/target/apache-iotdb-${project.version}-all-bin/apache-iotdb-${project.version}-all-bin/sbin/${iotdb.start.script}</argument>
- </arguments>
- </configuration>
- </execution>
- <execution>
- <id>stop-iotdb</id>
- <goals>
- <goal>stop-all</goal>
- </goals>
- <phase>post-integration-test</phase>
+ <phase>integration-test</phase>
<configuration>
<skip>${ctest.skip.tests}</skip>
- <name>iotdb-server</name>
- <waitForInterrupt>false</waitForInterrupt>
- <waitAfterLaunch>5</waitAfterLaunch>
-
<processLogFile>${cmake.project.dir}/stop.log</processLogFile>
+ <executable>python</executable>
<arguments>
-
<argument>${project.basedir}/../../distribution/target/apache-iotdb-${project.version}-all-bin/apache-iotdb-${project.version}-all-bin/sbin/${iotdb.stop.script}</argument>
+
<argument>${project.basedir}/test/scripts/run_cpp_it_phases.py</argument>
+ <argument>${cmake.project.dir}</argument>
+ <argument>${iotdb.dist.root}</argument>
+
<argument>${project.basedir}/test/fixtures</argument>
+ <argument>--config</argument>
+ <argument>${cmake.build.type}</argument>
</arguments>
</configuration>
</execution>
@@ -275,8 +255,6 @@
</os>
</activation>
<properties>
- <iotdb.start.script>start-standalone.sh</iotdb.start.script>
- <iotdb.stop.script>stop-standalone.sh</iotdb.stop.script>
<os.suffix>linux</os.suffix>
<client.cpp.package.classifier>linux-x86_64-glibc2.28</client.cpp.package.classifier>
</properties>
@@ -291,8 +269,6 @@
</os>
</activation>
<properties>
- <iotdb.start.script>start-standalone.sh</iotdb.start.script>
- <iotdb.stop.script>stop-standalone.sh</iotdb.stop.script>
<os.suffix>linux</os.suffix>
<client.cpp.package.classifier>linux-aarch64-glibc2.28</client.cpp.package.classifier>
</properties>
@@ -306,8 +282,6 @@
</os>
</activation>
<properties>
- <iotdb.start.script>start-standalone.sh</iotdb.start.script>
- <iotdb.stop.script>stop-standalone.sh</iotdb.stop.script>
<os.suffix>mac</os.suffix>
<client.cpp.package.classifier>macos-x86_64</client.cpp.package.classifier>
</properties>
@@ -321,8 +295,6 @@
</os>
</activation>
<properties>
- <iotdb.start.script>start-standalone.sh</iotdb.start.script>
- <iotdb.stop.script>stop-standalone.sh</iotdb.stop.script>
<os.suffix>mac</os.suffix>
<client.cpp.package.classifier>macos-aarch64</client.cpp.package.classifier>
</properties>
@@ -335,8 +307,6 @@
</os>
</activation>
<properties>
-
<iotdb.start.script>windows/start-standalone.bat</iotdb.start.script>
-
<iotdb.stop.script>windows/stop-standalone.bat</iotdb.stop.script>
<os.suffix>win</os.suffix>
<client.cpp.package.classifier>windows-x86_64-msvc14.3</client.cpp.package.classifier>
</properties>
diff --git
a/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/DEPENDENCIES.md
b/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/DEPENDENCIES.md
index 696ce154d16..da8b7698a70 100644
---
a/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/DEPENDENCIES.md
+++
b/iotdb-client/client-cpp/src/assembly/package-metadata/third_party/DEPENDENCIES.md
@@ -33,7 +33,7 @@ the [`NOTICE`](NOTICE) file in this directory; non-Apache
license texts are unde
| --- | --- | --- | --- |
| Apache Thrift | 0.24.0 | statically linked | Apache License 2.0 |
| Boost | 1.60.0 on Linux/Windows, 1.84.0 on macOS by default | statically
linked (header-only) | Boost Software License 1.0 |
-| OpenSSL | 3.x: system OpenSSL 3.x when present, else 3.5.0 built from source
(`WITH_SSL=ON`, default) | bundled shared libs in `lib/` | Apache License 2.0 |
+| OpenSSL | 3.5.8 built from checksum-pinned source (`WITH_SSL=ON`, default) |
bundled shared libs in `lib/` | Apache License 2.0 |
## Build-time only (not redistributed)
diff --git a/iotdb-client/client-cpp/src/include/AbstractSessionBuilder.h
b/iotdb-client/client-cpp/src/include/AbstractSessionBuilder.h
index 3735dfa227d..9e865da78e8 100644
--- a/iotdb-client/client-cpp/src/include/AbstractSessionBuilder.h
+++ b/iotdb-client/client-cpp/src/include/AbstractSessionBuilder.h
@@ -21,8 +21,10 @@
#define IOTDB_ABSTRACTSESSIONBUILDER_H
#include <string>
+#include <vector>
#include "SessionConfig.h"
+#include "SslConfig.h"
class AbstractSessionBuilder {
public:
@@ -54,8 +56,19 @@ public:
bool enableRedirections = DEFAULT_ENABLE_REDIRECTIONS;
bool enableRPCCompression = DEFAULT_ENABLE_RPC_COMPRESSION;
std::vector<std::string> nodeUrls;
+ // Kept for source compatibility with callers that configure builder fields
directly.
bool useSSL = false;
std::string trustCertFilePath;
+ SslConfig sslConfig;
+
+ SslConfig getSslConfig() const {
+ SslConfig result = sslConfig;
+ result.useSsl = useSSL || result.useSsl;
+ if (result.trustCertFilePath.empty()) {
+ result.trustCertFilePath = trustCertFilePath;
+ }
+ return result;
+ }
};
-#endif // IOTDB_ABSTRACTSESSIONBUILDER_H
\ No newline at end of file
+#endif // IOTDB_ABSTRACTSESSIONBUILDER_H
diff --git a/iotdb-client/client-cpp/src/include/Session.h
b/iotdb-client/client-cpp/src/include/Session.h
index 76895a30291..054c8f6f773 100644
--- a/iotdb-client/client-cpp/src/include/Session.h
+++ b/iotdb-client/client-cpp/src/include/Session.h
@@ -582,12 +582,14 @@ private:
class SessionConnection;
class TableSession;
+class SessionTestAccessor;
class Session {
struct Impl;
std::unique_ptr<Impl> impl_;
friend class SessionConnection;
friend class TableSession;
+ friend class SessionTestAccessor;
public:
Session(const std::string& host, int rpcPort);
@@ -606,6 +608,7 @@ public:
void setSqlDialect(const std::string& dialect);
void setDatabase(const std::string& database);
+ void setSslConfig(const SslConfig& sslConfig);
std::string getDatabase();
void changeDatabase(const std::string& database);
diff --git a/iotdb-client/client-cpp/src/include/SessionBuilder.h
b/iotdb-client/client-cpp/src/include/SessionBuilder.h
index 14342697eb5..3167bd1afe5 100644
--- a/iotdb-client/client-cpp/src/include/SessionBuilder.h
+++ b/iotdb-client/client-cpp/src/include/SessionBuilder.h
@@ -36,11 +36,23 @@ public:
SessionBuilder* useSSL(bool useSSL) {
AbstractSessionBuilder::useSSL = useSSL;
+ AbstractSessionBuilder::sslConfig.useSsl = useSSL;
return this;
}
SessionBuilder* trustCertFilePath(const std::string& trustCertFilePath) {
AbstractSessionBuilder::trustCertFilePath = trustCertFilePath;
+ AbstractSessionBuilder::sslConfig.trustCertFilePath = trustCertFilePath;
+ return this;
+ }
+
+ SessionBuilder* clientCertificateFilePath(const std::string&
clientCertificateFilePath) {
+ AbstractSessionBuilder::sslConfig.clientCertificateFilePath =
clientCertificateFilePath;
+ return this;
+ }
+
+ SessionBuilder* clientPrivateKeyFilePath(const std::string&
clientPrivateKeyFilePath) {
+ AbstractSessionBuilder::sslConfig.clientPrivateKeyFilePath =
clientPrivateKeyFilePath;
return this;
}
@@ -103,4 +115,4 @@ public:
}
};
-#endif // IOTDB_SESSION_BUILDER_H
\ No newline at end of file
+#endif // IOTDB_SESSION_BUILDER_H
diff --git a/iotdb-client/client-cpp/src/include/SessionC.h
b/iotdb-client/client-cpp/src/include/SessionC.h
index fdce5801a9d..91117936eef 100644
--- a/iotdb-client/client-cpp/src/include/SessionC.h
+++ b/iotdb-client/client-cpp/src/include/SessionC.h
@@ -131,6 +131,14 @@ TsStatus ts_session_open_with_compression(CSession*
session, bool enableRPCCompr
TsStatus ts_session_close(CSession* session);
+/**
+ * Enables TLS for a tree-model session. Pass NULL for both client certificate
and private key
+ * to use one-way TLS; pass PEM paths for both to use mutual TLS.
+ */
+TsStatus ts_session_set_ssl_config(CSession* session, const char*
trustCertFilePath,
+ const char* clientCertificateFilePath,
+ const char* clientPrivateKeyFilePath);
+
/* ============================================================
* Session Lifecycle — Table Model
* ============================================================ */
@@ -148,6 +156,13 @@ TsStatus ts_table_session_open(CTableSession* session);
TsStatus ts_table_session_close(CTableSession* session);
+/** Creates and opens a TLS or mutual-TLS table-model session. */
+CTableSession* ts_table_session_new_with_ssl(const char* host, int rpcPort,
const char* username,
+ const char* password, const char*
database,
+ const char* trustCertFilePath,
+ const char*
clientCertificateFilePath,
+ const char*
clientPrivateKeyFilePath);
+
/* ============================================================
* Timezone
* ============================================================ */
diff --git a/iotdb-client/client-cpp/src/include/SessionPool.h
b/iotdb-client/client-cpp/src/include/SessionPool.h
index 4483dab0c51..0b578294a35 100644
--- a/iotdb-client/client-cpp/src/include/SessionPool.h
+++ b/iotdb-client/client-cpp/src/include/SessionPool.h
@@ -188,6 +188,8 @@ public:
SessionPool& setWaitToGetSessionTimeoutMs(int64_t timeoutMs);
SessionPool& setUseSSL(bool useSSL);
SessionPool& setTrustCertFilePath(std::string path);
+ SessionPool& setClientCertificateFilePath(std::string path);
+ SessionPool& setClientPrivateKeyFilePath(std::string path);
// Borrow a Session. Blocks until one is free or a new one can be created,
// up to timeoutMs (<= 0 means use the pool default). Throws IoTDBException
on
@@ -247,8 +249,7 @@ private:
bool enableAutoFetch_ = AbstractSessionBuilder::DEFAULT_ENABLE_AUTO_FETCH;
bool enableRPCCompression_ =
AbstractSessionBuilder::DEFAULT_ENABLE_RPC_COMPRESSION;
int connectTimeoutMs_ = AbstractSessionBuilder::DEFAULT_CONNECT_TIMEOUT_MS;
- bool useSSL_ = false;
- std::string trustCertFilePath_;
+ SslConfig sslConfig_;
// pool sizing / waiting policy
size_t maxSize_;
@@ -333,10 +334,20 @@ public:
}
SessionPoolBuilder* useSSL(bool v) {
AbstractSessionBuilder::useSSL = v;
+ AbstractSessionBuilder::sslConfig.useSsl = v;
return this;
}
SessionPoolBuilder* trustCertFilePath(const std::string& v) {
AbstractSessionBuilder::trustCertFilePath = v;
+ AbstractSessionBuilder::sslConfig.trustCertFilePath = v;
+ return this;
+ }
+ SessionPoolBuilder* clientCertificateFilePath(const std::string& v) {
+ AbstractSessionBuilder::sslConfig.clientCertificateFilePath = v;
+ return this;
+ }
+ SessionPoolBuilder* clientPrivateKeyFilePath(const std::string& v) {
+ AbstractSessionBuilder::sslConfig.clientPrivateKeyFilePath = v;
return this;
}
SessionPoolBuilder* maxSize(size_t v) {
@@ -379,8 +390,10 @@ public:
.setEnableRPCCompression(AbstractSessionBuilder::enableRPCCompression)
.setConnectTimeoutMs(AbstractSessionBuilder::connectTimeoutMs)
.setWaitToGetSessionTimeoutMs(waitTimeoutMs_)
- .setUseSSL(AbstractSessionBuilder::useSSL)
- .setTrustCertFilePath(AbstractSessionBuilder::trustCertFilePath);
+ .setUseSSL(AbstractSessionBuilder::getSslConfig().useSsl)
+
.setTrustCertFilePath(AbstractSessionBuilder::getSslConfig().trustCertFilePath)
+
.setClientCertificateFilePath(AbstractSessionBuilder::sslConfig.clientCertificateFilePath)
+
.setClientPrivateKeyFilePath(AbstractSessionBuilder::sslConfig.clientPrivateKeyFilePath);
return pool;
}
diff --git a/iotdb-client/client-cpp/src/include/TableSession.h
b/iotdb-client/client-cpp/src/include/SslConfig.h
similarity index 50%
copy from iotdb-client/client-cpp/src/include/TableSession.h
copy to iotdb-client/client-cpp/src/include/SslConfig.h
index d1eecfeeaba..2a0a44dad44 100644
--- a/iotdb-client/client-cpp/src/include/TableSession.h
+++ b/iotdb-client/client-cpp/src/include/SslConfig.h
@@ -17,30 +17,18 @@
* under the License.
*/
-// This file is a translation of the Java file
iotdb-client/session/src/main/java/org/apache/iotdb/session/TableSession.java
+#ifndef IOTDB_SSL_CONFIG_H
+#define IOTDB_SSL_CONFIG_H
-#ifndef IOTDB_TABLESESSION_H
-#define IOTDB_TABLESESSION_H
+#include <string>
-#include "Session.h"
+struct SslConfig {
+ bool useSsl = false;
+ std::string trustCertFilePath;
+ std::string clientCertificateFilePath;
+ std::string clientPrivateKeyFilePath;
-class TableSession {
-private:
- std::shared_ptr<Session> session_;
- string getDatabase();
-
-public:
- TableSession(std::shared_ptr<Session> session) {
- this->session_ = session;
- }
- ~TableSession() {}
-
- void insert(Tablet& tablet, bool sorted = false);
- void executeNonQueryStatement(const std::string& sql);
- unique_ptr<SessionDataSet> executeQueryStatement(const std::string& sql);
- unique_ptr<SessionDataSet> executeQueryStatement(const std::string& sql,
int64_t timeoutInMs);
- void open(bool enableRPCCompression = false);
- void close();
+ void validate() const;
};
-#endif // IOTDB_TABLESESSION_H
\ No newline at end of file
+#endif // IOTDB_SSL_CONFIG_H
diff --git a/iotdb-client/client-cpp/src/include/TableSession.h
b/iotdb-client/client-cpp/src/include/TableSession.h
index d1eecfeeaba..2944b2355e8 100644
--- a/iotdb-client/client-cpp/src/include/TableSession.h
+++ b/iotdb-client/client-cpp/src/include/TableSession.h
@@ -41,6 +41,7 @@ public:
unique_ptr<SessionDataSet> executeQueryStatement(const std::string& sql,
int64_t timeoutInMs);
void open(bool enableRPCCompression = false);
void close();
+ void setSslConfig(const SslConfig& sslConfig);
};
-#endif // IOTDB_TABLESESSION_H
\ No newline at end of file
+#endif // IOTDB_TABLESESSION_H
diff --git a/iotdb-client/client-cpp/src/include/TableSessionBuilder.h
b/iotdb-client/client-cpp/src/include/TableSessionBuilder.h
index 3c9739ecc8e..3e762ad77c7 100644
--- a/iotdb-client/client-cpp/src/include/TableSessionBuilder.h
+++ b/iotdb-client/client-cpp/src/include/TableSessionBuilder.h
@@ -47,11 +47,23 @@ public:
}
TableSessionBuilder* useSSL(bool useSSL) {
AbstractSessionBuilder::useSSL = useSSL;
+ AbstractSessionBuilder::sslConfig.useSsl = useSSL;
return this;
}
TableSessionBuilder* trustCertFilePath(const std::string& trustCertFilePath)
{
AbstractSessionBuilder::trustCertFilePath = trustCertFilePath;
+ AbstractSessionBuilder::sslConfig.trustCertFilePath = trustCertFilePath;
+ return this;
+ }
+
+ TableSessionBuilder* clientCertificateFilePath(const std::string&
clientCertificateFilePath) {
+ AbstractSessionBuilder::sslConfig.clientCertificateFilePath =
clientCertificateFilePath;
+ return this;
+ }
+
+ TableSessionBuilder* clientPrivateKeyFilePath(const std::string&
clientPrivateKeyFilePath) {
+ AbstractSessionBuilder::sslConfig.clientPrivateKeyFilePath =
clientPrivateKeyFilePath;
return this;
}
@@ -93,4 +105,4 @@ public:
}
};
-#endif // IOTDB_TABLESESSIONBUILDER_H
\ No newline at end of file
+#endif // IOTDB_TABLESESSIONBUILDER_H
diff --git a/iotdb-client/client-cpp/src/rpc/NodesSupplier.cpp
b/iotdb-client/client-cpp/src/rpc/NodesSupplier.cpp
index 55fd13a08f4..f2872d476e5 100644
--- a/iotdb-client/client-cpp/src/rpc/NodesSupplier.cpp
+++ b/iotdb-client/client-cpp/src/rpc/NodesSupplier.cpp
@@ -67,33 +67,33 @@ std::vector<TEndPoint>
StaticNodesSupplier::getEndPointList() {
StaticNodesSupplier::~StaticNodesSupplier() = default;
-std::shared_ptr<NodesSupplier> NodesSupplier::create(
- const std::vector<TEndPoint>& endpoints, const std::string& userName,
- const std::string& password, bool useSSL, const std::string&
trustCertFilePath,
- const std::string& zoneId, int32_t thriftDefaultBufferSize, int32_t
thriftMaxFrameSize,
- int32_t connectionTimeoutInMs, bool enableRPCCompression, const
std::string& version,
- std::chrono::milliseconds refreshInterval, NodeSelectionPolicy policy) {
+std::shared_ptr<NodesSupplier>
+NodesSupplier::create(const std::vector<TEndPoint>& endpoints, const
std::string& userName,
+ const std::string& password, const SslConfig& sslConfig,
+ const std::string& zoneId, int32_t
thriftDefaultBufferSize,
+ int32_t thriftMaxFrameSize, int32_t
connectionTimeoutInMs,
+ bool enableRPCCompression, const std::string& version,
+ std::chrono::milliseconds refreshInterval,
NodeSelectionPolicy policy) {
if (endpoints.empty()) {
return nullptr;
}
auto supplier = std::make_shared<NodesSupplier>(
- userName, password, useSSL, trustCertFilePath, zoneId,
thriftDefaultBufferSize,
- thriftMaxFrameSize, connectionTimeoutInMs, enableRPCCompression,
version, endpoints, policy);
+ userName, password, sslConfig, zoneId, thriftDefaultBufferSize,
thriftMaxFrameSize,
+ connectionTimeoutInMs, enableRPCCompression, version, endpoints, policy);
supplier->startBackgroundRefresh(refreshInterval);
return supplier;
}
-NodesSupplier::NodesSupplier(const std::string& userName, const std::string&
password, bool useSSL,
- const std::string& trustCertFilePath, const
std::string& zoneId,
+NodesSupplier::NodesSupplier(const std::string& userName, const std::string&
password,
+ const SslConfig& sslConfig, const std::string&
zoneId,
int32_t thriftDefaultBufferSize, int32_t
thriftMaxFrameSize,
int32_t connectionTimeoutInMs, bool
enableRPCCompression,
const std::string& version, const
std::vector<TEndPoint>& endpoints,
NodeSelectionPolicy policy)
- : userName_(userName), password_(password), zoneId_(zoneId),
- thriftDefaultBufferSize_(thriftDefaultBufferSize),
thriftMaxFrameSize_(thriftMaxFrameSize),
- connectionTimeoutInMs_(connectionTimeoutInMs), useSSL_(useSSL),
- trustCertFilePath_(trustCertFilePath),
enableRPCCompression_(enableRPCCompression),
- version_(version), endpoints_(endpoints), selectionPolicy_(policy) {
+ : userName_(userName), password_(password),
thriftDefaultBufferSize_(thriftDefaultBufferSize),
+ thriftMaxFrameSize_(thriftMaxFrameSize),
connectionTimeoutInMs_(connectionTimeoutInMs),
+ sslConfig_(sslConfig), enableRPCCompression_(enableRPCCompression),
version_(version),
+ zoneId_(zoneId), endpoints_(endpoints), selectionPolicy_(policy) {
deduplicateEndpoints();
}
@@ -156,8 +156,7 @@ std::vector<TEndPoint>
NodesSupplier::fetchLatestEndpoints() {
try {
if (client_ == nullptr) {
client_ = std::make_shared<ThriftConnection>(endpoint);
- client_->init(userName_, password_, enableRPCCompression_, useSSL_,
trustCertFilePath_,
- zoneId_, version_);
+ client_->init(userName_, password_, enableRPCCompression_, sslConfig_,
zoneId_, version_);
}
auto sessionDataSet =
client_->executeQueryStatement(SHOW_AVAILABLE_URLS_COMMAND);
diff --git a/iotdb-client/client-cpp/src/rpc/NodesSupplier.h
b/iotdb-client/client-cpp/src/rpc/NodesSupplier.h
index c067bbb6d72..a5fe04ef0f0 100644
--- a/iotdb-client/client-cpp/src/rpc/NodesSupplier.h
+++ b/iotdb-client/client-cpp/src/rpc/NodesSupplier.h
@@ -78,8 +78,8 @@ public:
static std::shared_ptr<NodesSupplier>
create(const std::vector<TEndPoint>& endpoints, const std::string& userName,
- const std::string& password, bool useSSL = false,
- const std::string& trustCertFilePath = "", const std::string& zoneId
= "",
+ const std::string& password, const SslConfig& sslConfig = SslConfig(),
+ const std::string& zoneId = "",
int32_t thriftDefaultBufferSize =
ThriftConnection::THRIFT_DEFAULT_BUFFER_SIZE,
int32_t thriftMaxFrameSize = ThriftConnection::THRIFT_MAX_FRAME_SIZE,
int32_t connectionTimeoutInMs =
ThriftConnection::CONNECTION_TIMEOUT_IN_MS,
@@ -87,8 +87,8 @@ public:
std::chrono::milliseconds refreshInterval =
std::chrono::milliseconds(TIMEOUT_IN_MS),
NodeSelectionPolicy policy = RoundRobinPolicy::select);
- NodesSupplier(const std::string& userName, const std::string& password, bool
useSSL,
- const std::string& trustCertFilePath, const std::string&
zoneId,
+ NodesSupplier(const std::string& userName, const std::string& password,
+ const SslConfig& sslConfig, const std::string& zoneId,
int32_t thriftDefaultBufferSize, int32_t thriftMaxFrameSize,
int32_t connectionTimeoutInMs, bool enableRPCCompression,
const std::string& version, const std::vector<TEndPoint>&
endpoints,
@@ -106,8 +106,7 @@ private:
int32_t thriftDefaultBufferSize_;
int32_t thriftMaxFrameSize_;
int32_t connectionTimeoutInMs_;
- bool useSSL_;
- std::string trustCertFilePath_;
+ SslConfig sslConfig_;
bool enableRPCCompression_;
std::string version_;
std::string zoneId_;
@@ -135,4 +134,4 @@ private:
void stopBackgroundRefresh() noexcept;
};
-#endif
\ No newline at end of file
+#endif
diff --git a/iotdb-client/client-cpp/src/include/TableSession.h
b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp
similarity index 50%
copy from iotdb-client/client-cpp/src/include/TableSession.h
copy to iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp
index d1eecfeeaba..076e2de9732 100644
--- a/iotdb-client/client-cpp/src/include/TableSession.h
+++ b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.cpp
@@ -17,30 +17,20 @@
* under the License.
*/
-// This file is a translation of the Java file
iotdb-client/session/src/main/java/org/apache/iotdb/session/TableSession.java
+#include "RpcSslUtils.h"
-#ifndef IOTDB_TABLESESSION_H
-#define IOTDB_TABLESESSION_H
+#if WITH_SSL
-#include "Session.h"
-
-class TableSession {
-private:
- std::shared_ptr<Session> session_;
- string getDatabase();
-
-public:
- TableSession(std::shared_ptr<Session> session) {
- this->session_ = session;
+void configureSslSocketFactory(
+ const std::shared_ptr<apache::thrift::transport::TSSLSocketFactory>&
socketFactory,
+ const SslConfig& sslConfig) {
+ sslConfig.validate();
+ socketFactory->loadTrustedCertificates(sslConfig.trustCertFilePath.c_str());
+ if (!sslConfig.clientCertificateFilePath.empty()) {
+
socketFactory->loadCertificate(sslConfig.clientCertificateFilePath.c_str());
+ socketFactory->loadPrivateKey(sslConfig.clientPrivateKeyFilePath.c_str());
}
- ~TableSession() {}
-
- void insert(Tablet& tablet, bool sorted = false);
- void executeNonQueryStatement(const std::string& sql);
- unique_ptr<SessionDataSet> executeQueryStatement(const std::string& sql);
- unique_ptr<SessionDataSet> executeQueryStatement(const std::string& sql,
int64_t timeoutInMs);
- void open(bool enableRPCCompression = false);
- void close();
-};
+ socketFactory->authenticate(true);
+}
-#endif // IOTDB_TABLESESSION_H
\ No newline at end of file
+#endif // WITH_SSL
diff --git a/iotdb-client/client-cpp/src/include/TableSession.h
b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.h
similarity index 50%
copy from iotdb-client/client-cpp/src/include/TableSession.h
copy to iotdb-client/client-cpp/src/rpc/RpcSslUtils.h
index d1eecfeeaba..ab78ebec3f8 100644
--- a/iotdb-client/client-cpp/src/include/TableSession.h
+++ b/iotdb-client/client-cpp/src/rpc/RpcSslUtils.h
@@ -17,30 +17,21 @@
* under the License.
*/
-// This file is a translation of the Java file
iotdb-client/session/src/main/java/org/apache/iotdb/session/TableSession.java
+#ifndef IOTDB_RPC_SSL_UTILS_H
+#define IOTDB_RPC_SSL_UTILS_H
-#ifndef IOTDB_TABLESESSION_H
-#define IOTDB_TABLESESSION_H
+#if WITH_SSL
-#include "Session.h"
+#include <memory>
-class TableSession {
-private:
- std::shared_ptr<Session> session_;
- string getDatabase();
+#include <thrift/transport/TSSLSocket.h>
-public:
- TableSession(std::shared_ptr<Session> session) {
- this->session_ = session;
- }
- ~TableSession() {}
+#include "SslConfig.h"
- void insert(Tablet& tablet, bool sorted = false);
- void executeNonQueryStatement(const std::string& sql);
- unique_ptr<SessionDataSet> executeQueryStatement(const std::string& sql);
- unique_ptr<SessionDataSet> executeQueryStatement(const std::string& sql,
int64_t timeoutInMs);
- void open(bool enableRPCCompression = false);
- void close();
-};
+void configureSslSocketFactory(
+ const std::shared_ptr<apache::thrift::transport::TSSLSocketFactory>&
socketFactory,
+ const SslConfig& sslConfig);
-#endif // IOTDB_TABLESESSION_H
\ No newline at end of file
+#endif // WITH_SSL
+
+#endif // IOTDB_RPC_SSL_UTILS_H
diff --git a/iotdb-client/client-cpp/src/rpc/SessionConnection.cpp
b/iotdb-client/client-cpp/src/rpc/SessionConnection.cpp
index dfdb0198e38..3eea30cb989 100644
--- a/iotdb-client/client-cpp/src/rpc/SessionConnection.cpp
+++ b/iotdb-client/client-cpp/src/rpc/SessionConnection.cpp
@@ -19,6 +19,7 @@
#include "SessionConnection.h"
#include "SessionImpl.h"
#include "RpcCommon.h"
+#include "RpcSslUtils.h"
#include "common_types.h"
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/protocol/TCompactProtocol.h>
@@ -46,7 +47,7 @@ SessionConnection::SessionConnection(Session::Impl*
session_ptr, const TEndPoint
sqlDialect(std::move(dialect)), database(std::move(db)) {
this->zoneId = zoneId.empty() ? getSystemDefaultZoneId() : zoneId;
endPointList.push_back(endpoint);
- init(endPoint, session->useSSL_, session->trustCertFilePath_);
+ init(endPoint, session->sslConfig_);
}
void SessionConnection::close() {
@@ -92,12 +93,10 @@ SessionConnection::~SessionConnection() {
}
}
-void SessionConnection::init(const TEndPoint& endpoint, bool useSSL,
- const std::string& trustCertFilePath) {
- if (useSSL) {
+void SessionConnection::init(const TEndPoint& endpoint, const SslConfig&
sslConfig) {
+ if (sslConfig.useSsl) {
#if WITH_SSL
- socketFactory_->loadTrustedCertificates(trustCertFilePath.c_str());
- socketFactory_->authenticate(false);
+ configureSslSocketFactory(socketFactory_, sslConfig);
auto sslSocket = socketFactory_->createSocket(endPoint.ip, endPoint.port);
sslSocket->setConnTimeout(connectionTimeoutInMs);
transport = std::make_shared<TFramedTransport>(sslSocket);
@@ -332,7 +331,7 @@ bool SessionConnection::reconnect() {
}
tryHostNum++;
try {
- init(this->endPoint, this->session->useSSL_,
this->session->trustCertFilePath_);
+ init(this->endPoint, this->session->sslConfig_);
reconnect = true;
} catch (const IoTDBConnectionException& e) {
log_warn("The current node may have been down, connection exception:
%s", e.what());
diff --git a/iotdb-client/client-cpp/src/rpc/SessionConnection.h
b/iotdb-client/client-cpp/src/rpc/SessionConnection.h
index 472e29fd665..c3c6f1a5735 100644
--- a/iotdb-client/client-cpp/src/rpc/SessionConnection.h
+++ b/iotdb-client/client-cpp/src/rpc/SessionConnection.h
@@ -53,7 +53,7 @@ public:
const TEndPoint& getEndPoint();
- void init(const TEndPoint& endpoint, bool useSSL, const std::string&
trustCertFilePath);
+ void init(const TEndPoint& endpoint, const SslConfig& sslConfig);
void insertStringRecord(const TSInsertStringRecordReq& request);
@@ -123,6 +123,7 @@ public:
}
friend class Session;
+ friend class SessionTestAccessor;
private:
void close();
diff --git a/iotdb-client/client-cpp/src/rpc/SessionImpl.h
b/iotdb-client/client-cpp/src/rpc/SessionImpl.h
index 406537486c0..23c6870cce8 100644
--- a/iotdb-client/client-cpp/src/rpc/SessionImpl.h
+++ b/iotdb-client/client-cpp/src/rpc/SessionImpl.h
@@ -41,8 +41,7 @@ class Session::Impl {
public:
std::string host_;
int rpcPort_ = 6667;
- bool useSSL_ = false;
- std::string trustCertFilePath_;
+ SslConfig sslConfig_;
std::vector<std::string> nodeUrls_;
std::string username_ = "root";
std::string password_ = "root";
diff --git a/iotdb-client/client-cpp/src/rpc/ThriftConnection.cpp
b/iotdb-client/client-cpp/src/rpc/ThriftConnection.cpp
index 1cc6c5417b2..6b8d0289a16 100644
--- a/iotdb-client/client-cpp/src/rpc/ThriftConnection.cpp
+++ b/iotdb-client/client-cpp/src/rpc/ThriftConnection.cpp
@@ -26,6 +26,7 @@
#include <thrift/transport/TTransportException.h>
#include "RpcCommon.h"
+#include "RpcSslUtils.h"
#include "SessionDataSet.h"
#include "SessionDataSetFactory.h"
@@ -64,13 +65,11 @@ void ThriftConnection::initZoneId() {
}
void ThriftConnection::init(const std::string& username, const std::string&
password,
- bool enableRPCCompression, bool useSSL,
- const std::string& trustCertFilePath, const
std::string& zoneId,
- const std::string& version) {
- if (useSSL) {
+ bool enableRPCCompression, const SslConfig&
sslConfig,
+ const std::string& zoneId, const std::string&
version) {
+ if (sslConfig.useSsl) {
#if WITH_SSL
- socketFactory_->loadTrustedCertificates(trustCertFilePath.c_str());
- socketFactory_->authenticate(false);
+ configureSslSocketFactory(socketFactory_, sslConfig);
auto sslSocket = socketFactory_->createSocket(endPoint_.ip,
endPoint_.port);
sslSocket->setConnTimeout(connectionTimeoutInMs_);
transport_ = std::make_shared<TFramedTransport>(sslSocket);
diff --git a/iotdb-client/client-cpp/src/rpc/ThriftConnection.h
b/iotdb-client/client-cpp/src/rpc/ThriftConnection.h
index 28691174031..16bb01b7599 100644
--- a/iotdb-client/client-cpp/src/rpc/ThriftConnection.h
+++ b/iotdb-client/client-cpp/src/rpc/ThriftConnection.h
@@ -25,6 +25,7 @@
#endif
#include "IClientRPCService.h"
#include "SessionConfig.h"
+#include "SslConfig.h"
class SessionDataSet;
@@ -43,9 +44,8 @@ public:
~ThriftConnection();
void init(const std::string& username, const std::string& password,
- bool enableRPCCompression = false, bool useSSL = false,
- const std::string& trustCertFilePath = "", const std::string&
zoneId = std::string(),
- const std::string& version = "V_1_0");
+ bool enableRPCCompression = false, const SslConfig& sslConfig =
SslConfig(),
+ const std::string& zoneId = std::string(), const std::string&
version = "V_1_0");
std::unique_ptr<SessionDataSet> executeQueryStatement(const std::string& sql,
int64_t timeoutInMs =
-1);
diff --git a/iotdb-client/client-cpp/src/session/Session.cpp
b/iotdb-client/client-cpp/src/session/Session.cpp
index ab71732ebe1..e8cbc55d23c 100644
--- a/iotdb-client/client-cpp/src/session/Session.cpp
+++ b/iotdb-client/client-cpp/src/session/Session.cpp
@@ -563,7 +563,6 @@ Session::Session(const std::string& host, int rpcPort) :
impl_(new Impl()) {
impl_->host_ = host;
impl_->rpcPort_ = rpcPort;
impl_->initZoneId();
- impl_->initNodesSupplier();
}
Session::Session(const std::vector<std::string>& nodeUrls, const std::string&
username,
@@ -574,7 +573,6 @@ Session::Session(const std::vector<std::string>& nodeUrls,
const std::string& us
impl_->password_ = password;
impl_->version = Version::V_1_0;
impl_->initZoneId();
- impl_->initNodesSupplier(impl_->nodeUrls_);
}
Session::Session(const std::string& host, int rpcPort, const std::string&
username,
@@ -587,7 +585,6 @@ Session::Session(const std::string& host, int rpcPort,
const std::string& userna
impl_->fetchSize_ = iotdb::session::DEFAULT_FETCH_SIZE;
impl_->version = Version::V_1_0;
impl_->initZoneId();
- impl_->initNodesSupplier();
}
Session::Session(const std::string& host, int rpcPort, const std::string&
username,
@@ -601,7 +598,6 @@ Session::Session(const std::string& host, int rpcPort,
const std::string& userna
impl_->fetchSize_ = fetchSize;
impl_->version = Version::V_1_0;
impl_->initZoneId();
- impl_->initNodesSupplier();
}
Session::Session(const std::string& host, const std::string& rpcPort, const
std::string& username,
@@ -615,7 +611,6 @@ Session::Session(const std::string& host, const
std::string& rpcPort, const std:
impl_->fetchSize_ = fetchSize;
impl_->version = Version::V_1_0;
impl_->initZoneId();
- impl_->initNodesSupplier();
}
Session::Session(AbstractSessionBuilder* builder) : impl_(new Impl()) {
@@ -632,10 +627,8 @@ Session::Session(AbstractSessionBuilder* builder) :
impl_(new Impl()) {
impl_->enableRedirection_ = builder->enableRedirections;
impl_->connectTimeoutMs_ = builder->connectTimeoutMs;
impl_->nodeUrls_ = builder->nodeUrls;
- impl_->useSSL_ = builder->useSSL;
- impl_->trustCertFilePath_ = builder->trustCertFilePath;
+ impl_->sslConfig_ = builder->getSslConfig();
impl_->initZoneId();
- impl_->initNodesSupplier(impl_->nodeUrls_);
}
void Session::setSqlDialect(const std::string& dialect) {
@@ -646,6 +639,14 @@ void Session::setDatabase(const std::string& database) {
impl_->database_ = database;
}
+void Session::setSslConfig(const SslConfig& sslConfig) {
+ if (!impl_->isClosed_) {
+ throw IoTDBException("SSL configuration cannot be changed after the
Session is opened");
+ }
+ sslConfig.validate();
+ impl_->sslConfig_ = sslConfig;
+}
+
std::string Session::getDatabase() {
return impl_->database_;
}
@@ -961,8 +962,7 @@ void Session::Impl::initNodesSupplier(const
std::vector<std::string>& nodeUrls)
}
if (enableAutoFetch_) {
- nodesSupplier_ =
- NodesSupplier::create(endPoints, username_, password_, useSSL_,
trustCertFilePath_);
+ nodesSupplier_ = NodesSupplier::create(endPoints, username_, password_,
sslConfig_);
} else {
nodesSupplier_ = make_shared<StaticNodesSupplier>(endPoints);
}
@@ -1147,8 +1147,10 @@ void Session::open(bool enableRPCCompression, int
connectionTimeoutInMs) {
}
try {
+ impl_->initNodesSupplier(impl_->nodeUrls_);
impl_->initDefaultSessionConnection();
} catch (const exception& e) {
+ impl_->nodesSupplier_.reset();
log_debug(e.what());
throw IoTDBException(e.what());
}
@@ -1188,9 +1190,11 @@ void Session::close() {
impl_->defaultSessionConnection_.reset();
}
} catch (...) {
+ impl_->nodesSupplier_.reset();
impl_->isClosed_ = true;
throw;
}
+ impl_->nodesSupplier_.reset();
impl_->isClosed_ = true;
}
@@ -2122,9 +2126,10 @@ bool Session::checkTimeseriesExists(const string& path) {
}
shared_ptr<SessionConnection> Session::Impl::getQuerySessionConnection() {
+ auto defaultSessionConnection = getDefaultSessionConnection();
auto endPoint = nodesSupplier_->getQueryEndPoint();
if (!endPoint.is_initialized() || endPointToSessionConnection.empty()) {
- return getDefaultSessionConnection();
+ return defaultSessionConnection;
}
auto it = endPointToSessionConnection.find(endPoint.value());
diff --git a/iotdb-client/client-cpp/src/session/SessionC.cpp
b/iotdb-client/client-cpp/src/session/SessionC.cpp
index 7365d6ff645..e5f8e67c979 100644
--- a/iotdb-client/client-cpp/src/session/SessionC.cpp
+++ b/iotdb-client/client-cpp/src/session/SessionC.cpp
@@ -154,6 +154,26 @@ static std::map<std::string, std::string> toStringMap(int
count, const char* con
return m;
}
+static TsStatus setSslConfig(SslConfig& config, const char* trustCertFilePath,
+ const char* clientCertificateFilePath,
+ const char* clientPrivateKeyFilePath) {
+ if (trustCertFilePath == nullptr) {
+ return setError(TS_ERR_INVALID_PARAM, "trustCertFilePath is null");
+ }
+ if ((clientCertificateFilePath == nullptr) != (clientPrivateKeyFilePath ==
nullptr)) {
+ return setError(TS_ERR_INVALID_PARAM,
+ "clientCertificateFilePath and clientPrivateKeyFilePath
must both be null or "
+ "both be set");
+ }
+ config.useSsl = true;
+ config.trustCertFilePath = trustCertFilePath;
+ config.clientCertificateFilePath =
+ clientCertificateFilePath == nullptr ? "" : clientCertificateFilePath;
+ config.clientPrivateKeyFilePath =
+ clientPrivateKeyFilePath == nullptr ? "" : clientPrivateKeyFilePath;
+ return TS_OK;
+}
+
/**
* Convert C typed values (void* const* values, TSDataType_C* types, int count)
* to C++ vector<char*> that Session expects.
@@ -332,6 +352,26 @@ TsStatus ts_session_close(CSession* session) {
}
}
+TsStatus ts_session_set_ssl_config(CSession* session, const char*
trustCertFilePath,
+ const char* clientCertificateFilePath,
+ const char* clientPrivateKeyFilePath) {
+ clearError();
+ if (!session)
+ return setError(TS_ERR_NULL_PTR, "session is null");
+ try {
+ SslConfig config;
+ TsStatus status = setSslConfig(config, trustCertFilePath,
clientCertificateFilePath,
+ clientPrivateKeyFilePath);
+ if (status != TS_OK) {
+ return status;
+ }
+ session->cpp->setSslConfig(config);
+ return TS_OK;
+ } catch (const std::exception& e) {
+ return handleException(e);
+ }
+}
+
/* ============================================================
* Session Lifecycle — Table Model
* ============================================================ */
@@ -409,6 +449,35 @@ TsStatus ts_table_session_close(CTableSession* session) {
}
}
+CTableSession* ts_table_session_new_with_ssl(const char* host, int rpcPort,
const char* username,
+ const char* password, const char*
database,
+ const char* trustCertFilePath,
+ const char*
clientCertificateFilePath,
+ const char*
clientPrivateKeyFilePath) {
+ clearError();
+ try {
+ SslConfig config;
+ if (setSslConfig(config, trustCertFilePath, clientCertificateFilePath,
+ clientPrivateKeyFilePath) != TS_OK) {
+ return nullptr;
+ }
+ TableSessionBuilder builder;
+ builder.host(std::string(host))
+ ->rpcPort(rpcPort)
+ ->username(std::string(username))
+ ->password(std::string(password))
+ ->database(std::string(database ? database : ""));
+ builder.sslConfig = config;
+ auto tableSession = builder.build();
+ auto* session = new CTableSession_();
+ session->cpp = std::move(tableSession);
+ return session;
+ } catch (const std::exception& e) {
+ handleException(e);
+ return nullptr;
+ }
+}
+
/* ============================================================
* Timezone
* ============================================================ */
diff --git a/iotdb-client/client-cpp/src/session/SessionPool.cpp
b/iotdb-client/client-cpp/src/session/SessionPool.cpp
index a828f0ac2c6..c98a0fac365 100644
--- a/iotdb-client/client-cpp/src/session/SessionPool.cpp
+++ b/iotdb-client/client-cpp/src/session/SessionPool.cpp
@@ -100,12 +100,22 @@ SessionPool&
SessionPool::setWaitToGetSessionTimeoutMs(int64_t timeoutMs) {
}
SessionPool& SessionPool::setUseSSL(bool useSSL) {
- useSSL_ = useSSL;
+ sslConfig_.useSsl = useSSL;
return *this;
}
SessionPool& SessionPool::setTrustCertFilePath(std::string path) {
- trustCertFilePath_ = std::move(path);
+ sslConfig_.trustCertFilePath = std::move(path);
+ return *this;
+}
+
+SessionPool& SessionPool::setClientCertificateFilePath(std::string path) {
+ sslConfig_.clientCertificateFilePath = std::move(path);
+ return *this;
+}
+
+SessionPool& SessionPool::setClientPrivateKeyFilePath(std::string path) {
+ sslConfig_.clientPrivateKeyFilePath = std::move(path);
return *this;
}
@@ -124,8 +134,7 @@ std::shared_ptr<Session> SessionPool::constructNewSession()
{
builder.enableRedirections = enableRedirection_;
builder.enableRPCCompression = enableRPCCompression_;
builder.connectTimeoutMs = connectTimeoutMs_;
- builder.useSSL = useSSL_;
- builder.trustCertFilePath = trustCertFilePath_;
+ builder.sslConfig = sslConfig_;
auto session = std::make_shared<Session>(&builder);
session->open(enableRPCCompression_, connectTimeoutMs_);
diff --git a/iotdb-client/client-cpp/src/include/TableSession.h
b/iotdb-client/client-cpp/src/session/SslConfig.cpp
similarity index 50%
copy from iotdb-client/client-cpp/src/include/TableSession.h
copy to iotdb-client/client-cpp/src/session/SslConfig.cpp
index d1eecfeeaba..e72bcb089b5 100644
--- a/iotdb-client/client-cpp/src/include/TableSession.h
+++ b/iotdb-client/client-cpp/src/session/SslConfig.cpp
@@ -17,30 +17,19 @@
* under the License.
*/
-// This file is a translation of the Java file
iotdb-client/session/src/main/java/org/apache/iotdb/session/TableSession.java
+#include "SslConfig.h"
-#ifndef IOTDB_TABLESESSION_H
-#define IOTDB_TABLESESSION_H
+#include <stdexcept>
-#include "Session.h"
-
-class TableSession {
-private:
- std::shared_ptr<Session> session_;
- string getDatabase();
-
-public:
- TableSession(std::shared_ptr<Session> session) {
- this->session_ = session;
+void SslConfig::validate() const {
+ if (!useSsl) {
+ return;
}
- ~TableSession() {}
-
- void insert(Tablet& tablet, bool sorted = false);
- void executeNonQueryStatement(const std::string& sql);
- unique_ptr<SessionDataSet> executeQueryStatement(const std::string& sql);
- unique_ptr<SessionDataSet> executeQueryStatement(const std::string& sql,
int64_t timeoutInMs);
- void open(bool enableRPCCompression = false);
- void close();
-};
-
-#endif // IOTDB_TABLESESSION_H
\ No newline at end of file
+ if (trustCertFilePath.empty()) {
+ throw std::invalid_argument("trustCertFilePath is required when SSL is
enabled");
+ }
+ if (clientCertificateFilePath.empty() != clientPrivateKeyFilePath.empty()) {
+ throw std::invalid_argument(
+ "clientCertificateFilePath and clientPrivateKeyFilePath must be
configured together");
+ }
+}
diff --git a/iotdb-client/client-cpp/src/session/TableSession.cpp
b/iotdb-client/client-cpp/src/session/TableSession.cpp
index 9cd80b7dd78..04283fc857f 100644
--- a/iotdb-client/client-cpp/src/session/TableSession.cpp
+++ b/iotdb-client/client-cpp/src/session/TableSession.cpp
@@ -43,4 +43,7 @@ void TableSession::open(bool enableRPCCompression) {
}
void TableSession::close() {
session_->close();
-}
\ No newline at end of file
+}
+void TableSession::setSslConfig(const SslConfig& sslConfig) {
+ session_->setSslConfig(sslConfig);
+}
diff --git a/iotdb-client/client-cpp/test/CMakeLists.txt
b/iotdb-client/client-cpp/test/CMakeLists.txt
index b4a7cf1f767..41deaf513d8 100644
--- a/iotdb-client/client-cpp/test/CMakeLists.txt
+++ b/iotdb-client/client-cpp/test/CMakeLists.txt
@@ -45,11 +45,22 @@ set(_test_targets
session_c_relational_tests
session_utils_tests)
+if(WITH_SSL)
+ list(APPEND _test_targets rpc_ssl_iotdb_tests)
+endif()
+
add_executable(session_tests main.cpp
cpp/sessionIT.cpp)
add_executable(session_relational_tests main_Relational.cpp
cpp/sessionRelationalIT.cpp)
add_executable(session_c_tests main_c.cpp
cpp/sessionCIT.cpp)
add_executable(session_c_relational_tests main_c_Relational.cpp
cpp/sessionCRelationalIT.cpp)
add_executable(session_utils_tests main_utils.cpp
cpp/sessionUtilsTest.cpp)
+if(WITH_SSL)
+ add_executable(rpc_ssl_iotdb_tests main_rpc_ssl.cpp
cpp/RpcSslIotdbE2eTest.cpp)
+ file(TO_CMAKE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/fixtures"
_iotdb_test_fixtures_dir)
+ target_compile_definitions(rpc_ssl_iotdb_tests PRIVATE
+ CATCH_CONFIG_NO_POSIX_SIGNALS=1
+ IOTDB_TEST_FIXTURES_DIR="${_iotdb_test_fixtures_dir}")
+endif()
foreach(_t IN LISTS _test_targets)
target_include_directories(${_t} PRIVATE
@@ -89,10 +100,23 @@ if(MSVC)
add_test(NAME sessionCIT CONFIGURATIONS Release COMMAND
session_c_tests)
add_test(NAME sessionCRelationalIT CONFIGURATIONS Release COMMAND
session_c_relational_tests)
add_test(NAME sessionUtilsTest CONFIGURATIONS Release COMMAND
session_utils_tests)
+ if(WITH_SSL)
+ add_test(NAME rpcTlsIotdbTest CONFIGURATIONS Release COMMAND
rpc_ssl_iotdb_tests "[tls]")
+ add_test(NAME rpcMutualTlsIotdbTest CONFIGURATIONS Release
+ COMMAND rpc_ssl_iotdb_tests "[tls],[mtls]")
+ endif()
foreach(_t IN LISTS _test_targets)
add_custom_command(TARGET ${_t} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:iotdb_session> $<TARGET_FILE_DIR:${_t}>)
+ if(WITH_SSL)
+ _iotdb_collect_openssl_windows_dlls(_iotdb_ssl_runtime_dlls)
+ foreach(_ssl_dll IN LISTS _iotdb_ssl_runtime_dlls)
+ add_custom_command(TARGET ${_t} POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different
+ "${_ssl_dll}" $<TARGET_FILE_DIR:${_t}>)
+ endforeach()
+ endif()
endforeach()
else()
add_test(NAME sessionIT COMMAND session_tests)
@@ -100,10 +124,20 @@ else()
add_test(NAME sessionCIT COMMAND session_c_tests)
add_test(NAME sessionCRelationalIT COMMAND session_c_relational_tests)
add_test(NAME sessionUtilsTest COMMAND session_utils_tests)
+ if(WITH_SSL)
+ add_test(NAME rpcTlsIotdbTest COMMAND rpc_ssl_iotdb_tests "[tls]")
+ add_test(NAME rpcMutualTlsIotdbTest COMMAND rpc_ssl_iotdb_tests
"[tls],[mtls]")
+ endif()
endif()
# Run sequentially: parallel ctest overloads the single local IoTDB instance.
# sessionUtilsTest is a pure unit test and can run anytime.
set_tests_properties(
sessionIT sessionRelationalIT sessionCIT sessionCRelationalIT
- PROPERTIES RUN_SERIAL TRUE)
+ PROPERTIES LABELS "plain" RUN_SERIAL TRUE)
+set_tests_properties(sessionUtilsTest PROPERTIES LABELS "plain")
+if(WITH_SSL)
+ set_tests_properties(rpcTlsIotdbTest PROPERTIES LABELS "tls-only"
RUN_SERIAL TRUE)
+ set_tests_properties(rpcMutualTlsIotdbTest PROPERTIES
+ LABELS "mutual-auth" RUN_SERIAL TRUE ENVIRONMENT
"IOTDB_CPP_SSL_MUTUAL_AUTH=1")
+endif()
diff --git a/iotdb-client/client-cpp/test/cpp/RpcSslIotdbE2eTest.cpp
b/iotdb-client/client-cpp/test/cpp/RpcSslIotdbE2eTest.cpp
new file mode 100644
index 00000000000..88e97746397
--- /dev/null
+++ b/iotdb-client/client-cpp/test/cpp/RpcSslIotdbE2eTest.cpp
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <catch.hpp>
+
+#include <algorithm>
+#include <cstdlib>
+#include <memory>
+#include <string>
+
+#include "Session.h"
+#include "SessionBuilder.h"
+#include "SessionC.h"
+#include "SessionDataSet.h"
+#include "SessionImpl.h"
+#include "SessionPool.h"
+#include "TableSessionBuilder.h"
+
+class SessionTestAccessor {
+public:
+ static std::vector<TEndPoint> availableNodes(Session& session) {
+ return session.impl_->nodesSupplier_->getEndPointList();
+ }
+
+ static bool hasConnectionTo(Session& session, const TEndPoint& endpoint) {
+ return session.impl_->endPointToSessionConnection.find(endpoint) !=
+ session.impl_->endPointToSessionConnection.end();
+ }
+
+ static void makeInitialConnectionUnavailable(Session& session,
+ const TEndPoint&
bootstrapEndpoint) {
+ auto initialConnection = session.impl_->defaultSessionConnection_;
+ initialConnection->close();
+ session.impl_->endPointToSessionConnection.clear();
+ session.impl_->endPointToSessionConnection.emplace(bootstrapEndpoint,
initialConnection);
+ }
+};
+
+namespace {
+
+std::string fixture(const std::string& name) {
+ return std::string(IOTDB_TEST_FIXTURES_DIR) + "/tls/" + name;
+}
+
+bool mutualTlsEnabled() {
+ const char* value = std::getenv("IOTDB_CPP_SSL_MUTUAL_AUTH");
+ return value != nullptr && std::string(value) == "1";
+}
+
+template <typename Builder> void configureTls(Builder& builder) {
+ builder.useSSL(true)->trustCertFilePath(fixture("ca.crt"));
+ if (mutualTlsEnabled()) {
+ builder.clientCertificateFilePath(fixture("client.crt"))
+ ->clientPrivateKeyFilePath(fixture("client.key"));
+ }
+}
+
+SslConfig sslConfig() {
+ SslConfig config;
+ config.useSsl = true;
+ config.trustCertFilePath = fixture("ca.crt");
+ if (mutualTlsEnabled()) {
+ config.clientCertificateFilePath = fixture("client.crt");
+ config.clientPrivateKeyFilePath = fixture("client.key");
+ }
+ return config;
+}
+
+void requireDataSet(std::unique_ptr<SessionDataSet> dataSet) {
+ REQUIRE(dataSet != nullptr);
+ REQUIRE(dataSet->hasNext());
+ REQUIRE(dataSet->next() != nullptr);
+ dataSet->closeOperationHandle();
+}
+
+void requireCDataSet(CSessionDataSet* dataSet) {
+ REQUIRE(dataSet != nullptr);
+ REQUIRE(ts_dataset_has_next(dataSet));
+ CRowRecord* row = ts_dataset_next(dataSet);
+ REQUIRE(row != nullptr);
+ ts_row_record_destroy(row);
+ ts_dataset_destroy(dataSet);
+}
+
+} // namespace
+
+TEST_CASE("C++ APIs communicate with a TLS-enabled IoTDB", "[tls]") {
+ SessionBuilder treeBuilder;
+
treeBuilder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root");
+ configureTls(treeBuilder);
+ auto treeSession = treeBuilder.build();
+ requireDataSet(treeSession->executeQueryStatement("SHOW VERSION"));
+ treeSession->close();
+
+ TableSessionBuilder tableBuilder;
+
tableBuilder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root");
+ configureTls(tableBuilder);
+ auto tableSession = tableBuilder.build();
+ requireDataSet(tableSession->executeQueryStatement("SHOW VERSION"));
+ tableSession->close();
+
+ SessionPoolBuilder poolBuilder;
+
poolBuilder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root");
+ configureTls(poolBuilder);
+ auto pool = poolBuilder.build();
+ auto pooledDataSet = pool->executeQueryStatement("SHOW VERSION");
+ REQUIRE(pooledDataSet->hasNext());
+ REQUIRE(pooledDataSet->next() != nullptr);
+ pool->close();
+}
+
+TEST_CASE("C APIs communicate with a TLS-enabled IoTDB", "[tls]") {
+ const std::string certificatePath = fixture("client.crt");
+ const std::string privateKeyPath = fixture("client.key");
+ const char* cert = mutualTlsEnabled() ? certificatePath.c_str() : nullptr;
+ const char* key = mutualTlsEnabled() ? privateKeyPath.c_str() : nullptr;
+
+ CSession* treeSession = ts_session_new("127.0.0.1", 6667, "root", "root");
+ REQUIRE(treeSession != nullptr);
+ REQUIRE(ts_session_set_ssl_config(treeSession, fixture("ca.crt").c_str(),
cert, key) == TS_OK);
+ REQUIRE(ts_session_open(treeSession) == TS_OK);
+ CSessionDataSet* treeDataSet = nullptr;
+ REQUIRE(ts_session_execute_query(treeSession, "SHOW VERSION", &treeDataSet)
== TS_OK);
+ requireCDataSet(treeDataSet);
+ REQUIRE(ts_session_close(treeSession) == TS_OK);
+ ts_session_destroy(treeSession);
+
+ CTableSession* tableSession = ts_table_session_new_with_ssl("127.0.0.1",
6667, "root", "root", "",
+
fixture("ca.crt").c_str(), cert, key);
+ REQUIRE(tableSession != nullptr);
+ CSessionDataSet* tableDataSet = nullptr;
+ REQUIRE(ts_table_session_execute_query(tableSession, "SHOW VERSION",
&tableDataSet) == TS_OK);
+ requireCDataSet(tableDataSet);
+ REQUIRE(ts_table_session_close(tableSession) == TS_OK);
+ ts_table_session_destroy(tableSession);
+}
+
+TEST_CASE("TLS node discovery uses the final SSL configuration and supports
failover", "[tls]") {
+ std::vector<std::string> bootstrapNodes = {"127.0.0.1:6667"};
+ Session session(bootstrapNodes, "root", "root");
+ session.setSslConfig(sslConfig());
+ session.open();
+
+ auto discoveredNodes = SessionTestAccessor::availableNodes(session);
+ auto discovered =
+ std::find_if(discoveredNodes.begin(), discoveredNodes.end(), [](const
TEndPoint& node) {
+ return node.ip == "localhost" && node.port == 6667;
+ });
+ REQUIRE(discovered != discoveredNodes.end());
+
+ TEndPoint bootstrapEndpoint;
+ bootstrapEndpoint.ip = "127.0.0.1";
+ bootstrapEndpoint.port = 6667;
+ REQUIRE(std::none_of(discoveredNodes.begin(), discoveredNodes.end(),
+ [&bootstrapEndpoint](const TEndPoint& node) {
+ return node.ip == bootstrapEndpoint.ip &&
+ node.port == bootstrapEndpoint.port;
+ }));
+
+ // Model an unavailable initial bootstrap after discovery. The query must
+ // establish a new TLS connection to the endpoint learned from the server.
+ SessionTestAccessor::makeInitialConnectionUnavailable(session,
bootstrapEndpoint);
+ REQUIRE_FALSE(SessionTestAccessor::hasConnectionTo(session, *discovered));
+ requireDataSet(session.executeQueryStatement("SHOW VERSION"));
+ REQUIRE(SessionTestAccessor::hasConnectionTo(session, *discovered));
+ session.close();
+}
+
+TEST_CASE("mTLS server rejects a client without a certificate", "[mtls]") {
+ SessionBuilder builder;
+ builder.host("127.0.0.1")
+ ->rpcPort(6667)
+ ->username("root")
+ ->password("root")
+ ->useSSL(true)
+ ->trustCertFilePath(fixture("ca.crt"));
+ REQUIRE_THROWS(builder.build());
+}
diff --git a/iotdb-client/client-cpp/test/cpp/sessionIT.cpp
b/iotdb-client/client-cpp/test/cpp/sessionIT.cpp
index 45624f0300d..0c04d6bab68 100644
--- a/iotdb-client/client-cpp/test/cpp/sessionIT.cpp
+++ b/iotdb-client/client-cpp/test/cpp/sessionIT.cpp
@@ -390,8 +390,7 @@ TEST_CASE("Session rejects SQL after close",
"[sessionClose]") {
builder.host("127.0.0.1")->rpcPort(6667)->username("root")->password("root")->build();
localSession->open();
localSession->close();
- REQUIRE_THROWS_AS(localSession->executeNonQueryStatement("show databases"),
- IoTDBConnectionException);
+ REQUIRE_THROWS_AS(localSession->executeQueryStatement("SHOW VERSION"),
IoTDBConnectionException);
}
TEST_CASE("Test insertTablet ", "[testInsertTablet]") {
diff --git a/iotdb-client/client-cpp/test/cpp/sessionUtilsTest.cpp
b/iotdb-client/client-cpp/test/cpp/sessionUtilsTest.cpp
index 18047a38868..e8d593c80d9 100644
--- a/iotdb-client/client-cpp/test/cpp/sessionUtilsTest.cpp
+++ b/iotdb-client/client-cpp/test/cpp/sessionUtilsTest.cpp
@@ -25,6 +25,19 @@
using namespace std;
+TEST_CASE("Session query rejects states without an open connection",
"[utils]") {
+ SECTION("before open") {
+ Session session("127.0.0.1", 6667);
+ REQUIRE_THROWS_AS(session.executeQueryStatement("SHOW VERSION"),
IoTDBConnectionException);
+ }
+
+ SECTION("after failed open") {
+ Session session("127.0.0.1", 1);
+ REQUIRE_THROWS_AS(session.open(), IoTDBException);
+ REQUIRE_THROWS_AS(session.executeQueryStatement("SHOW VERSION"),
IoTDBConnectionException);
+ }
+}
+
TEST_CASE("SessionUtils filterNullColumns keeps only non-null FIELD columns",
"[utils]") {
vector<pair<string, TSDataType::TSDataType>> schemas = {{"s1",
TSDataType::INT32},
{"s2",
TSDataType::INT64},
@@ -62,8 +75,9 @@ TEST_CASE("SessionUtils filterNullColumns returns original
when nothing to drop"
REQUIRE(filtered.get() == &tablet);
}
-TEST_CASE("SessionUtils filterNullColumns returns nullptr when tree-model
FIELD columns are all null",
- "[utils]") {
+TEST_CASE(
+ "SessionUtils filterNullColumns returns nullptr when tree-model FIELD
columns are all null",
+ "[utils]") {
vector<pair<string, TSDataType::TSDataType>> schemas = {{"s1",
TSDataType::INT32},
{"s2",
TSDataType::INT64}};
Tablet tablet("root.sg.d1", schemas, 1);
diff --git a/iotdb-client/client-cpp/test/fixtures/tls/ca.crt
b/iotdb-client/client-cpp/test/fixtures/tls/ca.crt
new file mode 100644
index 00000000000..b356ec36632
--- /dev/null
+++ b/iotdb-client/client-cpp/test/fixtures/tls/ca.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDETCCAfmgAwIBAgIUFNsUYuwzkISBNzd0RdZEMLRjhu8wDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNSW9UREIgVGVzdCBDQTAeFw0yNjA3MDMwMzM0MzFaFw0z
+NjA2MzAwMzM0MzFaMBgxFjAUBgNVBAMMDUlvVERCIFRlc3QgQ0EwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCutbmN5+3qN8hGPzIys3XH5sSTnBmXbGNO
+MViLiE8kysCfRMlc4ckHri/EdTsgH+V6mjf0rxuyH2+TkE7kATiYSU+a6EB/N1Fv
+hpkEi7pL8lProdtcyriTTE8PahjdbWnpTe8lNjQFbkhRnQaJr0R8DGEXpVdsAVez
+gcG5lruj0lYzZRIWhVxvSEzKTUnqaO83NcEqaRobTLj2uCmfLo4jLd4OQGf3J94w
+6ayhNfP7U4iQeReheI9YhDjNIgkClVKgmmiyQb0VfE+O/nL1OVOazybkEHXNA8So
+mN8MRafKFCSm+T1t9MBHHbcXp1tZRUHN0x4RjmAU8MPZiGQ55OsBAgMBAAGjUzBR
+MB0GA1UdDgQWBBQWSSihr7kDyU9GS6cQaPz5+vtwhTAfBgNVHSMEGDAWgBQWSSih
+r7kDyU9GS6cQaPz5+vtwhTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA
+A4IBAQCT2TqltOY8slGpF+wB+pBELa1vcnZqajju5OO00uOYvkbKBEZ87wSO4Rag
+idRnKKeKPe3KcvT7DdR2z21hl36pt8neDC1b8OohL4quBcO21t7gUbmxvfcQLijd
+V9wd9sm8TriDIO/mx8ZsDwhu/dupGobboqy0r+C9t5/GsjdL17Kp30st0KffKsSs
+UxCaeL0sMD2tVQvx1a5BRrQl8IrLEzpVWDhVFiWY7iwimMX0c+bI2CcvWb8Hd/Km
+0l8Wt84XAYecWTVMEBLc/T5kj3DGv8S6TkAP4AgMn5Zjv59va+Pt7CoXbxmdjO8T
+agd3Xt8jfmKDNmnQkPmKTo5uuErR
+-----END CERTIFICATE-----
diff --git a/iotdb-client/client-cpp/test/fixtures/tls/client.crt
b/iotdb-client/client-cpp/test/fixtures/tls/client.crt
new file mode 100644
index 00000000000..2d3dbc14fda
--- /dev/null
+++ b/iotdb-client/client-cpp/test/fixtures/tls/client.crt
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICuzCCAaMCFBWG7ViMzmyrCBoJYpbg0zkC6USVMA0GCSqGSIb3DQEBCwUAMBgx
+FjAUBgNVBAMMDUlvVERCIFRlc3QgQ0EwHhcNMjYwNzAzMDMzNDMxWhcNMzYwNjMw
+MDMzNDMxWjAcMRowGAYDVQQDDBFJb1REQiBUZXN0IENsaWVudDCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBANsjPpYWA5e0HKyUxbdoVtdYjtnJegHbHdz0
+I4hbvBDe5ySMdBIEUtNTb/zGmxb0nhkTjIxV9wh3Wb3JVhNJ4oaIclnIjfMWNc0/
+o+j8E+lce1VIV5CfZwiYUI6cilP7H4vkaGrTW14x0LcJgU8BhoQbzk5GzRdVcayc
+h+nDIsTfbMoT6Ag7dq2mS32Iq0F58IFP9ELT8cJ9Ue1mfWE74d+O5P/NtPU2CWdZ
+JXu4yka1Li8Ug3Jq+6I2LmDlBbiq+IjF5kj3iyDIBU34b2WdiOChuhaB4EyhiXf6
+j+nQM7D+N1CCf55AtfJKsiLtA3Dp73uL3OE7yr/e1scHxOG6SOECAwEAATANBgkq
+hkiG9w0BAQsFAAOCAQEAnCt5Ffs8FkKRq8SkFnqLgZX2M0mlfXe8SzQk+dFPX1s+
+/2A+6JkiZ9JniR22uryUt40B3Cq2U5zhsINVlR3voye1F8MjJxEtaIfPTTh8MI2L
+vyAQaIKtBj/VJX+tCiaYyO0tSCrAyBvdzArGcwcr3V0SdPxLzT7q4DrDM9F0uf1x
+dDUgn9inGDBpWXHNgnOLzqM7Xjzs4+vbZSCQBbYY9HTmyvp+NDFmTT8dKC2lvMZH
+Cugw0tTHv2N+wXwx33LUtAPxO5WRCZQ8PhWxJ0lGtV9MMJK2YvNyf9qCHGMiOgX0
+w4q1Gwh6ZTF9Nhsk7gNtDit+bDLb06gtA6oeNI/d8A==
+-----END CERTIFICATE-----
diff --git a/iotdb-client/client-cpp/test/fixtures/tls/client.key
b/iotdb-client/client-cpp/test/fixtures/tls/client.key
new file mode 100644
index 00000000000..1686d990843
--- /dev/null
+++ b/iotdb-client/client-cpp/test/fixtures/tls/client.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDbIz6WFgOXtBys
+lMW3aFbXWI7ZyXoB2x3c9COIW7wQ3uckjHQSBFLTU2/8xpsW9J4ZE4yMVfcId1m9
+yVYTSeKGiHJZyI3zFjXNP6Po/BPpXHtVSFeQn2cImFCOnIpT+x+L5Ghq01teMdC3
+CYFPAYaEG85ORs0XVXGsnIfpwyLE32zKE+gIO3atpkt9iKtBefCBT/RC0/HCfVHt
+Zn1hO+HfjuT/zbT1NglnWSV7uMpGtS4vFINyavuiNi5g5QW4qviIxeZI94sgyAVN
++G9lnYjgoboWgeBMoYl3+o/p0DOw/jdQgn+eQLXySrIi7QNw6e97i9zhO8q/3tbH
+B8ThukjhAgMBAAECggEAH3DoGuqfq1V5Q724vH7o7s7S+CZzLe79UuVob7kRu63v
+pgvM34TlSVLQX4kzWVDRmjF22e+/mORe6N8JTY0tRjYvifg/faAzKfa2ksgQJ0xQ
+mcTeY26rfs0zybJmGnSOayjjXmhi1Jn7Izfm6KoEXdILgKmh5XYp8CUpTv3jcDGG
+MgdlqxG8rakJ4NHtO6qjgbaEAEsI7JbJj0T+7YPPD42KWvy9f9LYUSN0eCO/7TWw
+Cvvl6NX55tz+WwpWdDtIKVjWZRnx4ZA3cZizWGZfSDaoRJWfDTcUKJ4zXHDdxXRL
+6ha0cD7N6HJtQFvrxnyH/Uqpnm9rTKhfHvj5uTmaLQKBgQDdufi+60dIjVzQxYhy
+4w+BUaI5PQCN0naX+uvlUBzlctrImWQyGsLj2yDbo9IuDBU6qaGPC0Sl29ywBf7W
+QxsWbxE/rb9MKO2SEdLRq45W/H/Llr1IV494upWnDpWgvanBeanITveU9HA0/Fm4
+U0PrfExeBXca0dTfAD07Jr3gfwKBgQD9AtTKku/+jXfpQe4IPtZ8rjG6Ezg6KCw9
+JQVwHQaTked82Fj/1F6BiutVQwbQ6UI8FfZ7uF239Cw2O/PI28zpaCtOUPCP5TOI
+A1LdwhJAtogfXK1vSX4qxog4sNwmlboxAMixdSZGuBfO/vUxL6nQb+OH3g3gTqS5
+CjnKAQcmnwKBgQCAkASbLvD2MIFQzDiB5QZohVz6s1RO52m8VdHR9NHMePxCtC5U
+nw/B7pzuvd5wtLDaguEaf/4d7Y3YwqEwu1hJeb0Wnzf8gP6/Y3ZJ/J9b8Kxo785w
+09RsvENpyhsYSODVPiYj7yW/SLyG/ItJRX5sXHYrTh/xfRlg9FKMqboPIQKBgQDz
+0K2kxTKXOFbspocu1Pc20VrEOM8/ZAU1qx5xatcykDDmo0ooxsuHxIqB8IR5/76/
+Tl7n3MQbiCau4NlNn1r5NlQ9NUyNLk+Za7KIVwPl7sCAkHvluYnmyMju8KhGWpVB
+scK1F/KZxb/TzugTzR206o32GWt/0+lzE8KawqDUewKBgCIc9mkWBtyA5Z7qKDmZ
+6yaKs5210GzXGHBccVn6ABzV9BsWh+9r8guT2WxH6q+i4KBmFkRAk1u/AK7o/WOi
+2HdOTMgQe5j9Jxnzr6sOQcSHJLYblKbkzGonJ0eEiZH2qFtjjPIz6lqSiuXK6PPr
+fBH/Y8bZLE6KjrsBiqlzUiqV
+-----END PRIVATE KEY-----
diff --git a/iotdb-client/client-cpp/test/fixtures/tls/tls-server.p12
b/iotdb-client/client-cpp/test/fixtures/tls/tls-server.p12
new file mode 100644
index 00000000000..53ac363590c
Binary files /dev/null and
b/iotdb-client/client-cpp/test/fixtures/tls/tls-server.p12 differ
diff --git a/iotdb-client/client-cpp/src/include/TableSession.h
b/iotdb-client/client-cpp/test/main_rpc_ssl.cpp
similarity index 50%
copy from iotdb-client/client-cpp/src/include/TableSession.h
copy to iotdb-client/client-cpp/test/main_rpc_ssl.cpp
index d1eecfeeaba..ebf4ea96fcd 100644
--- a/iotdb-client/client-cpp/src/include/TableSession.h
+++ b/iotdb-client/client-cpp/test/main_rpc_ssl.cpp
@@ -17,30 +17,5 @@
* under the License.
*/
-// This file is a translation of the Java file
iotdb-client/session/src/main/java/org/apache/iotdb/session/TableSession.java
-
-#ifndef IOTDB_TABLESESSION_H
-#define IOTDB_TABLESESSION_H
-
-#include "Session.h"
-
-class TableSession {
-private:
- std::shared_ptr<Session> session_;
- string getDatabase();
-
-public:
- TableSession(std::shared_ptr<Session> session) {
- this->session_ = session;
- }
- ~TableSession() {}
-
- void insert(Tablet& tablet, bool sorted = false);
- void executeNonQueryStatement(const std::string& sql);
- unique_ptr<SessionDataSet> executeQueryStatement(const std::string& sql);
- unique_ptr<SessionDataSet> executeQueryStatement(const std::string& sql,
int64_t timeoutInMs);
- void open(bool enableRPCCompression = false);
- void close();
-};
-
-#endif // IOTDB_TABLESESSION_H
\ No newline at end of file
+#define CATCH_CONFIG_MAIN
+#include <catch.hpp>
diff --git a/iotdb-client/client-cpp/test/scripts/configure_iotdb_ssl_it.py
b/iotdb-client/client-cpp/test/scripts/configure_iotdb_ssl_it.py
new file mode 100644
index 00000000000..fcd8d193a4f
--- /dev/null
+++ b/iotdb-client/client-cpp/test/scripts/configure_iotdb_ssl_it.py
@@ -0,0 +1,117 @@
+#!/usr/bin/env python3
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+"""Configure an IoTDB distribution for C++ TLS integration tests."""
+
+from __future__ import annotations
+
+import re
+import shutil
+import subprocess
+import sys
+from pathlib import Path
+
+STORE_PASSWORD = "thrift"
+
+
+def replace_property(text: str, key: str, value: str) -> str:
+ pattern = re.compile(rf"^{re.escape(key)}=.*$", re.MULTILINE)
+ replacement = f"{key}={value}"
+ if pattern.search(text):
+ return pattern.sub(replacement, text, count=1)
+ return text.rstrip() + "\n" + replacement + "\n"
+
+
+def configure(dist_root: Path, fixtures_root: Path, mode: str) -> None:
+ properties = dist_root / "conf" / "iotdb-system.properties"
+ if mode == "plain":
+ text = properties.read_text(encoding="utf-8")
+ for key, value in {
+ "dn_rpc_address": "127.0.0.1",
+ "enable_thrift_ssl": "false",
+ "thrift_ssl_client_auth": "false",
+ "key_store_path": "",
+ "key_store_pwd": "",
+ "trust_store_path": "",
+ "trust_store_pwd": "",
+ "ssl_protocol": "TLS",
+ }.items():
+ text = replace_property(text, key, value)
+ properties.write_text(text, encoding="utf-8", newline="\n")
+ return
+
+ mutual_tls = mode == "mtls"
+ ssl_dir = dist_root / "conf" / "cpp-ssl-it"
+ ssl_dir.mkdir(parents=True, exist_ok=True)
+
+ server_store = ssl_dir / "tls-server.p12"
+ shutil.copy2(fixtures_root / "tls" / "tls-server.p12", server_store)
+
+ trust_store = ""
+ if mutual_tls:
+ trust_store_path = ssl_dir / "tls-server-trust.p12"
+ trust_store_path.unlink(missing_ok=True)
+ subprocess.run(
+ [
+ "keytool",
+ "-importcert",
+ "-noprompt",
+ "-alias",
+ "cpp-ssl-it-ca",
+ "-file",
+ str(fixtures_root / "tls" / "ca.crt"),
+ "-keystore",
+ str(trust_store_path),
+ "-storetype",
+ "PKCS12",
+ "-storepass",
+ STORE_PASSWORD,
+ ],
+ check=True,
+ )
+ trust_store = trust_store_path.as_posix()
+
+ text = properties.read_text(encoding="utf-8")
+ settings = {
+ "dn_rpc_address": "localhost",
+ "enable_thrift_ssl": "true",
+ "thrift_ssl_client_auth": str(mutual_tls).lower(),
+ "key_store_path": server_store.as_posix(),
+ "key_store_pwd": STORE_PASSWORD,
+ "trust_store_path": trust_store,
+ "trust_store_pwd": STORE_PASSWORD if mutual_tls else "",
+ "ssl_protocol": "TLS",
+ }
+ for key, value in settings.items():
+ text = replace_property(text, key, value)
+ properties.write_text(text, encoding="utf-8", newline="\n")
+
+
+def main() -> int:
+ if len(sys.argv) != 4 or sys.argv[3] not in ("plain", "tls", "mtls"):
+ print(
+ "usage: configure_iotdb_ssl_it.py <dist-root> <fixtures-root>
<plain|tls|mtls>",
+ file=sys.stderr,
+ )
+ return 2
+ configure(Path(sys.argv[1]).resolve(), Path(sys.argv[2]).resolve(),
sys.argv[3])
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/iotdb-client/client-cpp/test/scripts/run_cpp_it_phases.py
b/iotdb-client/client-cpp/test/scripts/run_cpp_it_phases.py
new file mode 100644
index 00000000000..ee342397d3a
--- /dev/null
+++ b/iotdb-client/client-cpp/test/scripts/run_cpp_it_phases.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python3
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+"""Run C++ client tests against plain TLS and mutual TLS IoTDB servers."""
+
+from __future__ import annotations
+
+import argparse
+import os
+import socket
+import subprocess
+import sys
+import time
+from pathlib import Path
+
+
+def run(
+ command: list[str],
+ cwd: Path,
+ env: dict[str, str] | None = None,
+ shell: bool = False,
+) -> None:
+ print(f"+ {' '.join(command)}", flush=True)
+ subprocess.run(command, cwd=cwd, env=env, shell=shell, check=True)
+
+
+def server_script(dist_root: Path, action: str) -> Path:
+ if sys.platform == "win32":
+ return dist_root / "sbin" / "windows" / f"{action}-standalone.bat"
+ return dist_root / "sbin" / f"{action}-standalone.sh"
+
+
+def stop_server(dist_root: Path, env: dict[str, str]) -> None:
+ subprocess.run(
+ [str(server_script(dist_root, "stop"))],
+ cwd=dist_root,
+ env=env,
+ shell=sys.platform == "win32",
+ check=False,
+ )
+ time.sleep(10)
+
+
+def wait_for_rpc_port(timeout_seconds: int) -> None:
+ deadline = time.monotonic() + timeout_seconds
+ consecutive_successes = 0
+ while time.monotonic() < deadline:
+ try:
+ with socket.create_connection(("127.0.0.1", 6667), timeout=1):
+ consecutive_successes += 1
+ if consecutive_successes == 3:
+ return
+ except OSError:
+ consecutive_successes = 0
+ time.sleep(1)
+ raise TimeoutError(f"IoTDB RPC port did not become ready within
{timeout_seconds} seconds")
+
+
+def start_server(dist_root: Path, wait_seconds: int, env: dict[str, str]) ->
None:
+ run(
+ [str(server_script(dist_root, "start"))],
+ dist_root,
+ env=env,
+ shell=sys.platform == "win32",
+ )
+ wait_for_rpc_port(wait_seconds)
+
+
+def main() -> int:
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument("build_dir", type=Path)
+ parser.add_argument("dist_root", type=Path)
+ parser.add_argument("fixtures_root", type=Path)
+ parser.add_argument("--config", default="Release")
+ parser.add_argument("--wait-seconds", type=int, default=120)
+ parser.add_argument(
+ "--modes",
+ nargs="+",
+ choices=("plain", "tls", "mtls"),
+ default=("plain", "tls", "mtls"),
+ )
+ args = parser.parse_args()
+
+ build_dir = args.build_dir.resolve()
+ dist_root = args.dist_root.resolve()
+ fixtures_root = args.fixtures_root.resolve()
+ configure_script = Path(__file__).with_name("configure_iotdb_ssl_it.py")
+ ctest = ["ctest", "--output-on-failure", "-C", args.config]
+ server_env = os.environ.copy()
+ if sys.platform == "win32":
+ # Node scripts pause after the Java process exits so an interactive
console
+ # stays open. In CI those paused cmd.exe children keep the job's
standard
+ # handles open after the tests finish, preventing Maven from returning.
+ server_env["IOTDB_NO_PAUSE"] = "1"
+
+ for mode in args.modes:
+ stop_server(dist_root, server_env)
+ run(
+ [sys.executable, str(configure_script), str(dist_root),
str(fixtures_root), mode],
+ configure_script.parent,
+ )
+ try:
+ start_server(dist_root, args.wait_seconds, server_env)
+ env = server_env.copy()
+ if mode == "mtls":
+ env["IOTDB_CPP_SSL_MUTUAL_AUTH"] = "1"
+ label = {"plain": "plain", "tls": "tls-only", "mtls":
"mutual-auth"}[mode]
+ run(ctest + ["-L", label], build_dir, env)
+ finally:
+ stop_server(dist_root, server_env)
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/iotdb-client/client-cpp/third-party/README.md
b/iotdb-client/client-cpp/third-party/README.md
index a430adc30c5..1817c0fe11f 100644
--- a/iotdb-client/client-cpp/third-party/README.md
+++ b/iotdb-client/client-cpp/third-party/README.md
@@ -68,8 +68,8 @@ Alternatively copy files manually from the URLs listed in
| Platform | Typical files |
|------------|---------------|
-| `linux/` | `thrift-0.24.0.tar.gz`, `boost_1_60_0.tar.gz`,
`m4-1.4.19.tar.gz`, `flex-2.6.4.tar.gz`, `bison-3.8.tar.gz` (+
`openssl-3.5.0.tar.gz` only when `WITH_SSL=ON` and no system OpenSSL is
present) |
-| `mac/` | `thrift-0.24.0.tar.gz`, `boost_1_60_0.tar.gz` (Xcode CLT
usually provides m4/flex/bison) |
-| `windows/` | `thrift-0.24.0.tar.gz`, `boost_1_60_0.tar.gz`,
`win_flex_bison-2.5.25.zip` (or any `win_flex_bison*.zip`; skip if flex/bison
already on `PATH`) |
+| `linux/` | `thrift-0.24.0.tar.gz`, `boost_1_60_0.tar.gz`,
`m4-1.4.19.tar.gz`, `flex-2.6.4.tar.gz`, `bison-3.8.tar.gz`,
`openssl-3.5.8.tar.gz` when `WITH_SSL=ON` |
+| `mac/` | `thrift-0.24.0.tar.gz`, `boost_1_60_0.tar.gz`,
`openssl-3.5.8.tar.gz` (Xcode CLT usually provides m4/flex/bison) |
+| `windows/` | `thrift-0.24.0.tar.gz`, `boost_1_60_0.tar.gz`,
`openssl-3.5.8.tar.gz`, `win_flex_bison-2.5.25.zip` (or any
`win_flex_bison*.zip`; skip if flex/bison already on `PATH`) |
Download URLs: see the *Offline build* table in [`README.md`](../README.md).
diff --git a/pom.xml b/pom.xml
index 8a4e3dda50b..e23bf05a485 100644
--- a/pom.xml
+++ b/pom.xml
@@ -766,6 +766,10 @@
<!-- bundled third-party NOTICE / license texts
for the C++ client package -->
<exclude>**/package-metadata/third_party/NOTICE</exclude>
<exclude>**/package-metadata/third_party/licenses/**</exclude>
+ <!-- generated certificates and private keys used
only by C++ TLS tests -->
+ <exclude>**/client-cpp/test/fixtures/**</exclude>
+ <exclude>test/fixtures/**/*.crt</exclude>
+ <exclude>test/fixtures/**/*.key</exclude>
<!-- bundled third-party NOTICE / license texts
for the AINode package -->
<exclude>**/ainode/src/assembly/resources/NOTICE-binary</exclude>
<exclude>**/ainode/src/assembly/resources/licenses/**</exclude>
diff --git a/scripts/sbin/windows/start-confignode.bat
b/scripts/sbin/windows/start-confignode.bat
index 3cedfe3c6a9..d6b68aecaa3 100644
--- a/scripts/sbin/windows/start-confignode.bat
+++ b/scripts/sbin/windows/start-confignode.bat
@@ -174,8 +174,8 @@ goto finally
:err
echo JAVA_HOME environment variable must be set!
-pause
+if not defined IOTDB_NO_PAUSE pause
:finally
@ENDLOCAL
-pause
+if not defined IOTDB_NO_PAUSE pause
diff --git a/scripts/sbin/windows/start-datanode.bat
b/scripts/sbin/windows/start-datanode.bat
index 761641c22fc..20675e362c3 100755
--- a/scripts/sbin/windows/start-datanode.bat
+++ b/scripts/sbin/windows/start-datanode.bat
@@ -217,10 +217,10 @@ goto finally
:err
echo JAVA_HOME environment variable must be set!
-pause
+if not defined IOTDB_NO_PAUSE pause
@REM
-----------------------------------------------------------------------------
:finally
@ENDLOCAL
-pause
+if not defined IOTDB_NO_PAUSE pause
diff --git a/scripts/sbin/windows/start-standalone.bat
b/scripts/sbin/windows/start-standalone.bat
index 9020539b8b9..abba8fc4eb6 100644
--- a/scripts/sbin/windows/start-standalone.bat
+++ b/scripts/sbin/windows/start-standalone.bat
@@ -38,7 +38,12 @@ IF EXIST "%IOTDB_HOME%\sbin\windows\start-datanode.bat" (
)
start cmd /c %CONFIGNODE_START_PATH%
-TIMEOUT /T 5 /NOBREAK
+if defined IOTDB_NO_PAUSE (
+ @REM TIMEOUT fails immediately when CI redirects stdin, so use a
non-interactive delay.
+ ping 127.0.0.1 -n 6 >NUL
+) ELSE (
+ TIMEOUT /T 5 /NOBREAK
+)
start cmd /c %DATANODE_START_PATH%
@REM if you have turned on "-XX:+SafepointTimeout" and
"-XX:SafepointTimeoutDelay=1000", you can use commands below instead to see
safepoint logs
@REM SET LOG_SAFEPOINT_PATH=%IOTDB_HOME%\logs\log_datanode_safepoint.log