This is an automated email from the ASF dual-hosted git repository.
phrocker pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nifi-minifi-cpp.git
The following commit(s) were added to refs/heads/master by this push:
new 97a05c1 MINIFICPP-814 - Fixed ListenHTTP and HTTPClient bugs, created
tests for ListenHTTP
97a05c1 is described below
commit 97a05c1bdb6bfd40f5f33da01cf0893060350ef4
Author: Daniel Bakai <[email protected]>
AuthorDate: Tue Aug 27 09:50:15 2019 +0200
MINIFICPP-814 - Fixed ListenHTTP and HTTPClient bugs, created tests for
ListenHTTP
This fixes the following ListenHTTP issues:
- HTTPS did not work
- SSL Minimum Version enforcement caused hanging client and leaked fds in
the server
- HEAD requests were not served properly
- random port did not work with HTTPS
This fixes the following HTTPClient issues:
- PUT did not work
- HEAD did not work
- POST using setPostFields was a potential use-after-free
- cURL SSL backend was erroneously deduced
- header parsing was faulty
This adds the following features:
- OpenSSL and cURL are now always built, system versions are not supported
- logging for CivetWeb
- logging for cURL
- tests for ListenHTTP
This closes #610.
MINIFICPP-814 - Windows fixes
Signed-off-by: Marc Parisi <[email protected]>
---
CMakeLists.txt | 74 +--
CMakeSettings.json | 8 -
README.md | 4 +-
bootstrap.sh | 15 -
centos.sh | 8 +-
cmake/LibSSH2.cmake | 16 +-
darwin.sh | 12 +-
debian.sh | 6 +-
extensions/civetweb/processors/ListenHTTP.cpp | 31 +-
extensions/civetweb/processors/ListenHTTP.h | 42 +-
extensions/civetweb/tests/CMakeLists.txt | 56 +++
extensions/civetweb/tests/ListenHTTPTests.cpp | 538 +++++++++++++++++++++
.../civetweb/tests/resources/badCA_goodClient.p12 | Bin 0 -> 2413 bytes
.../civetweb/tests/resources/badCA_goodClient.pem | 46 ++
extensions/civetweb/tests/resources/generate.sh | 53 ++
extensions/civetweb/tests/resources/goodCA.crt | 20 +
.../civetweb/tests/resources/goodCA_badClient.p12 | Bin 0 -> 2413 bytes
.../civetweb/tests/resources/goodCA_badClient.pem | 46 ++
.../civetweb/tests/resources/goodCA_goodClient.p12 | Bin 0 -> 2413 bytes
.../civetweb/tests/resources/goodCA_goodClient.pem | 46 ++
extensions/civetweb/tests/resources/server.p12 | Bin 0 -> 2405 bytes
extensions/civetweb/tests/resources/server.pem | 46 ++
extensions/http-curl/CMakeLists.txt | 5 -
extensions/http-curl/client/HTTPClient.cpp | 140 +++---
extensions/http-curl/client/HTTPClient.h | 26 +-
extensions/http-curl/tests/CMakeLists.txt | 7 +-
extensions/http-curl/tests/unit/CivetwebTests.cpp | 115 -----
.../http-curl/tests/unit/HTTPClientTests.cpp | 96 ++++
extensions/librdkafka/CMakeLists.txt | 4 +-
extensions/sftp/CMakeLists.txt | 5 -
.../standard-processors/tests/CMakeLists.txt | 5 +
fedora.sh | 7 +-
libminifi/include/controllers/SSLContextService.h | 52 +-
libminifi/include/utils/HTTPClient.h | 91 +++-
libminifi/src/controllers/SSLContextService.cpp | 109 ++++-
libminifi/test/resources/cn.p12 | Bin 0 -> 2437 bytes
libminifi/test/unit/SocketTests.cpp | 6 +-
main/CMakeLists.txt | 14 +-
rheldistro.sh | 8 +-
suse.sh | 7 +-
thirdparty/civetweb-1.10/src/civetweb.c | 9 +-
41 files changed, 1365 insertions(+), 408 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index cee2f23..2023622 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -34,17 +34,18 @@ option(PORTABLE "Instructs the compiler to remove
architecture specific optimiza
option(USE_SHARED_LIBS "Builds using shared libraries" ON)
option(ENABLE_PYTHON "Instructs the build system to enable building shared
objects for the python lib" OFF)
cmake_dependent_option(STATIC_BUILD "Attempts to statically link as many
dependencies as possible." ON "NOT ENABLE_PYTHON; NOT USE_SHARED_LIBS" OFF)
-cmake_dependent_option(USE_SYSTEM_OPENSSL "Instructs the build system to
search for and use an SSL library available in the host system" ON "NOT
STATIC_BUILD" OFF)
option(LIBC_STATIC "Instructs the build system to statically link libstdc++
and glibc into minifiexe. Experiemental" OFF)
option(OPENSSL_OFF "Disables OpenSSL" OFF)
option(ENABLE_OPS "Enable Operations/zlib Tools" ON)
option(USE_SYSTEM_UUID "Instructs the build system to search for and use a
UUID library available in the host system" OFF)
option(ENABLE_JNI "Instructs the build system to enable the JNI extension" OFF)
option(ENABLE_OPENCV "Instructs the build system to enable the OpenCV
extension" OFF)
-cmake_dependent_option(USE_SYSTEM_CURL "Instructs the build system to search
for and use a cURL library available in the host system" ON "NOT STATIC_BUILD"
OFF)
option(BUILD_SHARED_LIBS "Build yaml cpp shared lib" OFF)
+
cmake_dependent_option(USE_SYSTEM_ZLIB "Instructs the build system to search
for and use a zlib library available in the host system" ON "NOT STATIC_BUILD"
OFF)
+
cmake_dependent_option(USE_SYSTEM_LIBSSH2 "Instructs the build system to
search for and use a libssh2 library available in the host system" OFF "NOT
STATIC_BUILD" OFF)
+
option(USE_SYSTEM_BZIP2 "Instructs the build system to search for and use a
bzip2 library available in the host system" ON)
option(BUILD_ROCKSDB "Instructs the build system to use RocksDB from the third
party directory" ON)
option(FORCE_WINDOWS "Instructs the build system to force Windows builds when
WIN32 is specified" OFF)
@@ -192,21 +193,9 @@ endif()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
if (NOT OPENSSL_OFF)
- if(USE_SYSTEM_OPENSSL)
-
- # Set the right openssl root path
- if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
- set(OPENSSL_ROOT_DIR "/usr/local/opt/openssl/")
- elseif(NOT WIN32)
- set(OPENSSL_ROOT_DIR "/usr/lib/x86_64-linux-gnu")
- else()
- #set(OPENSSL_ROOT_DIR "/usr/lib/x86_64-linux-gnu")
- endif()
- else()
- include(LibreSSL)
- use_libre_ssl("${CMAKE_CURRENT_SOURCE_DIR}"
"${CMAKE_CURRENT_BINARY_DIR}")
- list(APPEND CMAKE_MODULE_PATH
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/ssl")
- endif()
+ include(LibreSSL)
+ use_libre_ssl("${CMAKE_CURRENT_SOURCE_DIR}"
"${CMAKE_CURRENT_BINARY_DIR}")
+ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/ssl")
find_package (OpenSSL REQUIRED)
@@ -216,9 +205,8 @@ if (NOT OPENSSL_OFF)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DOPENSSL_SUPPORT")
MESSAGE("OpenSSL found at ${OPENSSL_LIBRARIES}")
else ()
- message( FATAL_ERROR "OpenSSL was not found. Please install OpenSSL" )
+ message( FATAL_ERROR "OpenSSL was not found." )
endif (OPENSSL_FOUND)
-
else()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/winssl")
endif()
@@ -280,9 +268,9 @@ if (DISABLE_CURL)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDISABLE_CURL")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DDISABLE_CURL")
endif(DISABLE_CURL)
-if(NOT DISABLE_CURL AND (NOT USE_SYSTEM_CURL))
+if(NOT DISABLE_CURL)
message("Using bundled cURL")
-
+
set(CURL_C_FLAGS "${CMAKE_C_FLAGS}")
set(CURL_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
@@ -317,6 +305,7 @@ endif()
-DBUILD_TESTING=OFF
-DBUILD_SHARED_LIBS=OFF
-DHTTP_ONLY=ON
+ -DCMAKE_USE_OPENSSL=ON
"-DLIBRESSL_BIN_DIR=${LIBRESSL_BIN_DIR}"
"-DLIBRESSL_SRC_DIR=${LIBRESSL_SRC_DIR}"
-DCURL_DISABLE_CRYPTO_AUTH=ON
@@ -337,9 +326,7 @@ endif()
BUILD_BYPRODUCTS
"${CMAKE_CURRENT_BINARY_DIR}/thirdparty/curl-install/${BYPRODUCT}"
)
- if(NOT USE_SYSTEM_OPENSSL OR USE_SYSTEM_OPENSSL STREQUAL "OFF")
- add_dependencies(curl-external libressl-portable)
- endif()
+ add_dependencies(curl-external libressl-portable)
set(CURL_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/curl/" CACHE STRING
"" FORCE)
set(CURL_BIN_DIR "${CMAKE_CURRENT_BINARY_DIR}/thirdparty/curl-install/"
CACHE STRING "" FORCE)
@@ -375,7 +362,10 @@ set(CIVETWEB_ENABLE_CXX ON CACHE BOOL "Enable civet C++
library")
set(CIVETWEB_SSL_SSL_LIB "${OPENSSL_SSL_LIBRARY}" CACHE STRING "Enable civet
C++ library")
set(CIVETWEB_SSL_CRYPTO_LIB "${OPENSSL_CRYPTO_LIBRARY}" CACHE STRING "Enable
civet C++ library")
set(CIVETWEB_ENABLE_CXX ON CACHE BOOL "Enable civet C++ library")
-set(CIVETWEB_ENABLE_SSL ON CACHE BOOL "DISABLE SSL")
+if (NOT OPENSSL_FOUND)
+ message("SSL support disabled, ListenHTTP will not have HTTPS support")
+ set(CIVETWEB_ENABLE_SSL OFF CACHE BOOL "DISABLE SSL")
+endif()
SET(WITH_TOOLS OFF CACHE BOOL "Do not build RocksDB tools")
if ( NOT APPLE)
@@ -403,13 +393,8 @@ endif()
add_subdirectory(libminifi)
-if(NOT USE_SYSTEM_OPENSSL OR USE_SYSTEM_OPENSSL STREQUAL "OFF")
- add_dependencies(minifi libressl-portable)
-endif()
-
-if(NOT USE_SYSTEM_CURL)
- add_dependencies(minifi curl-external)
-endif()
+add_dependencies(minifi libressl-portable)
+add_dependencies(minifi curl-external)
if (WIN32 OR NOT USE_SYSTEM_ZLIB)
add_dependencies(minifi zlib-external)
@@ -421,29 +406,24 @@ createExtension(STANDARD-PROCESSORS "STANDARD PROCESSORS"
"Provides standard pro
#### EXTENSIONS
if ((DISABLE_CURL STREQUAL "OFF" OR NOT DISABLE_CURL) AND NOT DISABLE_CIVET)
createExtension(HTTP-CURL "HTTP CURL" "This enables RESTProtocol,
InvokeHTTP, and the HTTPClient for Site to Site" "extensions/http-curl"
"extensions/http-curl/tests/")
- if(NOT USE_SYSTEM_CURL)
- message("minifi-http-curl will depend on curl-external")
- add_dependencies(minifi-http-curl curl-external)
- endif()
+ message("minifi-http-curl will depend on curl-external")
+ add_dependencies(minifi-http-curl curl-external)
endif()
option(DISABLE_EXPRESSION_LANGUAGE "Disables the scripting extensions." OFF)
if (NOT DISABLE_EXPRESSION_LANGUAGE)
createExtension(EXPRESSION-LANGUAGE-EXTENSIONS "EXPRESSION LANGUAGE
EXTENSIONS" "This enables NiFi expression language"
"extensions/expression-language" "extensions/expression-language/tests")
- if(NOT USE_SYSTEM_OPENSSL OR USE_SYSTEM_OPENSSL STREQUAL "OFF")
- add_dependencies(minifi-expression-language-extensions
libressl-portable)
- endif()
- if(NOT USE_SYSTEM_CURL)
- message("minifi-expression-language-extensions will depend on
curl-external")
- add_dependencies(minifi-expression-language-extensions
curl-external)
- endif()
+ add_dependencies(minifi-expression-language-extensions
libressl-portable)
+
+ message("minifi-expression-language-extensions will depend on
curl-external")
+ add_dependencies(minifi-expression-language-extensions curl-external)
endif()
option(DISABLE_CIVET "Disables CivetWeb components." OFF)
if (NOT DISABLE_CIVET)
- createExtension(CIVETWEB CIVETWEB "This enables ListenHTTP"
"extensions/civetweb")
+ createExtension(CIVETWEB CIVETWEB "This enables ListenHTTP"
"extensions/civetweb" "extensions/civetweb/tests")
endif()
## Add the rocks DB extension
@@ -575,10 +555,8 @@ if ((ENABLE_ALL OR ENABLE_SFTP) AND NOT DISABLE_CURL)
message("minifi-sftp will depend on libssh2-external")
add_dependencies(minifi-sftp libssh2-external)
endif()
- if(NOT USE_SYSTEM_CURL)
- message("minifi-sftp will depend on curl-external")
- add_dependencies(minifi-sftp curl-external)
- endif()
+ message("minifi-sftp will depend on curl-external")
+ add_dependencies(minifi-sftp curl-external)
if(NOT USE_SYSTEM_ZLIB)
message("minifi-sftp will depend on zlib-external")
add_dependencies(minifi-sftp zlib-external)
diff --git a/CMakeSettings.json b/CMakeSettings.json
index f430bb4..880052f 100644
--- a/CMakeSettings.json
+++ b/CMakeSettings.json
@@ -16,10 +16,6 @@
"value": "OFF"
},
{
- "name": "USE_SYSTEM_OPENSSL",
- "value": "OFF"
- },
- {
"name": "PORTABLE",
"value": "ON"
},
@@ -32,10 +28,6 @@
"value": "TRUE"
},
{
- "name": "USE_SYSTEM_CURL",
- "value": "OFF"
- },
- {
"name": "USE_SYSTEM_UUID",
"value": "OFF"
},
diff --git a/README.md b/README.md
index 3fcf676..5e3de14 100644
--- a/README.md
+++ b/README.md
@@ -138,7 +138,7 @@ and rebuild.
#### Libraries / Development Headers
* libboost and boost-devel
* 1.48.0 or greater
-* libcurl-openssl (If not available or desired, NSS will be used by applying
-DUSE_CURL_NSS)
+* libcurl-openssl (If not available or desired, NSS will be used)
* librocksdb4.1 and librocksdb-dev
* libuuid and uuid-dev
* openssl
@@ -200,7 +200,7 @@ On all distributions please use -DUSE_SHARED_LIBS=OFF to
statically link zlib, l
#### Libraries
* libuuid
* librocksdb *** IF NOT INSTALLED, WILL BE BUILT FROM THIRD PARTY DIRECTORY ***
-* libcurl-openssl (If not available or desired, NSS will be used by applying
-DUSE_CURL_NSS)
+* libcurl-openssl (If not available or desired, NSS will be used)
* libssl and libcrypto from openssl
* libarchive
* librdkafka
diff --git a/bootstrap.sh b/bootstrap.sh
index f69d5ed..8d8fc77 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -243,8 +243,6 @@ add_cmake_option BUILD_ROCKSDB ${TRUE}
add_enabled_option ROCKSDB_ENABLED ${TRUE} "DISABLE_ROCKSDB"
## need libcurl installed
add_enabled_option HTTP_CURL_ENABLED ${TRUE} "DISABLE_CURL"
-add_dependency HTTP_CURL_ENABLED "libcurl"
-add_dependency HTTP_CURL_ENABLED "openssl"
# third party directory
add_enabled_option LIBARCHIVE_ENABLED ${TRUE} "DISABLE_LIBARCHIVE"
@@ -291,8 +289,6 @@ add_disabled_option OPENCV_ENABLED ${FALSE} "ENABLE_OPENCV"
add_disabled_option SFTP_ENABLED ${FALSE} "ENABLE_SFTP"
add_dependency SFTP_ENABLED "libssh2"
-add_dependency SFTP_ENABLED "libcurl"
-add_dependency SFTP_ENABLED "openssl"
TESTS_DISABLED=${FALSE}
@@ -468,17 +464,6 @@ build_cmake_command(){
add_os_flags
- curl -V | grep OpenSSL &> /dev/null
- if [ $? == 0 ]; then
- echo "Using libcurl-openssl..."
- else
- if [ "${USE_SHARED_LIBS}" = "${TRUE}" ]; then
- CMAKE_BUILD_COMMAND="${CMAKE_BUILD_COMMAND} -DUSE_CURL_NSS=true .."
- else
- echo "Using LibreSSL..."
- fi
- fi
-
CMAKE_BUILD_COMMAND="${CMAKE_BUILD_COMMAND} .."
continue_with_plan="Y"
diff --git a/centos.sh b/centos.sh
index da04c76..c3b9d77 100644
--- a/centos.sh
+++ b/centos.sh
@@ -122,14 +122,8 @@ build_deps(){
VALUE=${cmake_opt#*:}
if [ "$KEY" = "$option" ]; then
FOUND_VALUE="$VALUE"
- if [ "$FOUND_VALUE" = "libcurl" ]; then
- INSTALLED+=("libcurl-devel")
- elif [ "$FOUND_VALUE" = "libpcap" ]; then
+ if [ "$FOUND_VALUE" = "libpcap" ]; then
INSTALLED+=("libpcap-devel")
- elif [ "$FOUND_VALUE" = "openssl" ]; then
- INSTALLED+=("openssl")
- INSTALLED+=("openssl-devel")
- INSTALLED+=("openssl-static")
elif [ "$FOUND_VALUE" = "libusb" ]; then
install_libusb
elif [ "$FOUND_VALUE" = "libpng" ]; then
diff --git a/cmake/LibSSH2.cmake b/cmake/LibSSH2.cmake
index 134ec79..f49d8ca 100644
--- a/cmake/LibSSH2.cmake
+++ b/cmake/LibSSH2.cmake
@@ -38,13 +38,11 @@ function(use_bundled_libssh2 SOURCE_DIR BINARY_DIR)
-DBUILD_TESTING=OFF
-DBUILD_EXAMPLES=OFF)
- if(NOT USE_SYSTEM_OPENSSL OR USE_SYSTEM_OPENSSL STREQUAL "OFF")
- list(APPEND CMAKE_MODULE_PATH_PASSTHROUGH_LIST
${CMAKE_CURRENT_SOURCE_DIR}/cmake/ssl)
- list(APPEND LIBSSH2_CMAKE_ARGS "-DLIBRESSL_BIN_DIR=${LIBRESSL_BIN_DIR}"
- "-DLIBRESSL_SRC_DIR=${LIBRESSL_SRC_DIR}"
- "-DBYPRODUCT_PREFIX=${BYPRODUCT_PREFIX}"
- "-DBYPRODUCT_SUFFIX=${BYPRODUCT_SUFFIX}")
- endif()
+ list(APPEND CMAKE_MODULE_PATH_PASSTHROUGH_LIST
${CMAKE_CURRENT_SOURCE_DIR}/cmake/ssl)
+ list(APPEND LIBSSH2_CMAKE_ARGS "-DLIBRESSL_BIN_DIR=${LIBRESSL_BIN_DIR}"
+ "-DLIBRESSL_SRC_DIR=${LIBRESSL_SRC_DIR}"
+ "-DBYPRODUCT_PREFIX=${BYPRODUCT_PREFIX}"
+ "-DBYPRODUCT_SUFFIX=${BYPRODUCT_SUFFIX}")
if(NOT USE_SYSTEM_ZLIB OR USE_SYSTEM_ZLIB STREQUAL "OFF")
list(APPEND CMAKE_MODULE_PATH_PASSTHROUGH_LIST
${CMAKE_CURRENT_SOURCE_DIR}/cmake/zlib/dummy)
list(APPEND LIBSSH2_CMAKE_ARGS
"-DZLIB_BYPRODUCT_INCLUDE=${ZLIB_BYPRODUCT_INCLUDE}"
@@ -65,9 +63,7 @@ function(use_bundled_libssh2 SOURCE_DIR BINARY_DIR)
BUILD_BYPRODUCTS
"${CMAKE_CURRENT_BINARY_DIR}/thirdparty/libssh2-install/${BYPRODUCT}"
)
- if(NOT USE_SYSTEM_OPENSSL OR USE_SYSTEM_OPENSSL STREQUAL "OFF")
- add_dependencies(libssh2-external libressl-portable)
- endif()
+ add_dependencies(libssh2-external libressl-portable)
if(NOT USE_SYSTEM_ZLIB OR USE_SYSTEM_ZLIB STREQUAL "OFF")
add_dependencies(libssh2-external z)
endif()
diff --git a/darwin.sh b/darwin.sh
index 1f50956..da5df01 100644
--- a/darwin.sh
+++ b/darwin.sh
@@ -79,12 +79,8 @@ build_deps(){
VALUE=${cmake_opt#*:}
if [ "$KEY" = "$option" ]; then
FOUND_VALUE="$VALUE"
- if [ "$FOUND_VALUE" = "libcurl" ]; then
- brew install curl-openssl
- elif [ "$FOUND_VALUE" = "libpcap" ]; then
+ if [ "$FOUND_VALUE" = "libpcap" ]; then
INSTALLED+=("libpcap")
- elif [ "$FOUND_VALUE" = "openssl" ]; then
- INSTALLED+=("openssl")
elif [ "$FOUND_VALUE" = "libusb" ]; then
INSTALLED+=("libusb")
elif [ "$FOUND_VALUE" = "libpng" ]; then
@@ -126,10 +122,4 @@ build_deps(){
echo "Ensuring you have all dependencies installed..."
${COMMAND} > /dev/null 2>&1
-
- for option in "${INSTALLED[@]}" ; do
- if [ "$option" = "curl" ]; then
- brew link curl --force > /dev/null 2>&1
- fi
- done
}
diff --git a/debian.sh b/debian.sh
index db3f9b8..bb68ef0 100644
--- a/debian.sh
+++ b/debian.sh
@@ -50,12 +50,8 @@ build_deps(){
VALUE=${cmake_opt#*:}
if [ "$KEY" = "$option" ]; then
FOUND_VALUE="$VALUE"
- if [ "$FOUND_VALUE" = "libcurl" ]; then
- INSTALLED+=("libcurl4-openssl-dev")
- elif [ "$FOUND_VALUE" = "libpcap" ]; then
+ if [ "$FOUND_VALUE" = "libpcap" ]; then
INSTALLED+=("libpcap-dev")
- elif [ "$FOUND_VALUE" = "openssl" ]; then
- INSTALLED+=("openssl")
elif [ "$FOUND_VALUE" = "libusb" ]; then
INSTALLED+=("libusb-1.0-0-dev")
INSTALLED+=("libusb-dev")
diff --git a/extensions/civetweb/processors/ListenHTTP.cpp
b/extensions/civetweb/processors/ListenHTTP.cpp
index a6c2ef7..b065230 100644
--- a/extensions/civetweb/processors/ListenHTTP.cpp
+++ b/extensions/civetweb/processors/ListenHTTP.cpp
@@ -200,7 +200,7 @@ void ListenHTTP::onSchedule(core::ProcessContext *context,
core::ProcessSessionF
}
}
- server_.reset(new CivetServer(options));
+ server_.reset(new CivetServer(options, &callbacks_, &logger_));
handler_.reset(new Handler(basePath, context, sessionFactory,
std::move(authDNPattern), std::move(headersAsAttributesPattern)));
server_->addHandler(basePath, handler_.get());
@@ -209,7 +209,11 @@ void ListenHTTP::onSchedule(core::ProcessContext *context,
core::ProcessSessionF
if (vec.size() != 1) {
logger_->log_error("Random port is set, but there is no listening port!
Server most probably failed to start!");
} else {
+ bool is_secure = isSecure();
listeningPort = std::to_string(vec[0]);
+ if (is_secure) {
+ listeningPort += "s";
+ }
logger_->log_info("Listening on port %s", listeningPort);
}
}
@@ -390,7 +394,25 @@ bool ListenHTTP::Handler::handleGet(CivetServer *server,
struct mg_connection *c
return true;
}
-void ListenHTTP::Handler::write_body(mg_connection *conn, const
mg_request_info *req_info) {
+bool ListenHTTP::Handler::handleHead(CivetServer *server, struct mg_connection
*conn) {
+ auto req_info = mg_get_request_info(conn);
+ if (!req_info) {
+ logger_->log_error("ListenHTTP handling HEAD resulted in a null request");
+ return false;
+ }
+ logger_->log_debug("ListenHTTP handling HEAD request of URI %s",
req_info->request_uri);
+
+ if (!auth_request(conn, req_info)) {
+ return true;
+ }
+
+ mg_printf(conn, "HTTP/1.1 200 OK\r\n");
+ write_body(conn, req_info, false /*include_payload*/);
+
+ return true;
+}
+
+void ListenHTTP::Handler::write_body(mg_connection *conn, const
mg_request_info *req_info, bool include_payload /*=true*/) {
const auto &request_uri_str = std::string(req_info->request_uri);
if (request_uri_str.size() > base_uri_.size() + 1) {
@@ -414,8 +436,9 @@ void ListenHTTP::Handler::write_body(mg_connection *conn,
const mg_request_info
mg_printf(conn, "Content-length: ");
mg_printf(conn, "%s", std::to_string(response.body.size()).c_str());
mg_printf(conn, "\r\n\r\n");
- mg_printf(conn, "%s", response.body.c_str());
-
+ if (include_payload) {
+ mg_printf(conn, "%s", response.body.c_str());
+ }
} else {
logger_->log_debug("No response body available for URI: %s",
req_info->request_uri);
mg_printf(conn, "Content-length: 0\r\n\r\n");
diff --git a/extensions/civetweb/processors/ListenHTTP.h
b/extensions/civetweb/processors/ListenHTTP.h
index f337c7d..e949fad 100644
--- a/extensions/civetweb/processors/ListenHTTP.h
+++ b/extensions/civetweb/processors/ListenHTTP.h
@@ -50,6 +50,8 @@ class ListenHTTP : public core::Processor {
ListenHTTP(std::string name, utils::Identifier uuid = utils::Identifier())
: Processor(name, uuid),
logger_(logging::LoggerFactory<ListenHTTP>::getLogger()) {
+ callbacks_.log_message = &log_message;
+ callbacks_.log_access = &log_access;
}
// Destructor
virtual ~ListenHTTP();
@@ -89,6 +91,7 @@ class ListenHTTP : public core::Processor {
std::string &&headersAsAttributesPattern);
bool handlePost(CivetServer *server, struct mg_connection *conn);
bool handleGet(CivetServer *server, struct mg_connection *conn);
+ bool handleHead(CivetServer *server, struct mg_connection *conn);
/**
* Sets a static response body string to be used for a given URI, with a
number of seconds it will be kept in memory.
@@ -114,7 +117,7 @@ class ListenHTTP : public core::Processor {
void send_error_response(struct mg_connection *conn);
bool auth_request(mg_connection *conn, const mg_request_info *req_info)
const;
void set_header_attributes(const mg_request_info *req_info, const
std::shared_ptr<FlowFileRecord> &flow_file) const;
- void write_body(mg_connection *conn, const mg_request_info *req_info);
+ void write_body(mg_connection *conn, const mg_request_info *req_info, bool
include_payload = true);
std::string base_uri_;
std::regex auth_dn_regex_;
@@ -163,10 +166,47 @@ class ListenHTTP : public core::Processor {
const struct mg_request_info *req_info_;
};
+ static int log_message(const struct mg_connection *conn, const char
*message) {
+ try {
+ struct mg_context* ctx = mg_get_context(conn);
+ /* CivetServer stores 'this' as the userdata when calling mg_start */
+ CivetServer* server = static_cast<CivetServer*>(mg_get_user_data(ctx));
+ if (server == nullptr) {
+ return 0;
+ }
+ std::shared_ptr<logging::Logger>* logger =
static_cast<std::shared_ptr<logging::Logger>*>(const_cast<void*>(server->getUserContext()));
+ if (logger == nullptr) {
+ return 0;
+ }
+ logging::LOG_ERROR((*logger)) << "CivetWeb error: " << message;
+ } catch (...) {
+ }
+ return 0;
+ }
+
+ static int log_access(const struct mg_connection *conn, const char *message)
{
+ try {
+ struct mg_context* ctx = mg_get_context(conn);
+ /* CivetServer stores 'this' as the userdata when calling mg_start */
+ CivetServer* server = static_cast<CivetServer*>(mg_get_user_data(ctx));
+ if (server == nullptr) {
+ return 0;
+ }
+ std::shared_ptr<logging::Logger>* logger =
static_cast<std::shared_ptr<logging::Logger>*>(const_cast<void*>(server->getUserContext()));
+ if (logger == nullptr) {
+ return 0;
+ }
+ logging::LOG_DEBUG((*logger)) << "CivetWeb access: " << message;
+ } catch (...) {
+ }
+ return 0;
+ }
+
private:
// Logger
std::shared_ptr<logging::Logger> logger_;
+ CivetCallbacks callbacks_;
std::unique_ptr<CivetServer> server_;
std::unique_ptr<Handler> handler_;
std::string listeningPort;
diff --git a/extensions/civetweb/tests/CMakeLists.txt
b/extensions/civetweb/tests/CMakeLists.txt
new file mode 100644
index 0000000..723c407
--- /dev/null
+++ b/extensions/civetweb/tests/CMakeLists.txt
@@ -0,0 +1,56 @@
+#
+# 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.
+#
+
+if(NOT DISABLE_CURL)
+ file(GLOB CIVETWEB_INTEGRATION_TESTS "*.cpp")
+ SET(CIVETWEB-EXTENSIONS_TEST_COUNT 0)
+ FOREACH(testfile ${CIVETWEB_INTEGRATION_TESTS})
+ get_filename_component(testfilename "${testfile}" NAME_WE)
+ add_executable("${testfilename}" "${testfile}")
+ target_include_directories(${testfilename} PRIVATE BEFORE
"${CMAKE_SOURCE_DIR}/libminifi/test/")
+ target_include_directories(${testfilename} PRIVATE BEFORE
"${CMAKE_SOURCE_DIR}/extensions/civetweb")
+ target_include_directories(${testfilename} PRIVATE BEFORE
"${CMAKE_SOURCE_DIR}/extensions/standard-processors")
+ target_include_directories(${testfilename} PRIVATE BEFORE
"${CMAKE_SOURCE_DIR}/extensions/http-curl")
+ target_include_directories(${testfilename} PRIVATE BEFORE
"${CMAKE_SOURCE_DIR}/thirdparty/civetweb-1.10/include")
+ target_include_directories(${testfilename} PRIVATE BEFORE
${CURL_INCLUDE_DIRS})
+
+ if (APPLE)
+ target_link_libraries (${testfilename} -Wl,-all_load
${ZLIB_LIBRARIES} ${OPENSSL_LIBRARIES} minifi-civet-extensions minifi-http-curl
minifi-standard-processors)
+ elseif (WIN32)
+ target_link_libraries (${testfilename} ${ZLIB_LIBRARY}
${OPENSSL_LIBRARIES} minifi-civet-extensions minifi-http-curl
minifi-standard-processors)
+ set_target_properties (${testfilename} PROPERTIES LINK_FLAGS
"${LINK_FLAGS} /WHOLEARCHIVE:minifi-civet-extensions
/WHOLEARCHIVE:minifi-http-curl /WHOLEARCHIVE:minifi-standard-processors")
+ else ()
+ target_link_libraries (${testfilename} -Wl,--whole-archive
${ZLIB_LIBRARIES} ${OPENSSL_LIBRARIES} minifi-civet-extensions minifi-http-curl
minifi-standard-processors -Wl,--no-whole-archive)
+ endif ()
+
+ createTests("${testfilename}")
+ target_link_libraries(${testfilename} ${CATCH_MAIN_LIB})
+ MATH(EXPR CIVETWEB-EXTENSIONS_TEST_COUNT
"${CIVETWEB-EXTENSIONS_TEST_COUNT}+1")
+ add_test(NAME "${testfilename}" COMMAND "${testfilename}"
WORKING_DIRECTORY ${TEST_DIR})
+ # Copy test resources
+ add_custom_command(
+ TARGET "${testfilename}"
+ POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy_directory
+
"${CMAKE_SOURCE_DIR}/extensions/civetweb/tests/resources"
+ "$<TARGET_FILE_DIR:${testfilename}>/resources"
+ )
+ ENDFOREACH()
+ message("-- Finished building ${CIVETWEB-EXTENSIONS_TEST_COUNT} civetweb
related test file(s)...")
+endif()
diff --git a/extensions/civetweb/tests/ListenHTTPTests.cpp
b/extensions/civetweb/tests/ListenHTTPTests.cpp
new file mode 100644
index 0000000..1db2fea
--- /dev/null
+++ b/extensions/civetweb/tests/ListenHTTPTests.cpp
@@ -0,0 +1,538 @@
+/**
+ *
+ * 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 <uuid/uuid.h>
+#include <fstream>
+#include <map>
+#include <memory>
+#include <set>
+#include <iostream>
+
+#include "TestBase.h"
+
+#include "utils/file/FileUtils.h"
+#include "processors/GetFile.h"
+#include "processors/UpdateAttribute.h"
+#include "processors/LogAttribute.h"
+#include "processors/ListenHTTP.h"
+#include "client/HTTPClient.h"
+#include "controllers/SSLContextService.h"
+#include "properties/Configure.h"
+
+class ListenHTTPTestsFixture {
+ public:
+ ListenHTTPTestsFixture()
+ : tmp_dir_format(strdup("/tmp/gt.XXXXXX")) {
+ LogTestController::getInstance().setDebug<TestPlan>();
+ LogTestController::getInstance().setDebug<minifi::FlowController>();
+ LogTestController::getInstance().setDebug<minifi::SchedulingAgent>();
+ LogTestController::getInstance().setDebug<minifi::core::ProcessGroup>();
+ LogTestController::getInstance().setDebug<minifi::core::Processor>();
+ LogTestController::getInstance().setTrace<minifi::core::ProcessSession>();
+ LogTestController::getInstance().setTrace<processors::ListenHTTP>();
+
LogTestController::getInstance().setTrace<processors::ListenHTTP::Handler>();
+ LogTestController::getInstance().setDebug<processors::LogAttribute>();
+ LogTestController::getInstance().setDebug<utils::HTTPClient>();
+
LogTestController::getInstance().setDebug<minifi::controllers::SSLContextService>();
+
+ // Create temporary directories
+ tmp_dir = testController.createTempDirectory(tmp_dir_format);
+ REQUIRE(!tmp_dir.empty());
+
+ // Define test input file
+ std::string test_input_file = utils::file::FileUtils::concat_path(tmp_dir,
"test");
+ {
+ std::ofstream os(test_input_file);
+ os << "Hello response body";
+ }
+
+ // Build MiNiFi processing graph
+ plan = testController.createPlan();
+ get_file = plan->addProcessor(
+ "GetFile",
+ "GetFile");
+ update_attribute = plan->addProcessor(
+ "UpdateAttribute",
+ "UpdateAttribute",
+ core::Relationship("success", "description"),
+ true);
+ listen_http = plan->addProcessor(
+ "ListenHTTP",
+ "ListenHTTP",
+ core::Relationship("success", "description"),
+ true);
+ log_attribute = plan->addProcessor(
+ "LogAttribute",
+ "LogAttribute",
+ core::Relationship("success", "description"),
+ true);
+
+ // Configure GetFile processor
+ plan->setProperty(get_file, "Input Directory", tmp_dir);
+
+ // Configure UpdateAttribute processor
+ plan->setProperty(update_attribute, "http.type", "response_body", true);
+
+ // Configure ListenHTTP processor
+ plan->setProperty(listen_http, "Listening Port", "0");
+ }
+
+ virtual ~ListenHTTPTestsFixture() {
+ free(tmp_dir_format);
+ LogTestController::getInstance().reset();
+ }
+
+ void create_ssl_context_service(const char* ca, const char* client_cert) {
+ auto config = std::make_shared<minifi::Configure>();
+ if (ca != nullptr) {
+ config->set(minifi::Configure::nifi_security_client_ca_certificate,
utils::file::FileUtils::get_executable_dir() + "/resources/" + ca);
+ }
+ if (client_cert != nullptr) {
+ config->set(minifi::Configure::nifi_security_client_certificate,
utils::file::FileUtils::get_executable_dir() + "/resources/" + client_cert);
+ config->set(minifi::Configure::nifi_security_client_private_key,
utils::file::FileUtils::get_executable_dir() + "/resources/" + client_cert);
+ config->set(minifi::Configure::nifi_security_client_pass_phrase,
"Password12");
+ }
+ ssl_context_service =
std::make_shared<minifi::controllers::SSLContextService>("SSLContextService",
config);
+ ssl_context_service->onEnable();
+ }
+
+ void run_server() {
+ plan->runNextProcessor(); // GetFile
+ plan->runNextProcessor(); // UpdateAttribute
+ plan->runNextProcessor(); // ListenHTTP
+
+ auto raw_ptr =
dynamic_cast<org::apache::nifi::minifi::processors::ListenHTTP*>(listen_http.get());
+ std::string protocol = std::string("http") + (raw_ptr->isSecure() ? "s" :
"");
+ std::string portstr = raw_ptr->getPort();
+ REQUIRE(LogTestController::getInstance().contains("Listening on port " +
portstr));
+
+ url = protocol + "://localhost:" + portstr + "/contentListener/" +
endpoint;
+ }
+
+ void test_connect(bool should_succeed = true, int64_t response_code = 200) {
+ if (client == nullptr) {
+ client = std::unique_ptr<utils::HTTPClient>(new utils::HTTPClient());
+ client->initialize(method, url, ssl_context_service);
+ client->setVerbose();
+ for (const auto &header : headers) {
+ client->appendHeader(header.first, header.second);
+ }
+ if (method == "POST") {
+ client->setPostFields(payload);
+ }
+ }
+ auto res = client->submit();
+ if (should_succeed) {
+ REQUIRE(res);
+ REQUIRE(response_code == client->getResponseCode());
+ if (response_code == 200) {
+ if (endpoint == "test") {
+ std::string content_type;
+ if (!update_attribute->getDynamicProperty("mime.type",
content_type)) {
+ content_type = "application/octet-stream";
+ }
+ REQUIRE(content_type ==
utils::StringUtils::trim(client->getParsedHeaders().at("Content-type")));
+ REQUIRE("19" ==
utils::StringUtils::trim(client->getParsedHeaders().at("Content-length")));
+ } else {
+ REQUIRE("0" ==
utils::StringUtils::trim(client->getParsedHeaders().at("Content-length")));
+ }
+ if (method == "GET" || method == "POST") {
+ const auto &body_chars = client->getResponseBody();
+ std::string response_body(body_chars.data(), body_chars.size());
+ if (endpoint == "test") {
+ REQUIRE("Hello response body" == response_body);
+ } else {
+ REQUIRE("" == response_body);
+ }
+
+ plan->runNextProcessor(); // LogAttribute
+ REQUIRE(LogTestController::getInstance().contains("Size:" +
std::to_string(payload.size()) + " Offset:0"));
+ }
+ }
+ } else {
+ REQUIRE(!res);
+ }
+ }
+
+ protected:
+ char* tmp_dir_format;
+ std::string tmp_dir;
+ TestController testController;
+ std::shared_ptr<TestPlan> plan;
+ std::shared_ptr<core::Processor> get_file;
+ std::shared_ptr<core::Processor> update_attribute;
+ std::shared_ptr<core::Processor> listen_http;
+ std::shared_ptr<core::Processor> log_attribute;
+
+ std::shared_ptr<minifi::controllers::SSLContextService> ssl_context_service;
+ std::string method = "GET";
+ std::map<std::string, std::string> headers;
+ std::string payload;
+ std::string endpoint = "test";
+ std::string url;
+ std::unique_ptr<utils::HTTPClient> client;
+};
+
+TEST_CASE("ListenHTTP creation", "[basic]") {
+ TestController testController;
+ std::shared_ptr<core::Processor>
+ processor =
std::make_shared<org::apache::nifi::minifi::processors::ListenHTTP>("processorname");
+ REQUIRE(processor->getName() == "processorname");
+}
+
+TEST_CASE_METHOD(ListenHTTPTestsFixture, "HTTP GET", "[basic]") {
+ run_server();
+ test_connect();
+}
+
+TEST_CASE_METHOD(ListenHTTPTestsFixture, "HTTP POST", "[basic]") {
+ run_server();
+
+ method = "POST";
+ payload = "Test payload";
+
+ test_connect();
+}
+
+TEST_CASE_METHOD(ListenHTTPTestsFixture, "HTTP PUT", "[basic]") {
+ run_server();
+
+ method = "PUT";
+
+ test_connect(true /*should_succeed*/, 405);
+}
+
+TEST_CASE_METHOD(ListenHTTPTestsFixture, "HTTP DELETE", "[basic]") {
+ run_server();
+
+ method = "DELETE";
+
+ test_connect(true /*should_succeed*/, 405);
+}
+
+TEST_CASE_METHOD(ListenHTTPTestsFixture, "HTTP HEAD", "[basic]") {
+ run_server();
+
+ method = "HEAD";
+
+ test_connect();
+}
+
+TEST_CASE_METHOD(ListenHTTPTestsFixture, "HTTP no body", "[basic]") {
+ endpoint = "test2";
+
+ SECTION("GET") {
+ method = "GET";
+ }
+ SECTION("POST") {
+ method = "POST";
+ payload = "Test payload";
+ }
+ SECTION("HEAD") {
+ method = "HEAD";
+ }
+
+ run_server();
+ test_connect();
+}
+
+TEST_CASE_METHOD(ListenHTTPTestsFixture, "HTTP body with different mime type",
"[basic][mime]") {
+ plan->setProperty(update_attribute, "mime.type", "text/plain", true);
+
+ SECTION("GET") {
+ method = "GET";
+ }
+ SECTION("POST") {
+ method = "POST";
+ payload = "Test payload";
+ }
+ SECTION("HEAD") {
+ method = "HEAD";
+ }
+
+ run_server();
+ test_connect();
+}
+
+TEST_CASE_METHOD(ListenHTTPTestsFixture, "HTTP all headers",
"[basic][headers]") {
+ plan->setProperty(listen_http, "HTTP Headers to receive as Attributes
(Regex)", ".*");
+
+ headers = {{"foo", "1"},
+ {"bar", "2"}};
+
+ SECTION("GET") {
+ method = "GET";
+ }
+ SECTION("POST") {
+ method = "POST";
+ payload = "Test payload";
+ }
+
+ run_server();
+ test_connect();
+
+ REQUIRE(LogTestController::getInstance().contains("key:foo value:1"));
+ REQUIRE(LogTestController::getInstance().contains("key:bar value:2"));
+}
+
+TEST_CASE_METHOD(ListenHTTPTestsFixture, "HTTP filtered headers", "[headers]")
{
+ plan->setProperty(listen_http, "HTTP Headers to receive as Attributes
(Regex)", "f.*");
+
+ headers = {{"foo", "1"},
+ {"bar", "2"}};
+
+ SECTION("GET") {
+ method = "GET";
+ }
+ SECTION("POST") {
+ method = "POST";
+ payload = "Test payload";
+ }
+
+ run_server();
+ test_connect();
+
+ REQUIRE(LogTestController::getInstance().contains("key:foo value:1"));
+ REQUIRE(false == LogTestController::getInstance().contains("key:bar
value:2", std::chrono::seconds(0) /*timeout*/));
+}
+
+#ifdef OPENSSL_SUPPORT
+TEST_CASE_METHOD(ListenHTTPTestsFixture, "HTTPS without CA", "[basic][https]")
{
+ plan->setProperty(listen_http, "SSL Certificate",
utils::file::FileUtils::concat_path(utils::file::FileUtils::get_executable_dir(),
"resources/server.pem"));
+
+ create_ssl_context_service("goodCA.crt", nullptr);
+
+ SECTION("GET") {
+ method = "GET";
+ }
+ SECTION("POST") {
+ method = "POST";
+ payload = "Test payload";
+ }
+ SECTION("HEAD") {
+ method = "HEAD";
+ }
+
+ run_server();
+ test_connect();
+}
+
+TEST_CASE_METHOD(ListenHTTPTestsFixture, "HTTPS without client cert",
"[basic][https]") {
+ plan->setProperty(listen_http, "SSL Certificate",
utils::file::FileUtils::concat_path(utils::file::FileUtils::get_executable_dir(),
"resources/server.pem"));
+ plan->setProperty(listen_http, "SSL Certificate Authority",
utils::file::FileUtils::concat_path(utils::file::FileUtils::get_executable_dir(),
"resources/goodCA.crt"));
+
+ create_ssl_context_service("goodCA.crt", nullptr);
+
+ SECTION("GET") {
+ method = "GET";
+ }
+ SECTION("POST") {
+ method = "POST";
+ payload = "Test payload";
+ }
+ SECTION("HEAD") {
+ method = "HEAD";
+ }
+
+ run_server();
+ test_connect();
+}
+
+TEST_CASE_METHOD(ListenHTTPTestsFixture, "HTTPS with client cert from good
CA", "[https]") {
+ plan->setProperty(listen_http, "SSL Certificate",
utils::file::FileUtils::concat_path(utils::file::FileUtils::get_executable_dir(),
"resources/server.pem"));
+ plan->setProperty(listen_http, "SSL Certificate Authority",
utils::file::FileUtils::concat_path(utils::file::FileUtils::get_executable_dir(),
"resources/goodCA.crt"));
+ plan->setProperty(listen_http, "SSL Verify Peer", "yes");
+
+ create_ssl_context_service("goodCA.crt", "goodCA_goodClient.pem");
+
+ SECTION("GET") {
+ method = "GET";
+ }
+ SECTION("POST") {
+ method = "POST";
+ payload = "Test payload";
+ }
+ SECTION("HEAD") {
+ method = "HEAD";
+ }
+
+ run_server();
+ test_connect();
+}
+
+TEST_CASE_METHOD(ListenHTTPTestsFixture, "HTTPS with PKCS12 client cert from
good CA", "[https]") {
+ plan->setProperty(listen_http, "SSL Certificate",
utils::file::FileUtils::concat_path(utils::file::FileUtils::get_executable_dir(),
"resources/server.pem"));
+ plan->setProperty(listen_http, "SSL Certificate Authority",
utils::file::FileUtils::concat_path(utils::file::FileUtils::get_executable_dir(),
"resources/goodCA.crt"));
+ plan->setProperty(listen_http, "SSL Verify Peer", "yes");
+
+ create_ssl_context_service("goodCA.crt", "goodCA_goodClient.p12");
+
+ SECTION("GET") {
+ method = "GET";
+ }
+ SECTION("POST") {
+ method = "POST";
+ payload = "Test payload";
+ }
+ SECTION("HEAD") {
+ method = "HEAD";
+ }
+
+ run_server();
+ test_connect();
+}
+
+TEST_CASE_METHOD(ListenHTTPTestsFixture, "HTTPS with client cert from bad CA",
"[https]") {
+ plan->setProperty(listen_http, "SSL Certificate",
utils::file::FileUtils::concat_path(utils::file::FileUtils::get_executable_dir(),
"resources/server.pem"));
+ plan->setProperty(listen_http, "SSL Certificate Authority",
utils::file::FileUtils::concat_path(utils::file::FileUtils::get_executable_dir(),
"resources/goodCA.crt"));
+
+ bool should_succeed = false;
+ SECTION("verify peer") {
+ should_succeed = false;
+ plan->setProperty(listen_http, "SSL Verify Peer", "yes");
+
+ SECTION("GET") {
+ method = "GET";
+ }
+ SECTION("POST") {
+ method = "POST";
+ payload = "Test payload";
+ }
+ SECTION("HEAD") {
+ method = "HEAD";
+ }
+ }
+ SECTION("do not verify peer") {
+ should_succeed = true;
+
+ SECTION("GET") {
+ method = "GET";
+ }
+ SECTION("POST") {
+ method = "POST";
+ payload = "Test payload";
+ }
+ SECTION("HEAD") {
+ method = "HEAD";
+ }
+ }
+
+ create_ssl_context_service("goodCA.crt", "badCA_goodClient.pem");
+
+ run_server();
+ test_connect(should_succeed);
+}
+
+TEST_CASE_METHOD(ListenHTTPTestsFixture, "HTTPS with client cert with matching
DN", "[https][DN]") {
+ plan->setProperty(listen_http, "SSL Certificate",
utils::file::FileUtils::concat_path(utils::file::FileUtils::get_executable_dir(),
"resources/server.pem"));
+ plan->setProperty(listen_http, "SSL Certificate Authority",
utils::file::FileUtils::concat_path(utils::file::FileUtils::get_executable_dir(),
"resources/goodCA.crt"));
+ plan->setProperty(listen_http, "Authorized DN Pattern", ".*/CN=good\\..*");
+ plan->setProperty(listen_http, "SSL Verify Peer", "yes");
+
+ create_ssl_context_service("goodCA.crt", "goodCA_goodClient.pem");
+
+ SECTION("GET") {
+ method = "GET";
+ }
+ SECTION("POST") {
+ method = "POST";
+ payload = "Test payload";
+ }
+ SECTION("HEAD") {
+ method = "HEAD";
+ }
+
+ run_server();
+ test_connect();
+}
+
+TEST_CASE_METHOD(ListenHTTPTestsFixture, "HTTPS with client cert with
non-matching DN", "[https][DN]") {
+ plan->setProperty(listen_http, "SSL Certificate",
utils::file::FileUtils::concat_path(utils::file::FileUtils::get_executable_dir(),
"resources/server.pem"));
+ plan->setProperty(listen_http, "SSL Certificate Authority",
utils::file::FileUtils::concat_path(utils::file::FileUtils::get_executable_dir(),
"resources/goodCA.crt"));
+ plan->setProperty(listen_http, "Authorized DN Pattern", ".*/CN=good\\..*");
+
+ int64_t response_code = 0;
+ SECTION("verify peer") {
+ plan->setProperty(listen_http, "SSL Verify Peer", "yes");
+ response_code = 403;
+
+ SECTION("GET") {
+ method = "GET";
+ }
+ SECTION("POST") {
+ method = "POST";
+ payload = "Test payload";
+ }
+ SECTION("HEAD") {
+ method = "HEAD";
+ }
+ }
+ SECTION("do not verify peer") {
+ response_code = 200;
+
+ SECTION("GET") {
+ method = "GET";
+ }
+ SECTION("POST") {
+ method = "POST";
+ payload = "Test payload";
+ }
+ SECTION("HEAD") {
+ method = "HEAD";
+ }
+ }
+
+ create_ssl_context_service("goodCA.crt", "goodCA_badClient.pem");
+
+ run_server();
+ test_connect(true /*should_succeed*/, response_code /*response_code*/);
+}
+
+#if CURL_AT_LEAST_VERSION(7, 54, 0)
+TEST_CASE_METHOD(ListenHTTPTestsFixture, "HTTPS minimum SSL version",
"[https]") {
+ plan->setProperty(listen_http, "SSL Certificate",
utils::file::FileUtils::concat_path(utils::file::FileUtils::get_executable_dir(),
"resources/server.pem"));
+ plan->setProperty(listen_http, "SSL Certificate Authority",
utils::file::FileUtils::concat_path(utils::file::FileUtils::get_executable_dir(),
"resources/goodCA.crt"));
+ plan->setProperty(listen_http, "SSL Minimum Version", "TLS1.1");
+
+ SECTION("GET") {
+ method = "GET";
+ }
+ SECTION("POST") {
+ method = "POST";
+ payload = "Test payload";
+ }
+ SECTION("HEAD") {
+ method = "HEAD";
+ }
+
+ create_ssl_context_service("goodCA.crt", "goodCA_goodClient.pem");
+
+ run_server();
+
+ client = std::unique_ptr<utils::HTTPClient>(new utils::HTTPClient());
+ client->setVerbose();
+ client->initialize(method, url, ssl_context_service);
+ if (method == "POST") {
+ client->setPostFields(payload);
+ }
+ REQUIRE(client->setSpecificSSLVersion(utils::SSLVersion::TLSv1_0));
+
+ test_connect(false /*should_succeed*/);
+}
+#endif
+#endif
diff --git a/extensions/civetweb/tests/resources/badCA_goodClient.p12
b/extensions/civetweb/tests/resources/badCA_goodClient.p12
new file mode 100644
index 0000000..b51a3a3
Binary files /dev/null and
b/extensions/civetweb/tests/resources/badCA_goodClient.p12 differ
diff --git a/extensions/civetweb/tests/resources/badCA_goodClient.pem
b/extensions/civetweb/tests/resources/badCA_goodClient.pem
new file mode 100644
index 0000000..86bc17c
--- /dev/null
+++ b/extensions/civetweb/tests/resources/badCA_goodClient.pem
@@ -0,0 +1,46 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEAsccjk4783E7/2RDkqunqTAO31PnTwJQyxr8UJIRe1ZVAsTlp
+4UbQfQ3fonpyYg1oJOt0RvgKIRMQA4ECKvTmQpaWqmrjCmie25nnGzCCZuNJ6wU/
+qmsA15LAJOzcNDKLmVGh/5ts7tIkof0ujvGhHWv2MhfciBhICIxn9feTgj3U1eyH
+q+TqqGKXwuclS6wFrMPZd0OnfKXteTyxfjUrW7YDn1GQZrJJBOSXVKP93fRE/FY7
+pbqrLyzOE4XU+Y9RM8gGFuMc1BKCiKZGubgdDexoUL8iLOQxzVt0GCzRri3ugfjO
+YvbfClgP1lLfX96ISm2sXjffXNl4DJgAF4FfqQIDAQABAoIBABqqSfXKDrdkyg9e
+702LhG8eZ6Z0SoSqNeuFoZnQmQDkQC3U9MKrgn4fZJnUT+/RHvvarTgv4CUR3OcJ
+pK+YyCjYuYSaP6/B/YHm1blIT2brVJ0BzojbP+cVxehD9suFgVbf3bKfN3mi34fE
+mAUszQPCu8zLs3JeYf+WP8mu9tsj9xMkGnWDcfApl5TLT/gEQgkS2K6UUPXZb6m3
+YzjW3hp2jcx+0RrJxBbbSTWzAj5SDExYzw/ulYyhuenFK0LTi0RK5c+3XPq9lfxY
+0AjHVYTb7D5dl9oRgCPaaFJAidSGD1s5tXY43yQhaupWjOUkgIohVWkkDfEjrH7r
++omCEAECgYEA2iqpJlGn8U/aIPar8TmOo5aVI8x6lMYOlGhgwgscXjx4YBjal+fo
+cv6o0Lw0RU0agpnPDgWon2xb0ufIaR7cn+DXU1vXAKLVzip+vuPbfh+l86LO+Zfl
+KlhRime7hWUJLo8EtFSs4ooVuu8n3gSJYbVa45xD1IrUPQdrI/jLnwECgYEA0Jtz
+XIS+DlizmPStqtfTQ6JSSlV+3QJC02/IllNSihsBNnuqwQjYIv2NLwBKAzjEO159
+Fsrk2WovjdtKs8jPzlb/pNwkn88/6jnUfxh+2/hKSAjiMadY3wDtDY8V4sj20EU4
+hi/gHPnqillabEvTrK+WkoY5pv5a6622Hn19aKkCgYBXUhHn03ELxfFBllmVsHrm
+ASRqcrJxj6BQSELKB9Zv5XYsyGXdvSWtuT3qZhnpzwWYVmWocB1gyecq0DjH0mFt
+4Hlu1OiGSaaX4SxfzSWSIqqyjGyZO/GudDEW25QTvS/iob25S9byyWAPNR/Y3in6
+oLLjPS1tCbAPSUPZ3v/pAQKBgBKr91VupDxQgDLOo8TI8KX7H3Z71JEfpK+cL84U
+wyyNYjxoMU555i6rlzl/wyAqspXFzVh+7KDxOjRuTm9tJ/yGGPe+pKCRQl1Ks9R0
+ctZpkOyFrwlWu0Oqp40xI3pbFoxpxbdtDZhKXk3n1Yof92BbjxSqYvqphaXWtJhi
+DxmRAoGAQxlwKKlx2NU9mvMkJ1oXLUiegrjAXK/y1ErC1I07SFz/Yfq6Pf94l2kd
+CVenMLeQxl3RxleejoF8ZcUQrqEP+OoXhkxQRthyM57pF/jYMpeD/Pd0Pe4hflEF
+aoljZz+7YdGg8R5CGLYpqHb9LpqnmosDPpEz+L8Mao3DPJ8lC8M=
+-----END RSA PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIDJDCCAgwCCQDtE0id+/yQXjANBgkqhkiG9w0BAQsFADBbMQswCQYDVQQGEwJV
+UzELMAkGA1UECAwCQ0ExFjAUBgNVBAoMDUV4YW1wbGUsIEluYy4xJzAlBgNVBAMM
+HkJhZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xOTA3MTIwOTUyNTNa
+Fw0yOTA3MDkwOTUyNTNaME0xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEWMBQG
+A1UECgwNRXhhbXBsZSwgSW5jLjEZMBcGA1UEAwwQZ29vZC5leGFtcGxlLmNvbTCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALHHI5OO/NxO/9kQ5Krp6kwD
+t9T508CUMsa/FCSEXtWVQLE5aeFG0H0N36J6cmINaCTrdEb4CiETEAOBAir05kKW
+lqpq4wpontuZ5xswgmbjSesFP6prANeSwCTs3DQyi5lRof+bbO7SJKH9Lo7xoR1r
+9jIX3IgYSAiMZ/X3k4I91NXsh6vk6qhil8LnJUusBazD2XdDp3yl7Xk8sX41K1u2
+A59RkGaySQTkl1Sj/d30RPxWO6W6qy8szhOF1PmPUTPIBhbjHNQSgoimRrm4HQ3s
+aFC/IizkMc1bdBgs0a4t7oH4zmL23wpYD9ZS31/eiEptrF4331zZeAyYABeBX6kC
+AwEAATANBgkqhkiG9w0BAQsFAAOCAQEADMnoHqwUbGM2NRdqoJ2FdgK697b/ah5n
+EjsmAeyYNhTRpVFyErWqvZp9F4R1ARSOXH3RLo79jexAvqt9b8Ho8a1bfIliGVkr
+482qLjTbS15sv6lwSX8XCeSI1994Csj9iB1d+zIHax6UXe62biaomtUzBzEdll1G
+EcuwJOzpEbROPp4ciLYPmIDY2Gdn4eeEGvxjFLT+U979BH4kwz98vKLUwUiY4o+Z
+n1gndeIHwVB0YZFFZWqumQ6FNSChH7YRuhxDMgoj/dgOSAZkvR20HYatJhjErEhX
+dTVJtNP8eU/vTFrxTvVTRqJWR4dnwDFre1J9CIIGL20r34MvbjTCQA==
+-----END CERTIFICATE-----
diff --git a/extensions/civetweb/tests/resources/generate.sh
b/extensions/civetweb/tests/resources/generate.sh
new file mode 100755
index 0000000..8daab7f
--- /dev/null
+++ b/extensions/civetweb/tests/resources/generate.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+function create_ca {
+ if [ $# -ne 2 ]; then
+ exit -1
+ fi
+ name=$1
+ cn=$2
+
+ openssl genrsa -out "$name.key"
+ openssl req -x509 -new -nodes -key "$name.key" -sha256 -days 10950 -subj
"/C=US/ST=CA/O=Example, Inc./CN=$cn" -out "$name.crt"
+}
+
+function create_cert {
+ if [ $# -ne 3 ]; then
+ exit -1
+ fi
+ ca=$1
+ name=$2
+ cn=$3
+
+ openssl genrsa -out "$name.key"
+ openssl req -new -sha256 -key "$name.key" -subj "/C=US/ST=CA/O=Example,
Inc./CN=$cn" -out "$name.csr"
+ serial_arg=""
+ if [ ! -f "$ca.srl" ]; then
+ serial_arg="-CAcreateserial"
+ fi
+ openssl x509 -req -in "$name.csr" -CA "$ca.crt" -CAkey "$ca.key" $serial_arg
-out "$name.crt" -days 3650 -sha256
+ cat "$name.key" "$name.crt" > "$name.pem"
+ openssl pkcs12 -export -out "$name.p12" -inkey "$name.key" -in "$name.crt"
-password pass:Password12
+ rm "$name.csr" "$name.key" "$name.crt"
+}
+
+# Generate good CA
+create_ca "goodCA" "Good Root Certificate Authority"
+
+# Generate server cert with good case CA
+create_cert "goodCA" "server" "localhost"
+
+# Generate good client cert with good CA
+create_cert "goodCA" "goodCA_goodClient" "good.example.com"
+
+# Generate bad client cert with good CA
+create_cert "goodCA" "goodCA_badClient" "bad.example.com"
+
+# Generate bad CA
+create_ca "badCA" "Bad Root Certificate Authority"
+
+# Generate good client cert with bad CA
+create_cert "badCA" "badCA_goodClient" "good.example.com"
+
+# Cleanup
+rm goodCA.key goodCA.srl badCA.crt badCA.key badCA.srl
diff --git a/extensions/civetweb/tests/resources/goodCA.crt
b/extensions/civetweb/tests/resources/goodCA.crt
new file mode 100644
index 0000000..e801553
--- /dev/null
+++ b/extensions/civetweb/tests/resources/goodCA.crt
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDNDCCAhwCCQDoXhDkdH/BBjANBgkqhkiG9w0BAQsFADBcMQswCQYDVQQGEwJV
+UzELMAkGA1UECAwCQ0ExFjAUBgNVBAoMDUV4YW1wbGUsIEluYy4xKDAmBgNVBAMM
+H0dvb2QgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTkwNzEyMDk1MjUz
+WhcNNDkwNzA0MDk1MjUzWjBcMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExFjAU
+BgNVBAoMDUV4YW1wbGUsIEluYy4xKDAmBgNVBAMMH0dvb2QgUm9vdCBDZXJ0aWZp
+Y2F0ZSBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDG
+t5qp++4NAO83uASsVx7xRc3YS1Ss6La2opJTeXSnnsL6d+eLIUZrO6R/vofjLMPb
+qHisnQXAtl560d/XPBXm/ydp2IBLJQJW9aRxa/zqcf4tDTdBLKXYHhqKSQDJGS78
+vOuNuhf6T+p1guqnLYxwlRp6V8DMY/nC5n+IgByr9Jp2QtqJceH5WdyABVauqtMo
+LKXdbhfU6lDZ1XIZNeoKY8u2s34UQLUvOGaP/FzYHvKev1KzFF/nR3+svK8cvxXM
+EuqHM5tdtIp1ugjvR66PUIT00HoT00wS6VIpBdHq/8uXJeY77lr52xyVdk282tlw
+wr9/W0AGXjVMW3O+VRhFAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAE5HYjHkh3fI
+qakhENGL5PSszmOz5yQRrggP2ZJeEAoFjy5fbf/zUPIPMgMa0qM4QI+2C0iGlem1
+c1MCGNk5BiDPWMaUjppYmPZWkXzYu9Nl1dizXYidcnTiiBTROkpMij2fzCErymx9
+CmYxfeFyeJ5uAHSWSOGCfvlxi0vHvHn/+5rm0eqHcGP2c9ivW/SC/6RCXnHuIS9O
+O/UHrQPQe7YmdBgCHw4K4UHLZkYPH6osMPdII09PbZBB1TgrogbuA6TMp9NU6LrX
+WNN3nhFaVVjEb8tawMabfG9PU/1PKGRuNdaLsYb3IXhT0I/SWobD3MJ9xcO9sAhv
+QKZuUQf4ntI=
+-----END CERTIFICATE-----
diff --git a/extensions/civetweb/tests/resources/goodCA_badClient.p12
b/extensions/civetweb/tests/resources/goodCA_badClient.p12
new file mode 100644
index 0000000..7154fc8
Binary files /dev/null and
b/extensions/civetweb/tests/resources/goodCA_badClient.p12 differ
diff --git a/extensions/civetweb/tests/resources/goodCA_badClient.pem
b/extensions/civetweb/tests/resources/goodCA_badClient.pem
new file mode 100644
index 0000000..6a33c19
--- /dev/null
+++ b/extensions/civetweb/tests/resources/goodCA_badClient.pem
@@ -0,0 +1,46 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA9c7u+CkKybpZIBPzUiYgkvlb07enbZR1skpQt6hUO5p7Dvgx
+OKDxy/83gwyOwTOxjeS82Vvfd8QJuwIPVHOy5WwX0zgOZlwL2OiIpPIim7idO9a7
+9Y0fSlOMofbisa1ssVEypqvOCrvsNbLPXuioPxse5FnSrncPpR0Qj9FtX01SGIK5
+QYER1RiLZ5ZVaUxxdc81mvuxvsCrS+SO7BaCr5Pd46FMWZzr+f1unCjLsLvYLoVP
+Gt7stJOnbThY9/wKp9SjdNr2Q30rKIHwjjKSchxEWuldu6gcZHmgjdPP4bA2utCr
+/I04ucrZV4wy1IESlyjBhMLOoHhWoHxSewowtQIDAQABAoIBAQCw0X13R9a5y/Gm
+sJM8ia6u6u8SOi8XFU8gxHKR2mVVRse0ufZ2PNgSnXEqNNNPyUa0wlDSrlLuzTcO
+remnH18Vx47P4qgBG79t9b+tn3wjtkZssAgfF7fleHpWW8kwdIcWeklMOZQKhqng
+6tJe/E9irbioHLD/zSeU53ZtbgIWJZi4X9v5n03n8usBj9+TgueE4RhaXmP5PtO6
++AA7DKARxeVFo9j6vmmGkxeOdyeb/28pzK2lxHTEUROW9b0kqShs//9sUczgP1j/
+hetF9FWc+b5ZT0bHrv1a3+buPzMVk1Uok71piMwvO5Hbim38XB54Be5sLbLtotRX
+J9xJlCVhAoGBAP18BzvUbW9PyUjHqzHPEemJnlDj4SE89i+PqaCHpJdi7nvBjBmF
+Dmv6M1FiRK5AT/Pren6P3CHmRLftfilX1E/4/ea8yxL5DOUv/gup9bf6lRnAgXiR
+79bCUwGWC9q+/NvneXGv7WKJVF45cf0JTm580vlCrupgfxrj8ogjIM/9AoGBAPg/
+Z3SqFwJ2L+2AmObtgqUd9MuQqKKdEYAx1HdPs7vLB9yAcbousoBXYzWjPuG/pMdI
+2+ObOEGDYG1QI9C+1xE5flMLPI5rqk+WqA40s00X2CsR4pH+L7uQTNRbj9g4clbj
+muDYPysR6G34YzIc92y39pkHwt2fhVGOMfE3aLUZAoGBAI57dv10bIcTDAty4JHA
+2UqyZmEFlng+cgtN74Uieav2miLKKlv15KNhIhNu7zgbQlXTWSlm58/ORXY1cqL+
+kYLabK2UFXn2r/7ruRsJT+s4WTL+eEgzj+LhnBLaKpOsoylgtWzn/MFUfC3ykFYx
+Mvr8AwLFLtjjoM6Wrq9DP6BpAoGAB06Oc/+hp7/kzz/OwFVTWBrWnrtGS2sGHdjZ
+oR1mc+uY8qORNWK0fFSWJfkFG83xQrBhUIS8FimQyAbo1vcXC3m+vyEAikye+bK8
+hZaFhIpkIXhoS9XIf+PSbxm21S1sKCSQ5XdX/KONTNdXzBzQJ5IOnxh8YtuUJ/9g
+dvIjkdkCgYBwXD3eT9dbRbEBGGR70yey4wg5DYmQB3jl97zSK9G9aoyMvNnUobjR
+3sq1v/kWRSLKihizis5SV3onu8SCdhUr91gvq0y7xOe0JlDSnfJ3r6wX3AD1p8+6
+8qRRO43Bl80N782iVFhjl4LyWyttFIWCu35jtSE6wbKfCPRAW3cNgw==
+-----END RSA PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIDJDCCAgwCCQDclmfqcI6z1TANBgkqhkiG9w0BAQsFADBcMQswCQYDVQQGEwJV
+UzELMAkGA1UECAwCQ0ExFjAUBgNVBAoMDUV4YW1wbGUsIEluYy4xKDAmBgNVBAMM
+H0dvb2QgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTkwNzEyMDk1MjUz
+WhcNMjkwNzA5MDk1MjUzWjBMMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExFjAU
+BgNVBAoMDUV4YW1wbGUsIEluYy4xGDAWBgNVBAMMD2JhZC5leGFtcGxlLmNvbTCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPXO7vgpCsm6WSAT81ImIJL5
+W9O3p22UdbJKULeoVDuaew74MTig8cv/N4MMjsEzsY3kvNlb33fECbsCD1RzsuVs
+F9M4DmZcC9joiKTyIpu4nTvWu/WNH0pTjKH24rGtbLFRMqarzgq77DWyz17oqD8b
+HuRZ0q53D6UdEI/RbV9NUhiCuUGBEdUYi2eWVWlMcXXPNZr7sb7Aq0vkjuwWgq+T
+3eOhTFmc6/n9bpwoy7C72C6FTxre7LSTp204WPf8CqfUo3Ta9kN9KyiB8I4yknIc
+RFrpXbuoHGR5oI3Tz+GwNrrQq/yNOLnK2VeMMtSBEpcowYTCzqB4VqB8UnsKMLUC
+AwEAATANBgkqhkiG9w0BAQsFAAOCAQEAljiINTae1/lqQRTYc5GNnf9qz0tf56GQ
+UMc8LnYsETFJ5cUu7jvgkulXkIH8ItVWG+s96otEqvw7Cibe/PYYUIteS8gIrFLf
+OcxaWWaCD4aKYvdwCJ8oRsS/ULzF7VWSBv8fPVHRbBWHh8ET/FR1R684IHaD/7nI
+UJYnvzFAfdPJrPo4VdQwk9h0fTC8Q6Yqkxtpm5nQmSyXuV6vWh/Yiltqywn9Hz51
+c4YJx1whXJ5/zcruY2Jj53qs/ZlnifSJ0mm9tpHA5Tm9UfnnUV7vunIzc/n2MYw2
+EudqHqzsZriRHMlASfdGpWLLICrNQMl53A2Fo48mxkAeiXwxlX4Z9w==
+-----END CERTIFICATE-----
diff --git a/extensions/civetweb/tests/resources/goodCA_goodClient.p12
b/extensions/civetweb/tests/resources/goodCA_goodClient.p12
new file mode 100644
index 0000000..a5355d5
Binary files /dev/null and
b/extensions/civetweb/tests/resources/goodCA_goodClient.p12 differ
diff --git a/extensions/civetweb/tests/resources/goodCA_goodClient.pem
b/extensions/civetweb/tests/resources/goodCA_goodClient.pem
new file mode 100644
index 0000000..e4891c5
--- /dev/null
+++ b/extensions/civetweb/tests/resources/goodCA_goodClient.pem
@@ -0,0 +1,46 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEAtdEk8MHL8RFiOuHz/hfyjEe3PxumlNnubzn7u6EdJpiLhOLs
+cWcvQAbYvbsyI6duGzKh1FRNmS8+Q/gp/36vmssZy96B4K+axiG7WBqHfjPJbjfa
+NHbz3D7D7i36r/Vu/pXsBMPb/DSJ978maY3oikSB9906bx73XJHAx3RLxgLHl4po
+vt20SDOY9R2Klcbew4pDXuUpRdEK0h1+fdZkYU92YiAz2O86eYMvwgorCX9tBrcw
+sih/cYYiTQ9DMZ3DPJ0HfDH5T5gIPH/+5/YVHaTBdthhIZ13UNY/X8XcOVO/nlzC
+3/MtBiTxKj4zIyqncbClm4BqzW3S5hxUQXR32QIDAQABAoIBAAXoCmwrz4VATFGf
+X37EpmN6PPC25D13qvBAEPZycHD9iaLCgG3arUVGM6pON33DBaeqiGlOZ8rvJvWs
+TSj4o5nCuU7PJqb27W88T0q4aehmpEeJVvRXXOqtu020fq1Sqs1ob2dkOXRC/Kxo
+sEXDj2dWfGZh8HEFr4F5VqrkE0YWaQLaNHf9g6vAuOtlNMnhu5iM7mNq0qQi4Qg0
+zmOpEyAK5obhPEa8eYgjuWUeuul342wpMEGaFqD3lr4rnhcESZtm/S37L6lJ24UF
+SIBPzuEjEDlthlh3tqgKyQFsHvcMN4XN4850J/nMoX8jDcvnV7iYYFykHEb6y6FZ
++ZlftNECgYEA2hS+s7yer2bQGw3LsKqwpNmpckLeAb84JEbra6i7A7SPUMEYwjOm
+Q2ePpz6ZBVDs7qODBgMV2g9a9GCbSzgV7SeQ0367dCPugONckVQvMVU5wPtSkFKF
+8jLn66+6YK64ASTqLLVd8TkU3bdByWcsh3JTR/lmDwlSeGjbm1OETj0CgYEA1W41
+Pi8ZbGDrpc9/CnyFCLMaipq9cAm4n/M58CVf0ogxeAXShIcDboTfv7lqpnqM2vg7
+sSRjyHz0++5VZTNSnQDlLIndQHQ6NKKC1tb0zNKlRuL2gwMHwMmWLqCjbLsqSP5E
+lHEM8fn2EVAMiKRod01kOY7OnUnPSSgMD7QvJc0CgYEAyWqZi0WtRhDuKd5+/0dW
+6JqDrp1lkDV887xwmLl5KH3uU8ZUSKENcXnHqs7c45UPj4SDcd0NpJ3EAqrrIvjE
+/4kocL2/AhBhqrbS+wLGp4iwU7WLVvJw9fXgT8S4na0hEyV2Bx7nifCPfgtQfmSF
+Mv/7PSFyCncwrTcjhP0I2H0CgYEAkTNbEaUlXLBLYRDbUx0HvLVstyMzAgf7DQaC
+QjiLCkYRsZ/0aqkX0pafSmYwgnYZYddDdO5W3Ez2tnacri7OY3X6c+SPG4x3FNwC
+u3qeLMKaIrHCF7t2CNicTbiHti9XQzWJHpwSvITbvUeCX2vKjm+eYfIf6q4OUazn
+F7/z23kCgYEA0r2N20DkEN9uD281SZbjAmkjYMMvUkDyChk2oWnODZcFkEhuaGM9
+0TBw1vjpoO67H/tSNEUziXMNLtnH4p5gRzP84ZGpMPxe4MK6pIwbDxhDyPlLCwOG
+dYnaYyWqsiMbqtv6LbMh/uatuMpCzqJEQZz80xDNUp3k90muk3+0M4w=
+-----END RSA PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIDJTCCAg0CCQDclmfqcI6z1DANBgkqhkiG9w0BAQsFADBcMQswCQYDVQQGEwJV
+UzELMAkGA1UECAwCQ0ExFjAUBgNVBAoMDUV4YW1wbGUsIEluYy4xKDAmBgNVBAMM
+H0dvb2QgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTkwNzEyMDk1MjUz
+WhcNMjkwNzA5MDk1MjUzWjBNMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExFjAU
+BgNVBAoMDUV4YW1wbGUsIEluYy4xGTAXBgNVBAMMEGdvb2QuZXhhbXBsZS5jb20w
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC10STwwcvxEWI64fP+F/KM
+R7c/G6aU2e5vOfu7oR0mmIuE4uxxZy9ABti9uzIjp24bMqHUVE2ZLz5D+Cn/fq+a
+yxnL3oHgr5rGIbtYGod+M8luN9o0dvPcPsPuLfqv9W7+lewEw9v8NIn3vyZpjeiK
+RIH33TpvHvdckcDHdEvGAseXimi+3bRIM5j1HYqVxt7DikNe5SlF0QrSHX591mRh
+T3ZiIDPY7zp5gy/CCisJf20GtzCyKH9xhiJND0MxncM8nQd8MflPmAg8f/7n9hUd
+pMF22GEhnXdQ1j9fxdw5U7+eXMLf8y0GJPEqPjMjKqdxsKWbgGrNbdLmHFRBdHfZ
+AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAIh6k/epw3dWtRuMwXxjqEobi/RD/8Nk
+52kX6x8WTcnglrSzPSvkhnfR5PQ9whY2Zbw0aVdenejlGZEi8cAxwmJbN4NIhQwW
+FjHYYQA0MPgFGq/4XFT9E49aS212+ivUBRoPlWfw7QmCdGq3z6eQGfVtIGGLfSGH
+cvnC9Z4VdY0RJrnzgRKd7iq/RW66u3Uyg1fdOKCp9F5PSwwl+6dPgKO84muWjRi4
+9y+htcXSboEtYQy/ncul0MeJ8fGTY1YEG2QUolmCKeJ8a2e6SHcX0Unu+6tAD8sK
+fjpZAOI1lgRrhrIKhi3Rx0aCkhayhvvScDQL0ODA5ciCu5EJHRhWCbg=
+-----END CERTIFICATE-----
diff --git a/extensions/civetweb/tests/resources/server.p12
b/extensions/civetweb/tests/resources/server.p12
new file mode 100644
index 0000000..f4616b4
Binary files /dev/null and b/extensions/civetweb/tests/resources/server.p12
differ
diff --git a/extensions/civetweb/tests/resources/server.pem
b/extensions/civetweb/tests/resources/server.pem
new file mode 100644
index 0000000..5d4ae85
--- /dev/null
+++ b/extensions/civetweb/tests/resources/server.pem
@@ -0,0 +1,46 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAu68f+fPUMAWkBGrqImsm5geyfwrZaHkoyNHD0fqV+eHGIerN
+lPwKIqc5jGZk1gC2557O7QbguRjTYyCXlgf8Os60nZmcvYKiTEddVaSzunFroHlb
+M3teePOdraO9Dmot9FSo31iaCrEhTVMLSEvlSCgtOZxrVzz2+WraO6s7BGLASwha
+Gk5K+vvaa2GEK8qyj6ybRSRr301pr/ZUpYKzVcuyfSAVGD+D+oJM7sUc73n3Az8o
+fGMNUx0tbTy9o9XMgzoqyZD2c6LdkPhVyc/bc8PaMb2jKuybGiXHroUb301hInAE
+LeUn5YAH7AeG6mwRWcEGY0OZVO+uqwFA3Ozv3QIDAQABAoIBAQCl3uAjx5p/1nxe
+ax8BzDFUmvjlznDWJD4nPTwTF6P0c9TnpNyMDzPl7GSv8M6tU3RSv8ehM7Ln40jv
+Ep7luajxUD3QCzK7Sfil8WxLhIRTAmpcKOSxWxbjTmrMSymK08xJY4jb2zJIwMLt
+07bk7i501w0hHVzAfODJDeZRVcOS2y9yDkEHAnX4plcasFNKG1jOk1nT6hjRkD3u
+BK8Xntqryk2VWXn5SLm6zrHq7ca+uLD4+iXB3oTGYWIdXJaG+PN2BrwmQncMm8cj
+dFwwW1EzRqCkSNv7y69HxGugaYAoKUHZ5mXv2Hy9VN54N+/EsNiDZtLMgtPqwOLl
++KtQVKgBAoGBAOjAsqMi1oCjIDhxbqMayaV0TBX3AMC6vtMEkW3MH05JCFlGYDTr
+rBeqb8tZWJqv+tjGsyl5wiffk7nSXEBnQk08fnv9DW/0sLUBiJCrwSdKRhD0pGF+
+sPpCOGU1ir7lYlRNViXoipGkbk+OEkfqBh6NYscu6J2xu7ASy7tg6F4JAoGBAM5u
+DsVMmOZehZHZGkc1SeDLIPqGIX2ek5yeFmrTYuO62XEeP/MLL1fuKTihcoMuzz5D
+tgIjtKsEMv8RH5+XBu8WGsoJ5z/+cDuEJD6XKiKzbZMFMHNwFqoXoMgk3xA9V3zW
+Df0BeKhC+LRZVT8ENtzJQm1eaGrjUsRvEhX+I7g1AoGAAqVWIoad255/GkUn1dDT
+I/9bchB5wLcevjVaFd5xKKmp36HuLAvVy/sTBEPCvxdrCZXQqZMJwvxGqKEcjVrf
+JROf+Hba9T/Z1mTrEYHyUykD/ONbDwSqrF2eWIAwUJU49e5fIVUwZhFxc5QQ3yJo
+6WYADnWZDVnc4VaFXF7wpUkCgYBA3t8va2HFS0DoU3RpmjpsNQlZERunMVUr65YZ
+3fH+pLI+VQY9p28qT8KOdFXbGbOw2nBw/a2B7KDl/QiWC0z3h1fF1BTizF+SpHUL
+Yk+wdfhiMkhGjpvgueoh20xp+wzqQw5EStkS73DepBAg7H8dJPYGDpv7sxJIfqsN
+VD7/XQKBgQDQnLwMQdsSZmkWUBIbTV3aqQqtoRun2GIol+YZzx0fCN+1SWd828po
+kzH7sgrim8G9psGEjIJmVLbGhPKKqNMhJS/hZIuREFd9yQ5zLlz//A/qQPsabaUw
+kNly6sCvEakSa9O5EP6bGAZL1WfN3XqvrUo2bF6EjqV5R3b6fQK4dg==
+-----END RSA PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIDHjCCAgYCCQDclmfqcI6z0zANBgkqhkiG9w0BAQsFADBcMQswCQYDVQQGEwJV
+UzELMAkGA1UECAwCQ0ExFjAUBgNVBAoMDUV4YW1wbGUsIEluYy4xKDAmBgNVBAMM
+H0dvb2QgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTkwNzEyMDk1MjUz
+WhcNMjkwNzA5MDk1MjUzWjBGMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExFjAU
+BgNVBAoMDUV4YW1wbGUsIEluYy4xEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBALuvH/nz1DAFpARq6iJrJuYHsn8K2Wh5
+KMjRw9H6lfnhxiHqzZT8CiKnOYxmZNYAtueezu0G4LkY02Mgl5YH/DrOtJ2ZnL2C
+okxHXVWks7pxa6B5WzN7Xnjzna2jvQ5qLfRUqN9YmgqxIU1TC0hL5UgoLTmca1c8
+9vlq2jurOwRiwEsIWhpOSvr72mthhCvKso+sm0Uka99Naa/2VKWCs1XLsn0gFRg/
+g/qCTO7FHO959wM/KHxjDVMdLW08vaPVzIM6KsmQ9nOi3ZD4VcnP23PD2jG9oyrs
+mxolx66FG99NYSJwBC3lJ+WAB+wHhupsEVnBBmNDmVTvrqsBQNzs790CAwEAATAN
+BgkqhkiG9w0BAQsFAAOCAQEAbQswzjiFCYaA7v7nwAbi6EPJgeqyITBduPZLFMLO
+WEhhOm3B9x2jhxkojlIaGq99sC3WXfX2A8xtOj3D0w2/rFyTk+4rfnbJD81qIuUN
+bcYajh2mCBl2j+OP34sxd5G79eDgY4N0wIXLoG+NfpOK5r3G5/hKC0ccd0srkunr
+WJi7kI512uA6KFqw2dUl6KFV0Tqvz8Dsp8UKxc6JoikZ1LTIUfNordaWeCt6Kwv+
+agsnY3+SKp8yv8GMu0FRbBrTkypXLrbKhtU4LuJkEgNvW1ymvN8vghAYty6YKPxi
+FGuf5L43MQEP9zUANUZrHGhThWoIEShb1qjHQnxjSgaV7A==
+-----END CERTIFICATE-----
diff --git a/extensions/http-curl/CMakeLists.txt
b/extensions/http-curl/CMakeLists.txt
index 2697475..178ce7f 100644
--- a/extensions/http-curl/CMakeLists.txt
+++ b/extensions/http-curl/CMakeLists.txt
@@ -24,11 +24,6 @@ include_directories(protocols client processors sitetosite)
file(GLOB SOURCES "*.cpp" "protocols/*.cpp" "client/*.cpp" "processors/*.cpp"
"sitetosite/*.cpp")
-if (USE_CURL_NSS)
- message("Using NSS")
- add_definitions(-DUSE_CURL_NSS)
-endif()
-
add_library(minifi-http-curl STATIC ${SOURCES})
set_property(TARGET minifi-http-curl PROPERTY POSITION_INDEPENDENT_CODE ON)
if(THREADS_HAVE_PTHREAD_ARG)
diff --git a/extensions/http-curl/client/HTTPClient.cpp
b/extensions/http-curl/client/HTTPClient.cpp
index b42ed56..edfc59e 100644
--- a/extensions/http-curl/client/HTTPClient.cpp
+++ b/extensions/http-curl/client/HTTPClient.cpp
@@ -120,8 +120,23 @@ void HTTPClient::forceClose() {
}
-void HTTPClient::setVerbose() {
+int HTTPClient::debug_callback(CURL *handle, curl_infotype type, char *data,
size_t size, void *userptr) {
+ std::shared_ptr<logging::Logger>* logger =
static_cast<std::shared_ptr<logging::Logger>*>(userptr);
+ if (logger == nullptr) {
+ return 0;
+ }
+ if (type == CURLINFO_TEXT) {
+ logging::LOG_DEBUG((*logger)) << "CURL(" <<
reinterpret_cast<void*>(handle) << "): " << std::string(data, size);
+ }
+ return 0;
+}
+
+void HTTPClient::setVerbose(bool use_stderr /*= false*/) {
curl_easy_setopt(http_session_, CURLOPT_VERBOSE, 1L);
+ if (!use_stderr) {
+ curl_easy_setopt(http_session_, CURLOPT_DEBUGDATA, &logger_);
+ curl_easy_setopt(http_session_, CURLOPT_DEBUGFUNCTION, &debug_callback);
+ }
}
void HTTPClient::initialize(const std::string &method, const std::string url,
const std::shared_ptr<minifi::controllers::SSLContextService>
ssl_context_service) {
@@ -148,6 +163,44 @@ void HTTPClient::setDisableHostVerification() {
curl_easy_setopt(http_session_, CURLOPT_SSL_VERIFYHOST, 0L);
}
+bool HTTPClient::setSpecificSSLVersion(SSLVersion specific_version) {
+#if CURL_AT_LEAST_VERSION(7, 54, 0)
+ CURLcode ret = CURLE_UNKNOWN_OPTION;
+ switch (specific_version) {
+ case SSLVersion::TLSv1_0:
+ ret = curl_easy_setopt(http_session_, CURLOPT_SSLVERSION,
CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_0);
+ break;
+ case SSLVersion::TLSv1_1:
+ ret = curl_easy_setopt(http_session_, CURLOPT_SSLVERSION,
CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_TLSv1_1);
+ break;
+ case SSLVersion::TLSv1_2:
+ ret = curl_easy_setopt(http_session_, CURLOPT_SSLVERSION,
CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_TLSv1_2);
+ break;
+ }
+
+ return ret == CURLE_OK;
+#else
+ return false;
+#endif
+}
+
+bool HTTPClient::setMinimumSSLVersion(SSLVersion minimum_version) {
+ CURLcode ret = CURLE_UNKNOWN_OPTION;
+ switch (minimum_version) {
+ case SSLVersion::TLSv1_0:
+ ret = curl_easy_setopt(http_session_, CURLOPT_SSLVERSION,
CURL_SSLVERSION_TLSv1_0);
+ break;
+ case SSLVersion::TLSv1_1:
+ ret = curl_easy_setopt(http_session_, CURLOPT_SSLVERSION,
CURL_SSLVERSION_TLSv1_1);
+ break;
+ case SSLVersion::TLSv1_2:
+ ret = curl_easy_setopt(http_session_, CURLOPT_SSLVERSION,
CURL_SSLVERSION_TLSv1_2);
+ break;
+ }
+
+ return ret == CURLE_OK;
+}
+
void HTTPClient::setConnectionTimeout(int64_t timeout) {
connect_timeout_ = timeout;
curl_easy_setopt(http_session_, CURLOPT_NOSIGNAL, 1);
@@ -194,9 +247,9 @@ std::string HTTPClient::escape(std::string
string_to_escape) {
return curl_easy_escape(http_session_, string_to_escape.c_str(),
string_to_escape.length());
}
-void HTTPClient::setPostFields(std::string input) {
+void HTTPClient::setPostFields(const std::string& input) {
curl_easy_setopt(http_session_, CURLOPT_POSTFIELDSIZE, input.length());
- curl_easy_setopt(http_session_, CURLOPT_POSTFIELDS, input.c_str());
+ curl_easy_setopt(http_session_, CURLOPT_COPYPOSTFIELDS, input.c_str());
}
void HTTPClient::setPostSize(size_t size) {
@@ -265,36 +318,6 @@ bool HTTPClient::submit() {
}
logger_->log_debug("Finished with %s", url_);
- std::string key = "";
- for (auto header_line : header_response_.header_tokens_) {
- unsigned int i = 0;
- for (i = 0; i < header_line.length(); i++) {
- if (header_line.at(i) == ':') {
- break;
- }
- }
- if (i >= header_line.length()) {
- if (key.empty())
- header_response_.append("HEADER", header_line);
- else
- header_response_.append(key, header_line);
- } else {
- key = header_line.substr(0, i);
- int length = header_line.length() - (i + 2);
- if (length <= 0) {
- continue;
- }
- std::string value = header_line.substr(i + 2, length);
- int end_find = value.size() - 1;
- for (; end_find > 0; end_find--) {
- if (value.at(end_find) > 32) {
- break;
- }
- }
- value = value.substr(0, end_find + 1);
- header_response_.append(key, value);
- }
- }
return true;
}
@@ -327,7 +350,9 @@ void HTTPClient::set_request_method(const std::string
method) {
if (my_method == "POST") {
curl_easy_setopt(http_session_, CURLOPT_POST, 1L);
} else if (my_method == "PUT") {
- curl_easy_setopt(http_session_, CURLOPT_PUT, 0L);
+ curl_easy_setopt(http_session_, CURLOPT_UPLOAD, 1L);
+ } else if (my_method == "HEAD") {
+ curl_easy_setopt(http_session_, CURLOPT_NOBODY, 1L);
} else if (my_method == "GET") {
} else {
curl_easy_setopt(http_session_, CURLOPT_CUSTOMREQUEST, my_method.c_str());
@@ -346,29 +371,36 @@ bool HTTPClient::matches(const std::string &value, const
std::string &sregex) {
}
void HTTPClient::configure_secure_connection(CURL *http_session) {
-#ifdef USE_CURL_NSS
- setVerbose();
- logger_->log_debug("Using NSS and certificate file %s",
ssl_context_service_->getCertificateFile());
- logger_->log_debug("Using NSS and CA certificate file %s",
ssl_context_service_->getCACertificate());
- curl_easy_setopt(http_session, CURLOPT_CAINFO, 0);
- if
(utils::StringUtils::endsWithIgnoreCase(ssl_context_service_->getCertificateFile(),"p12"))
{
- curl_easy_setopt(http_session, CURLOPT_SSLCERTTYPE, "P12");
- }
- else {
- curl_easy_setopt(http_session, CURLOPT_SSLCERTTYPE, "PEM");
+ logger_->log_debug("Using certificate file \"%s\"",
ssl_context_service_->getCertificateFile());
+ logger_->log_debug("Using private key file \"%s\"",
ssl_context_service_->getPrivateKeyFile());
+ logger_->log_debug("Using CA certificate file \"%s\"",
ssl_context_service_->getCACertificate());
+#if 0 // Reenable this path once we change from the direct manipulation of the
SSL context to using the cURL API
+ if (!ssl_context_service_->getCertificateFile().empty()) {
+ if
(utils::StringUtils::endsWithIgnoreCase(ssl_context_service_->getCertificateFile(),"p12"))
{
+ curl_easy_setopt(http_session, CURLOPT_SSLCERTTYPE, "P12");
+ }
+ else {
+ curl_easy_setopt(http_session, CURLOPT_SSLCERTTYPE, "PEM");
+ }
+ curl_easy_setopt(http_session, CURLOPT_SSLCERT,
ssl_context_service_->getCertificateFile().c_str());
}
- curl_easy_setopt(http_session, CURLOPT_SSLCERT,
ssl_context_service_->getCertificateFile().c_str());
- if
(utils::StringUtils::endsWithIgnoreCase(ssl_context_service_->getPrivateKeyFile(),"p12"))
{
- curl_easy_setopt(http_session, CURLOPT_SSLKEYTYPE, "P12");
+ if (!ssl_context_service_->getPrivateKeyFile().empty()) {
+ if
(utils::StringUtils::endsWithIgnoreCase(ssl_context_service_->getPrivateKeyFile(),"p12"))
{
+ curl_easy_setopt(http_session, CURLOPT_SSLKEYTYPE, "P12");
+ }
+ else {
+ curl_easy_setopt(http_session, CURLOPT_SSLKEYTYPE, "PEM");
+ }
+ curl_easy_setopt(http_session, CURLOPT_SSLKEY,
ssl_context_service_->getPrivateKeyFile().c_str());
+ curl_easy_setopt(http_session, CURLOPT_KEYPASSWD,
ssl_context_service_->getPassphrase().c_str());
}
- else {
- curl_easy_setopt(http_session, CURLOPT_SSLKEYTYPE, "PEM");
+ if (!ssl_context_service_->getCACertificate().empty()) {
+ curl_easy_setopt(http_session, CURLOPT_CAINFO,
ssl_context_service_->getCACertificate().c_str());
+ } else {
+ curl_easy_setopt(http_session, CURLOPT_CAINFO, nullptr);
}
- curl_easy_setopt(http_session, CURLOPT_SSLKEY,
ssl_context_service_->getPrivateKeyFile().c_str());
- curl_easy_setopt(http_session, CURLOPT_KEYPASSWD,
ssl_context_service_->getPassphrase().c_str());
- curl_easy_setopt(http_session, CURLOPT_CAINFO,
ssl_context_service_->getCACertificate().c_str());
+ curl_easy_setopt(http_session, CURLOPT_CAPATH, nullptr);
#else
- logger_->log_debug("Using OpenSSL and certificate file %s",
ssl_context_service_->getCertificateFile());
curl_easy_setopt(http_session, CURLOPT_SSL_CTX_FUNCTION,
&configure_ssl_context);
curl_easy_setopt(http_session, CURLOPT_SSL_CTX_DATA,
static_cast<void*>(ssl_context_service_.get()));
curl_easy_setopt(http_session, CURLOPT_CAINFO, 0);
@@ -377,7 +409,7 @@ void HTTPClient::configure_secure_connection(CURL
*http_session) {
}
bool HTTPClient::isSecure(const std::string &url) {
- if (url.find("https") != std::string::npos) {
+ if (url.find("https") == 0U) {
logger_->log_debug("%s is a secure url", url);
return true;
}
diff --git a/extensions/http-curl/client/HTTPClient.h
b/extensions/http-curl/client/HTTPClient.h
index d27a688..b876bac 100644
--- a/extensions/http-curl/client/HTTPClient.h
+++ b/extensions/http-curl/client/HTTPClient.h
@@ -19,7 +19,16 @@
#define __HTTP_UTILS_H__
#include "utils/HTTPClient.h"
+#ifdef WIN32
+#pragma comment(lib, "wldap32.lib" )
+#pragma comment(lib, "crypt32.lib" )
+#pragma comment(lib, "Ws2_32.lib")
+
+#define CURL_STATICLIB
#include <curl/curl.h>
+#else
+#include <curl/curl.h>
+#endif
#include <vector>
#include <iostream>
#include <string>
@@ -64,7 +73,9 @@ class HTTPClient : public BaseHTTPClient, public
core::Connectable {
~HTTPClient();
- virtual void setVerbose() override;
+ static int debug_callback(CURL *handle, curl_infotype type, char *data,
size_t size, void *userptr);
+
+ virtual void setVerbose(bool use_stderr = false) override;
void forceClose();
@@ -84,7 +95,7 @@ class HTTPClient : public BaseHTTPClient, public
core::Connectable {
virtual std::string escape(std::string string_to_escape) override;
- virtual void setPostFields(std::string input) override;
+ virtual void setPostFields(const std::string& input) override;
void setHeaders(struct curl_slist *list);
@@ -110,6 +121,10 @@ class HTTPClient : public BaseHTTPClient, public
core::Connectable {
void setDisableHostVerification() override;
+ bool setSpecificSSLVersion(SSLVersion specific_version) override;
+
+ bool setMinimumSSLVersion(SSLVersion minimum_version) override;
+
void setKeepAliveProbe(long probe){
keep_alive_probe_ = probe;
}
@@ -124,14 +139,13 @@ class HTTPClient : public BaseHTTPClient, public
core::Connectable {
}
const std::vector<std::string> &getHeaders() override {
- return header_response_.header_tokens_;
-
+ return header_response_.getHeaderLines();
}
void setInterface(const std::string &);
virtual const std::map<std::string, std::string> &getParsedHeaders()
override {
- return header_response_.header_mapping_;
+ return header_response_.getHeaderMap();
}
/**
@@ -143,7 +157,7 @@ class HTTPClient : public BaseHTTPClient, public
core::Connectable {
*/
const std::string getHeaderValue(const std::string &key) {
std::string ret;
- for (const auto &kv : header_response_.header_mapping_) {
+ for (const auto &kv : header_response_.getHeaderMap()) {
if (utils::StringUtils::equalsIgnoreCase(key, kv.first)) {
ret = kv.second;
break;
diff --git a/extensions/http-curl/tests/CMakeLists.txt
b/extensions/http-curl/tests/CMakeLists.txt
index 417a807..97b53ed 100644
--- a/extensions/http-curl/tests/CMakeLists.txt
+++ b/extensions/http-curl/tests/CMakeLists.txt
@@ -46,7 +46,8 @@ FOREACH(testfile ${CURL_UNIT_TESTS})
else ()
target_link_libraries ("${testfilename}" -Wl,--whole-archive
${ZLIB_LIBRARY} ${OPENSSL_LIBRARIES} minifi-http-curl minifi-civet-extensions
minifi-standard-processors -Wl,--no-whole-archive)
endif()
- MATH(EXPR CURL_INT_TEST_COUNT "${CURL_INT_TEST_COUNT}+1")
+ MATH(EXPR CURL_INT_TEST_COUNT "${CURL_INT_TEST_COUNT}+1")
+# add_test(NAME "${testfilename}" COMMAND "${testfilename}"
WORKING_DIRECTORY ${TEST_DIR})
ENDFOREACH()
FOREACH(testfile ${CURL_INTEGRATION_TESTS})
@@ -77,13 +78,17 @@ ENDFOREACH()
message("-- Finished building ${CURL_INT_TEST_COUNT} libcURL integration test
file(s)...")
+add_test(NAME HTTPClientTests COMMAND "HTTPClientTests" WORKING_DIRECTORY
${TEST_DIR})
+
add_test(NAME HttpGetIntegrationTest COMMAND HttpGetIntegrationTest
"${TEST_RESOURCES}/TestHTTPGet.yml" "${TEST_RESOURCES}/")
add_test(NAME C2UpdateTest COMMAND C2UpdateTest
"${TEST_RESOURCES}/TestHTTPGet.yml" "${TEST_RESOURCES}/")
add_test(NAME C2JstackTest COMMAND C2JstackTest
"${TEST_RESOURCES}/TestHTTPGet.yml" "${TEST_RESOURCES}/")
add_test(NAME C2UpdateAgentTest COMMAND C2UpdateAgentTest
"${TEST_RESOURCES}/TestHTTPGet.yml" "${TEST_RESOURCES}/")
add_test(NAME C2FailedUpdateTest COMMAND C2FailedUpdateTest
"${TEST_RESOURCES}/TestHTTPGet.yml" "${TEST_RESOURCES}/TestBad.yml"
"${TEST_RESOURCES}/")
add_test(NAME C2NullConfiguration COMMAND C2NullConfiguration
"${TEST_RESOURCES}/TestNull.yml" "${TEST_RESOURCES}/")
+if(OPENSSL_FOUND)
add_test(NAME HttpGetIntegrationTestSecure COMMAND HttpGetIntegrationTest
"${TEST_RESOURCES}/TestHTTPGetSecure.yml" "${TEST_RESOURCES}/")
+endif()
add_test(NAME HttpPostIntegrationTest COMMAND HttpPostIntegrationTest
"${TEST_RESOURCES}/TestHTTPPost.yml" "${TEST_RESOURCES}/")
if (NOT APPLE)
add_test(NAME HttpPostIntegrationTestChunked COMMAND HttpPostIntegrationTest
"${TEST_RESOURCES}/TestHTTPPostChunkedEncoding.yml" "${TEST_RESOURCES}/")
diff --git a/extensions/http-curl/tests/unit/CivetwebTests.cpp
b/extensions/http-curl/tests/unit/CivetwebTests.cpp
deleted file mode 100644
index 7122141..0000000
--- a/extensions/http-curl/tests/unit/CivetwebTests.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-/**
- *
- * 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 <uuid/uuid.h>
-#include <fstream>
-#include <chrono>
-#include <map>
-#include <memory>
-#include <set>
-#include <iostream>
-#include <processors/GenerateFlowFile.h>
-#include <processors/UpdateAttribute.h>
-#include <processors/LogAttribute.h>
-#include <processors/ListenHTTP.h>
-
-#include "TestBase.h"
-
-#include "processors/GetFile.h"
-#include "processors/PutFile.h"
-#include "client/HTTPClient.h"
-
-TEST_CASE("Test Creation of ListenHTTP", "[ListenHTTPreate]") { // NOLINT
- TestController testController;
- std::shared_ptr<core::Processor>
- processor =
std::make_shared<org::apache::nifi::minifi::processors::ListenHTTP>("processorname");
- REQUIRE(processor->getName() == "processorname");
-}
-
-TEST_CASE("Test GET Body", "[ListenHTTPGETBody]") { // NOLINT
- TestController testController;
-
- LogTestController::getInstance().setTrace<TestPlan>();
- LogTestController::getInstance().setTrace<processors::GenerateFlowFile>();
- LogTestController::getInstance().setTrace<processors::UpdateAttribute>();
- LogTestController::getInstance().setTrace<processors::LogAttribute>();
- LogTestController::getInstance().setTrace<processors::ListenHTTP>();
- LogTestController::getInstance().setTrace<processors::ListenHTTP::Handler>();
-
- auto plan = testController.createPlan();
- auto repo = std::make_shared<TestRepository>();
-
- // Define directory for test input
- std::string test_in_dir("/tmp/gt.XXXXXX");
- REQUIRE(!testController.createTempDirectory(&test_in_dir[0]).empty());
-
- // Define test input file
- std::string test_input_file(test_in_dir);
- test_input_file.append("/test");
- {
- std::ofstream os(test_input_file);
- os << "Hello response body" << std::endl;
- }
-
- // Build MiNiFi processing graph
- auto get = plan->addProcessor(
- "GetFile",
- "Get");
- plan->setProperty(
- get,
- "Input Directory",
- test_in_dir);
- auto update = plan->addProcessor(
- "UpdateAttribute",
- "Update",
- core::Relationship("success", "description"),
- true);
- plan->setProperty(
- update,
- "http.type",
- "response_body",
- true);
- auto log = plan->addProcessor(
- "LogAttribute",
- "Log",
- core::Relationship("success", "description"),
- true);
- auto listen = plan->addProcessor(
- "ListenHTTP",
- "ListenHTTP",
- core::Relationship("success", "description"),
- true);
- plan->setProperty(listen, "Listening Port", "0");
- listen->setAutoTerminatedRelationships({{"success", ""}});
-
- plan->runNextProcessor(); // Get
- plan->runNextProcessor(); // Update
- plan->runNextProcessor(); // Log
- plan->runNextProcessor(); // Listen
-
- auto raw_ptr =
dynamic_cast<org::apache::nifi::minifi::processors::ListenHTTP*>(listen.get());
- std::string protocol = std::string("http") + (raw_ptr->isSecure() ? "s" :
"");
- std::string portstr = raw_ptr->getPort();
- REQUIRE(LogTestController::getInstance().contains("Listening on port " +
portstr));
-
- utils::HTTPClient client(protocol + "://localhost:" + portstr +
"/contentListener/test");
- REQUIRE(client.submit());
- const auto &body_chars = client.getResponseBody();
- std::string response_body(body_chars.data(), body_chars.size());
- REQUIRE("Hello response body\n" == response_body);
-}
diff --git a/extensions/http-curl/tests/unit/HTTPClientTests.cpp
b/extensions/http-curl/tests/unit/HTTPClientTests.cpp
new file mode 100644
index 0000000..82919b0
--- /dev/null
+++ b/extensions/http-curl/tests/unit/HTTPClientTests.cpp
@@ -0,0 +1,96 @@
+/**
+ *
+ * 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 <uuid/uuid.h>
+#include <fstream>
+#include <map>
+#include <memory>
+#include <utility>
+#include <string>
+#include <set>
+#include "FlowController.h"
+#include "io/BaseStream.h"
+#include "TestBase.h"
+#include "processors/GetFile.h"
+#include "core/Core.h"
+#include "client/HTTPClient.h"
+#include "CivetServer.h"
+
+TEST_CASE("HTTPClientTestChunkedResponse", "[basic]") {
+ LogTestController::getInstance().setDebug<utils::HTTPClient>();
+
+ class Responder : public CivetHandler {
+ public:
+ void send_response(struct mg_connection *conn) {
+ mg_printf(conn, "HTTP/1.1 200 OK\r\n");
+ mg_printf(conn, "Content-Type: application/octet-stream\r\n");
+ mg_printf(conn, "Transfer-Encoding: chunked\r\n");
+ mg_printf(conn, "X-Custom-Test: whatever\r\n");
+ mg_printf(conn, "\r\n");
+ mg_send_chunk(conn, "foo", 3U);
+ mg_send_chunk(conn, "bar\r\n", 5U);
+ mg_send_chunk(conn, "buzz", 4U);
+ mg_send_chunk(conn, nullptr, 0U);
+ }
+
+ bool handleGet(CivetServer *server, struct mg_connection *conn) {
+ send_response(conn);
+ return true;
+ }
+
+ bool handlePost(CivetServer *server, struct mg_connection *conn) {
+ mg_printf(conn, "HTTP/1.1 100 Continue\r\n\r\n");
+
+ std::array<uint8_t, 16384U> buf;
+ while (mg_read(conn, buf.data(), buf.size()) > 0);
+
+ send_response(conn);
+ return true;
+ }
+ };
+
+ std::vector<std::string> options;
+ options.emplace_back("enable_keep_alive");
+ options.emplace_back("yes");
+ options.emplace_back("keep_alive_timeout_ms");
+ options.emplace_back("15000");
+ options.emplace_back("num_threads");
+ options.emplace_back("1");
+ options.emplace_back("listening_ports");
+ options.emplace_back("0");
+
+ CivetServer server(options);
+ Responder responder;
+ server.addHandler("**", responder);
+ const auto& vec = server.getListeningPorts();
+ REQUIRE(1U == vec.size());
+ const std::string port = std::to_string(vec.at(0));
+
+ utils::HTTPClient client;
+ client.initialize("GET", "http://localhost:" + port + "/testytesttest");
+
+ REQUIRE(client.submit());
+
+ const auto& headers = client.getParsedHeaders();
+ REQUIRE("whatever" == headers.at("X-Custom-Test"));
+
+ const std::vector<char>& response = client.getResponseBody();
+ REQUIRE("foobar\r\nbuzz" == std::string(response.begin(), response.end()));
+
+ LogTestController::getInstance().reset();
+}
\ No newline at end of file
diff --git a/extensions/librdkafka/CMakeLists.txt
b/extensions/librdkafka/CMakeLists.txt
index 5aeb383..4fc2eff 100644
--- a/extensions/librdkafka/CMakeLists.txt
+++ b/extensions/librdkafka/CMakeLists.txt
@@ -59,9 +59,7 @@ ExternalProject_Add(
"-DCMAKE_CXX_FLAGS=${CURL_CXX_FLAGS}"
EXCLUDE_FROM_ALL TRUE
)
-if ( NOT USE_SYSTEM_OPENSSL )
- add_dependencies(kafka-external libressl-portable)
-endif()
+add_dependencies(kafka-external libressl-portable)
set(KAFKA_INCLUDE "${BASE_DIR}/install/include/librdkafka/")
set(KAFKA_LIBRARY "${BYPRODUCT}")
add_dependencies(minifi-rdkafka-extensions kafka-external)
diff --git a/extensions/sftp/CMakeLists.txt b/extensions/sftp/CMakeLists.txt
index 2536ee1..073acb8 100644
--- a/extensions/sftp/CMakeLists.txt
+++ b/extensions/sftp/CMakeLists.txt
@@ -24,11 +24,6 @@ include_directories(client processors)
file(GLOB SOURCES "*.cpp" "client/*.cpp" "processors/*.cpp")
-if (USE_CURL_NSS)
- message("Using NSS")
- add_definitions(-DUSE_CURL_NSS)
-endif()
-
add_library(minifi-sftp STATIC ${SOURCES})
set_property(TARGET minifi-sftp PROPERTY POSITION_INDEPENDENT_CODE ON)
if(THREADS_HAVE_PTHREAD_ARG)
diff --git a/extensions/standard-processors/tests/CMakeLists.txt
b/extensions/standard-processors/tests/CMakeLists.txt
index 93b80fb..0d525c2 100644
--- a/extensions/standard-processors/tests/CMakeLists.txt
+++ b/extensions/standard-processors/tests/CMakeLists.txt
@@ -20,6 +20,9 @@
file(GLOB PROCESSOR_UNIT_TESTS "unit/*.cpp")
file(GLOB PROCESSOR_INTEGRATION_TESTS "integration/*.cpp")
+if (NOT OPENSSL_FOUND)
+ list(REMOVE_ITEM PROCESSOR_INTEGRATION_TESTS
"${CMAKE_CURRENT_SOURCE_DIR}/integration/SecureSocketGetTCPTest.cpp")
+endif()
SET(PROCESSOR_INT_TEST_COUNT 0)
@@ -76,10 +79,12 @@ message("-- Finished building ${INT_TEST_COUNT} integration
test file(s)...")
add_test(NAME TestExecuteProcess COMMAND TestExecuteProcess )
+if (OPENSSL_FOUND)
add_test(NAME SecureSocketGetTCPTest COMMAND SecureSocketGetTCPTest
"${TEST_RESOURCES}/TestGetTCPSecure.yml" "${TEST_RESOURCES}/")
add_test(NAME SecureSocketGetTCPTestEmptyPass COMMAND SecureSocketGetTCPTest
"${TEST_RESOURCES}/TestGetTCPSecureEmptyPass.yml" "${TEST_RESOURCES}/")
add_test(NAME SecureSocketGetTCPTestWithPassword COMMAND
SecureSocketGetTCPTest "${TEST_RESOURCES}/TestGetTCPSecureWithPass.yml"
"${TEST_RESOURCES}/")
add_test(NAME SecureSocketGetTCPTestWithPasswordFile COMMAND
SecureSocketGetTCPTest "${TEST_RESOURCES}/TestGetTCPSecureWithFilePass.yml"
"${TEST_RESOURCES}/")
+endif()
add_test(NAME TailFileTest COMMAND TailFileTest
"${TEST_RESOURCES}/TestTailFile.yml" "${TEST_RESOURCES}/")
diff --git a/fedora.sh b/fedora.sh
index 6b4a576..db28954 100644
--- a/fedora.sh
+++ b/fedora.sh
@@ -50,13 +50,8 @@ build_deps(){
if [ "$KEY" = "$option" ]; then
FOUND_VALUE="$VALUE"
echo $FOUND_VALUE
- if [ "$FOUND_VALUE" = "libcurl" ]; then
- INSTALLED+=("libcurl-devel")
- elif [ "$FOUND_VALUE" = "libpcap" ]; then
+ if [ "$FOUND_VALUE" = "libpcap" ]; then
INSTALLED+=("libpcap-devel")
- elif [ "$FOUND_VALUE" = "openssl" ]; then
- INSTALLED+=("openssl")
- INSTALLED+=("openssl-devel")
elif [ "$FOUND_VALUE" = "libusb" ]; then
INSTALLED+=("libusb-devel")
elif [ "$FOUND_VALUE" = "libpng" ]; then
diff --git a/libminifi/include/controllers/SSLContextService.h
b/libminifi/include/controllers/SSLContextService.h
index ce485e6..b56b163 100644
--- a/libminifi/include/controllers/SSLContextService.h
+++ b/libminifi/include/controllers/SSLContextService.h
@@ -23,6 +23,8 @@
#ifdef OPENSSL_SUPPORT
#include <openssl/err.h>
#include <openssl/ssl.h>
+#include <openssl/bio.h>
+#include <openssl/pkcs12.h>
#include "io/tls/TLSUtils.h"
#endif
#include <iostream>
@@ -146,41 +148,7 @@ class SSLContextService : public
core::controller::ControllerService {
}
#ifdef OPENSSL_SUPPORT
- bool configure_ssl_context(SSL_CTX *ctx) {
- if (!IsNullOrEmpty(certificate)) {
- if (SSL_CTX_use_certificate_file(ctx, certificate.c_str(),
SSL_FILETYPE_PEM) <= 0) {
- logger_->log_error("Could not create load certificate, error : %s",
std::strerror(errno));
- return false;
- }
- if (!IsNullOrEmpty(passphrase_)) {
- SSL_CTX_set_default_passwd_cb_userdata(ctx, &passphrase_);
- SSL_CTX_set_default_passwd_cb(ctx, minifi::io::tls::pemPassWordCb);
- }
- }
-
- if (!IsNullOrEmpty(private_key_)) {
- int retp = SSL_CTX_use_PrivateKey_file(ctx, private_key_.c_str(),
SSL_FILETYPE_PEM);
- if (retp != 1) {
- logger_->log_error("Could not create load private key,%i on %s error :
%s", retp, private_key_, std::strerror(errno));
- return false;
- }
-
- if (!SSL_CTX_check_private_key(ctx)) {
- logger_->log_error("Private key does not match the public certificate,
error : %s", std::strerror(errno));
- return false;
- }
- }
-
- SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, nullptr);
- int retp = SSL_CTX_load_verify_locations(ctx, ca_certificate_.c_str(), 0);
-
- if (retp == 0) {
- logger_->log_error("Can not load CA certificate, Exiting, error : %s",
std::strerror(errno));
- return false;
- }
-
- return true;
- }
+ bool configure_ssl_context(SSL_CTX *ctx);
#endif
virtual void onEnable();
@@ -198,6 +166,20 @@ class SSLContextService : public
core::controller::ControllerService {
std::string passphrase_file_;
std::string ca_certificate_;
+ static std::string getLatestOpenSSLErrorString() {
+ unsigned long err = ERR_peek_last_error();
+ if (err == 0U) {
+ return "";
+ }
+ char buf[4096];
+ ERR_error_string_n(err, buf, sizeof(buf));
+ return buf;
+ }
+
+ static bool isFileTypeP12(const std::string& filename) {
+ return utils::StringUtils::endsWithIgnoreCase(filename, "p12");
+ }
+
private:
std::shared_ptr<logging::Logger> logger_;
};
diff --git a/libminifi/include/utils/HTTPClient.h
b/libminifi/include/utils/HTTPClient.h
index 87bf659..d477f73 100644
--- a/libminifi/include/utils/HTTPClient.h
+++ b/libminifi/include/utils/HTTPClient.h
@@ -17,8 +17,11 @@
*/
#ifndef LIBMINIFI_INCLUDE_UTILS_BaseHTTPClient_H_
#define LIBMINIFI_INCLUDE_UTILS_BaseHTTPClient_H_
+
#include "ByteArrayCallback.h"
#include "controllers/SSLContextService.h"
+#include "core/Deprecated.h"
+
namespace org {
namespace apache {
namespace nifi {
@@ -66,37 +69,89 @@ struct HTTPReadCallback {
}
};
+enum class SSLVersion : uint8_t {
+ TLSv1_0,
+ TLSv1_1,
+ TLSv1_2,
+};
+
struct HTTPHeaderResponse {
public:
-
HTTPHeaderResponse(int max)
- : max_tokens_(max) {
+ : max_tokens_(max)
+ , parsed(false) {
}
- void append(const std::string &header) {
+ /* Deprecated, headers are stored internally and can be accessed by
getHeaderLines or getHeaderMap */
+ DEPRECATED(/*deprecated in*/ 0.7.0, /*will remove in */ 2.0) void
append(const std::string &header) {
if (max_tokens_ == -1 || (int32_t)header_tokens_.size() <= max_tokens_) {
header_tokens_.push_back(header);
}
}
- void append(const std::string &key, const std::string &value) {
+ /* Deprecated, headers are stored internally and can be accessed by
getHeaderLines or getHeaderMap */
+ DEPRECATED(/*deprecated in*/ 0.7.0, /*will remove in */ 2.0) void
append(const std::string &key, const std::string &value) {
header_mapping_[key].append(value);
}
int32_t max_tokens_;
std::vector<std::string> header_tokens_;
std::map<std::string, std::string> header_mapping_;
+ bool parsed;
static size_t receive_headers(void *buffer, size_t size, size_t nmemb, void
*userp) {
- HTTPHeaderResponse *pHeaders = (HTTPHeaderResponse *) (userp);
- int result = 0;
- if (pHeaders != NULL) {
- std::string s = "";
- s.append((char*) buffer, size * nmemb);
- pHeaders->append(s);
- result = size * nmemb;
+ HTTPHeaderResponse *pHeaders = static_cast<HTTPHeaderResponse*>(userp);
+ if (pHeaders == nullptr) {
+ return 0U;
}
- return result;
+ pHeaders->header_tokens_.emplace_back(static_cast<char*>(buffer), size *
nmemb);
+ return size * nmemb;
+ }
+
+ const std::vector<std::string>& getHeaderLines() const {
+ return header_tokens_;
+ }
+
+ const std::map<std::string, std::string>& getHeaderMap() {
+ if (!parsed) {
+ std::string last_key;
+ bool got_status_line = false;
+ for (const auto& header_line : header_tokens_) {
+ if (header_line.empty()) {
+ /* This should not happen */
+ continue;
+ }
+ if (!got_status_line) {
+ if (header_line.compare(0, 4, "HTTP") == 0) {
+ /* We got a status line now */
+ got_status_line = true;
+ header_mapping_.clear();
+ }
+ /* This is probably a chunked encoding trailer */
+ continue;
+ }
+ if (header_line == "\r\n") {
+ /* This is the end of the header */
+ got_status_line = false;
+ continue;
+ }
+ size_t separator_pos = header_line.find(':');
+ if (separator_pos == std::string::npos) {
+ if (!last_key.empty() && (header_line[0] == ' ' || header_line[0] ==
'\t')) {
+ /* This is a "folded header", which is deprecated
(https://www.ietf.org/rfc/rfc7230.txt) but here we are */
+ header_mapping_[last_key].append(" " +
utils::StringUtils::trim(header_line));
+ }
+ continue;
+ }
+ auto key = header_line.substr(0, separator_pos);
+ /* This will remove leading and trailing LWS and the ending CRLF from
the value */
+ auto value = utils::StringUtils::trim(header_line.substr(separator_pos
+ 1));
+ header_mapping_[key] = value;
+ last_key = key;
+ }
+ parsed = true;
+ }
+ return header_mapping_;
}
};
@@ -210,7 +265,7 @@ public:
virtual ~BaseHTTPClient() {
}
- virtual void setVerbose() {
+ virtual void setVerbose(bool use_stderr = false) {
}
virtual void initialize(const std::string &method, const std::string url =
"", const std::shared_ptr<minifi::controllers::SSLContextService>
ssl_context_service = nullptr) {
@@ -232,7 +287,7 @@ public:
return "";
}
- virtual void setPostFields(std::string input) {
+ virtual void setPostFields(const std::string& input) {
}
virtual bool submit() {
@@ -270,6 +325,14 @@ public:
virtual void setDisableHostVerification() {
}
+ virtual bool setSpecificSSLVersion(SSLVersion specific_version) {
+ return false;
+ }
+
+ virtual bool setMinimumSSLVersion(SSLVersion minimum_version) {
+ return false;
+ }
+
virtual const std::vector<std::string> &getHeaders() {
return headers_;
diff --git a/libminifi/src/controllers/SSLContextService.cpp
b/libminifi/src/controllers/SSLContextService.cpp
index 9899348..10944e2 100644
--- a/libminifi/src/controllers/SSLContextService.cpp
+++ b/libminifi/src/controllers/SSLContextService.cpp
@@ -47,9 +47,87 @@ void SSLContextService::initialize() {
initialized_ = true;
}
+bool SSLContextService::configure_ssl_context(SSL_CTX *ctx) {
+ if (!IsNullOrEmpty(certificate)) {
+ if (isFileTypeP12(certificate)) {
+ BIO* fp = BIO_new(BIO_s_file());
+ if (fp == nullptr) {
+ logging::LOG_ERROR(logger_) << "Failed create new file BIO, " <<
getLatestOpenSSLErrorString();
+ return false;
+ }
+ if (BIO_read_filename(fp, certificate.c_str()) <= 0) {
+ logging::LOG_ERROR(logger_) << "Failed to read certificate file " <<
certificate << ", " << getLatestOpenSSLErrorString();
+ BIO_free(fp);
+ return false;
+ }
+ PKCS12* p12 = d2i_PKCS12_bio(fp, nullptr);
+ BIO_free(fp);
+ if (p12 == nullptr) {
+ logging::LOG_ERROR(logger_) << "Failed to DER decode certificate file
" << certificate << ", " << getLatestOpenSSLErrorString();
+ return false;
+ }
+ EVP_PKEY* pkey = nullptr;
+ X509* cert = nullptr;
+ if (!PKCS12_parse(p12, passphrase_.c_str(), &pkey, &cert, nullptr
/*ca*/)) {
+ logging::LOG_ERROR(logger_) << "Failed to parse certificate file " <<
certificate << " as PKCS#12, " << getLatestOpenSSLErrorString();
+ PKCS12_free(p12);
+ return false;
+ }
+ PKCS12_free(p12);
+ if (SSL_CTX_use_certificate(ctx, cert) != 1) {
+ logging::LOG_ERROR(logger_) << "Failed to set certificate from " <<
certificate << ", " << getLatestOpenSSLErrorString();
+ EVP_PKEY_free(pkey);
+ X509_free(cert);
+ return false;
+ }
+ if (SSL_CTX_use_PrivateKey(ctx, pkey) != 1) {
+ logging::LOG_ERROR(logger_) << "Failed to set private key from " <<
certificate << ", " << getLatestOpenSSLErrorString();
+ EVP_PKEY_free(pkey);
+ X509_free(cert);
+ return false;
+ }
+ EVP_PKEY_free(pkey);
+ X509_free(cert);
+ } else {
+ if (SSL_CTX_use_certificate_file(ctx, certificate.c_str(),
SSL_FILETYPE_PEM) <= 0) {
+ logging::LOG_ERROR(logger_) << "Could not create load certificate " <<
certificate << ", " << getLatestOpenSSLErrorString();
+ return false;
+ }
+
+ if (!IsNullOrEmpty(passphrase_)) {
+ SSL_CTX_set_default_passwd_cb_userdata(ctx, &passphrase_);
+ SSL_CTX_set_default_passwd_cb(ctx, minifi::io::tls::pemPassWordCb);
+ }
+
+ if (!IsNullOrEmpty(private_key_)) {
+ int retp = SSL_CTX_use_PrivateKey_file(ctx, private_key_.c_str(),
SSL_FILETYPE_PEM);
+ if (retp != 1) {
+ logging::LOG_ERROR(logger_) << "Could not create load private key, "
<< retp << " on " << private_key_ << ", " << getLatestOpenSSLErrorString();
+ return false;
+ }
+ }
+ }
+
+ if (!SSL_CTX_check_private_key(ctx)) {
+ logging::LOG_ERROR(logger_) << "Private key does not match the public
certificate, " << getLatestOpenSSLErrorString();
+ return false;
+ }
+ }
+
+ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, nullptr);
+ int retp = SSL_CTX_load_verify_locations(ctx, ca_certificate_.c_str(), 0);
+
+ if (retp == 0) {
+ logging::LOG_ERROR(logger_) << "Can not load CA certificate, Exiting, " <<
getLatestOpenSSLErrorString();
+ return false;
+ }
+
+ return true;
+}
+
/**
* If OpenSSL is not installed we may still continue operations. Nullptr will
- * be returned and it will be up to the caller to determien if this failure is
+ * be returned and it will be up to the caller to determine if this failure is
* recoverable.
*/
std::unique_ptr<SSLContext> SSLContextService::createSSLContext() {
@@ -62,34 +140,15 @@ std::unique_ptr<SSLContext>
SSLContextService::createSSLContext() {
method = TLSv1_2_client_method();
SSL_CTX *ctx = SSL_CTX_new(method);
- if (!IsNullOrEmpty(certificate)) {
- if (SSL_CTX_use_certificate_file(ctx, certificate.c_str(),
SSL_FILETYPE_PEM) <= 0) {
- logger_->log_error("Could not create load certificate, error : %s",
std::strerror(errno));
- return nullptr;
- }
- if (!IsNullOrEmpty(passphrase_)) {
- SSL_CTX_set_default_passwd_cb_userdata(ctx, &passphrase_);
- SSL_CTX_set_default_passwd_cb(ctx, io::tls::pemPassWordCb);
- }
+ if (ctx == nullptr) {
+ return nullptr;
}
- if (!IsNullOrEmpty(private_key_)) {
- int retp = SSL_CTX_use_PrivateKey_file(ctx, private_key_.c_str(),
SSL_FILETYPE_PEM);
- if (retp != 1) {
- logger_->log_error("Could not create load private key,%i on %s error :
%s", retp, private_key_, std::strerror(errno));
- return nullptr;
- }
-
- if (!SSL_CTX_check_private_key(ctx)) {
- logger_->log_error("Private key does not match the public certificate,
error : %s", std::strerror(errno));
- return nullptr;
- }
+ if (!configure_ssl_context(ctx)) {
+ SSL_CTX_free(ctx);
+ return nullptr;
}
- int retp = SSL_CTX_load_verify_locations(ctx, ca_certificate_.c_str(), 0);
- if (retp == 0) {
- logger_->log_error("Can not load CA certificate %s, Exiting, error : %s",
ca_certificate_, std::strerror(errno));
- }
return std::unique_ptr<SSLContext>(new SSLContext(ctx));
#else
return nullptr;
diff --git a/libminifi/test/resources/cn.p12 b/libminifi/test/resources/cn.p12
new file mode 100644
index 0000000..56d0bd3
Binary files /dev/null and b/libminifi/test/resources/cn.p12 differ
diff --git a/libminifi/test/unit/SocketTests.cpp
b/libminifi/test/unit/SocketTests.cpp
index 7e943b4..bae9bf6 100644
--- a/libminifi/test/unit/SocketTests.cpp
+++ b/libminifi/test/unit/SocketTests.cpp
@@ -25,9 +25,7 @@
#include <utility>
#include "../TestBase.h"
#include "io/StreamFactory.h"
-#include "io/ClientSocket.h"
-#include "io/ServerSocket.h"
-#include "io/tls/TLSSocket.h"
+#include "io/Sockets.h"
#include "utils/ThreadPool.h"
using Sockets = org::apache::nifi::minifi::io::Socket;
@@ -173,6 +171,7 @@ TEST_CASE("TestSocketWriteTestAfterClose", "[TestSocket7]")
{
server.closeStream();
}
+#ifdef OPENSSL_ENABLED
std::atomic<uint8_t> counter;
std::mt19937_64 seed { std::random_device { }() };
bool createSocket() {
@@ -240,3 +239,4 @@ TEST_CASE("TestTLSContextCreationNullptr",
"[TestSocket10]") {
minifi::io::TLSSocket *tls = dynamic_cast<minifi::io::TLSSocket*>(socket);
REQUIRE(tls == nullptr);
}
+#endif
diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt
index ecd5f6b..c0cf8c2 100644
--- a/main/CMakeLists.txt
+++ b/main/CMakeLists.txt
@@ -90,16 +90,12 @@ endif ()
# Include OpenSSL
if (OPENSSL_FOUND)
- if (NOT USE_SYSTEM_OPENSSL)
- if (APPLE)
- target_link_libraries(minifiexe -Wl,-all_load
${OPENSSL_LIBRARIES})
- elseif(NOT WIN32)
- target_link_libraries(minifiexe -Wl,--whole-archive
${OPENSSL_LIBRARIES} -Wl,--no-whole-archive)
- else()
- target_link_libraries(minifiexe ${OPENSSL_LIBRARIES})
- endif()
+ if (APPLE)
+ target_link_libraries(minifiexe -Wl,-all_load
${OPENSSL_LIBRARIES})
+ elseif(NOT WIN32)
+ target_link_libraries(minifiexe -Wl,--whole-archive
${OPENSSL_LIBRARIES} -Wl,--no-whole-archive)
else()
- target_link_libraries(minifiexe ${OPENSSL_LIBRARIES})
+ target_link_libraries(minifiexe ${OPENSSL_LIBRARIES})
endif()
include_directories(${OPENSSL_INCLUDE_DIR})
endif(OPENSSL_FOUND)
diff --git a/rheldistro.sh b/rheldistro.sh
index 4466c63..ec1396f 100644
--- a/rheldistro.sh
+++ b/rheldistro.sh
@@ -91,14 +91,8 @@ build_deps(){
VALUE=${cmake_opt#*:}
if [ "$KEY" = "$option" ]; then
FOUND_VALUE="$VALUE"
- if [ "$FOUND_VALUE" = "libcurl" ]; then
- INSTALLED+=("libcurl-devel")
- elif [ "$FOUND_VALUE" = "libpcap" ]; then
+ if [ "$FOUND_VALUE" = "libpcap" ]; then
INSTALLED+=("libpcap-devel")
- elif [ "$FOUND_VALUE" = "openssl" ]; then
- INSTALLED+=("openssl")
- INSTALLED+=("openssl-devel")
- INSTALLED+=("openssl-static")
elif [ "$FOUND_VALUE" = "libusb" ]; then
INSTALLED+=("libusb-devel")
elif [ "$FOUND_VALUE" = "libpng" ]; then
diff --git a/suse.sh b/suse.sh
index 2b3c826..dceb710 100644
--- a/suse.sh
+++ b/suse.sh
@@ -82,13 +82,8 @@ build_deps(){
VALUE=${cmake_opt#*:}
if [ "$KEY" = "$option" ]; then
FOUND_VALUE="$VALUE"
- if [ "$FOUND_VALUE" = "libcurl" ]; then
- INSTALLED+=("libcurl-devel")
- elif [ "$FOUND_VALUE" = "libpcap" ]; then
+ if [ "$FOUND_VALUE" = "libpcap" ]; then
INSTALLED+=("libpcap-devel")
- elif [ "$FOUND_VALUE" = "openssl" ]; then
- INSTALLED+=("openssl")
- INSTALLED+=("openssl-devel")
elif [ "$FOUND_VALUE" = "libusb" ]; then
INSTALLED+=("libusb-devel")
elif [ "$FOUND_VALUE" = "libpng" ]; then
diff --git a/thirdparty/civetweb-1.10/src/civetweb.c
b/thirdparty/civetweb-1.10/src/civetweb.c
index 43412b0..95945d3 100644
--- a/thirdparty/civetweb-1.10/src/civetweb.c
+++ b/thirdparty/civetweb-1.10/src/civetweb.c
@@ -16237,10 +16237,13 @@ worker_thread_run(struct worker_thread_args
*thread_args)
mg_free(conn->request_info.client_cert);
conn->request_info.client_cert = 0;
}
- }
+ } else {
+ /* make sure the connection is cleaned up on SSL failure */
+ close_connection(conn);
+ }
#endif
- } else {
- /* process HTTP connection */
+ } else {
+ /* process HTTP connection */
process_new_connection(conn);
}