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);
                }
 

Reply via email to