aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/elevator/IOUtil.hpp62
-rwxr-xr-xscripts/test_exe_template.sh4
-rw-r--r--src/elevator/CMakeLists.txt4
-rw-r--r--src/elevator/IOUtil.cpp289
-rw-r--r--test/elevator/CMakeLists.txt2
-rw-r--r--test/elevator/test_02_iostream.cpp119
-rw-r--r--test/elevator/test_data.cpp13
-rw-r--r--test/elevator/test_data.hpp30
8 files changed, 476 insertions, 47 deletions
diff --git a/include/elevator/IOUtil.hpp b/include/elevator/IOUtil.hpp
index d4d8293..92b47e6 100644
--- a/include/elevator/IOUtil.hpp
+++ b/include/elevator/IOUtil.hpp
@@ -4,12 +4,14 @@
*/
#ifndef IOUTIL_HPP_
-#define IOUtil_HPP_
+#define IOUTIL_HPP_
#include <string>
#include <cstdint>
+#include <functional>
#include <jau/basic_types.hpp>
+#include <jau/ringbuffer.hpp>
#include <botan_all.h>
@@ -17,11 +19,28 @@ namespace elevator {
class IOUtil {
public:
- static bool file_exists(const std::string& name);
- static bool remove(const std::string& fname);
+ static bool file_exists(const std::string& name) noexcept;
+ static bool remove(const std::string& fname) noexcept;
typedef std::function<void (Botan::secure_vector<uint8_t>& /* data */, bool /* is_final */)> StreamConsumerFunc;
+ typedef jau::ringbuffer<uint8_t, uint8_t, size_t> ByteRingbuffer;
+
+ /**
+ * Operation result value
+ */
+ enum class result_t : int8_t {
+ /** Operation failed. */
+ FAILED = -1,
+
+ /** Operation still in progress. */
+ NONE = 0,
+
+ /** Operation succeeded. */
+ SUCCESS = 1
+ };
+ typedef jau::ordered_atomic<result_t, std::memory_order::memory_order_relaxed> relaxed_atomic_result_t;
+
/**
*
* @param input_file
@@ -51,10 +70,45 @@ class IOUtil {
static ssize_t read_stream(Botan::DataSource& in, Botan::secure_vector<uint8_t>& buffer,
StreamConsumerFunc consumer_fn);
+ /**
+ *
+ * @param url
+ * @param buffer
+ * @param consumer_fn
+ * @return total bytes read or -1 if error
+ */
static ssize_t read_http_get(const std::string& url, Botan::secure_vector<uint8_t>& buffer,
StreamConsumerFunc consumer_fn);
- static void print_stats(const std::string &prefix, const uint64_t out_bytes_total, uint64_t td_ms);
+ static const size_t BEST_HTTP_RINGBUFFER_SIZE;
+
+ /**
+ * Asynchronous http read content using a byte jau::ringbuffer,
+ * allowing parallel reading.
+ *
+ * @param url the URL of the content to read
+ * @param buffer the ringbuffer destination to write into
+ * @param content_length tracking the content_length
+ * @param total_read tracking the total_read
+ * @param result tracking the result_t
+ */
+ static void read_http_get(const std::string& url, ByteRingbuffer& buffer,
+ jau::relaxed_atomic_ssize_t& content_length,
+ jau::relaxed_atomic_ssize_t& total_read,
+ relaxed_atomic_result_t& result) noexcept;
+
+ /**
+ * Asynchronous http read content using a byte jau::ringbuffer,
+ * allowing parallel reading.
+ *
+ * @param url the URL of the content to read
+ * @param buffer the ringbuffer destination to write into
+ * @param result tracking the result_t
+ */
+ static void read_http_get(const std::string& url, ByteRingbuffer& buffer,
+ relaxed_atomic_result_t& result) noexcept;
+
+ static void print_stats(const std::string &prefix, const uint64_t out_bytes_total, uint64_t td_ms) noexcept;
};
} // namespace elevator
diff --git a/scripts/test_exe_template.sh b/scripts/test_exe_template.sh
index 3f63af9..c7a0214 100755
--- a/scripts/test_exe_template.sh
+++ b/scripts/test_exe_template.sh
@@ -1,5 +1,8 @@
#!/bin/sh
+export elevator_debug=true
+export elevator_verbose=true
+
# Arguments:
# --perf_analysis special performance analysis using 3rd party tools
# -v normal dummy for full benchmarking
@@ -67,6 +70,7 @@ runit() {
#export ASAN_OPTIONS=verbosity=1:malloc_context_size=20
#export ASAN_OPTIONS=print_stats:halt_on_error:replace_intrin
+ cd $build_dir/test/elevator/
$EXE_WRAPPER $build_dir/test/elevator/$bname $*
}
diff --git a/src/elevator/CMakeLists.txt b/src/elevator/CMakeLists.txt
index bd0037b..287bee2 100644
--- a/src/elevator/CMakeLists.txt
+++ b/src/elevator/CMakeLists.txt
@@ -32,10 +32,10 @@ target_link_libraries (
${CMAKE_THREAD_LIBS_INIT}
)
-if(CMAKE_COMPILER_IS_GNUCC)
+# if(CMAKE_COMPILER_IS_GNUCC)
# botan fails on cast align etc
target_compile_options(elevator PUBLIC "-Wno-cast-align" "-Wno-unused-parameter")
-endif(CMAKE_COMPILER_IS_GNUCC)
+# endif(CMAKE_COMPILER_IS_GNUCC)
if(USE_STRIP)
add_custom_command(TARGET elevator POST_BUILD
diff --git a/src/elevator/IOUtil.cpp b/src/elevator/IOUtil.cpp
index 205cbad..6d47fd6 100644
--- a/src/elevator/IOUtil.cpp
+++ b/src/elevator/IOUtil.cpp
@@ -5,6 +5,7 @@
#include <fstream>
#include <iostream>
+#include <chrono>
#include <elevator/elevator.hpp>
@@ -14,6 +15,9 @@
#include <curl/curl.h>
+#include <thread>
+#include <pthread.h>
+
// #define USE_CXX17lib_FS 1
#if USE_CXX17lib_FS
#include <filesystem>
@@ -22,12 +26,12 @@
using namespace elevator;
-bool IOUtil::file_exists(const std::string& name) {
+bool IOUtil::file_exists(const std::string& name) noexcept {
std::ifstream f(name);
return f.good() && f.is_open();
}
-bool IOUtil::remove(const std::string& fname) {
+bool IOUtil::remove(const std::string& fname) noexcept {
#if USE_CXX17lib_FS
const fs::path fname2 = fname;
return fs::remove(fname);
@@ -87,7 +91,7 @@ ssize_t IOUtil::read_stream(Botan::DataSource& in, Botan::secure_vector<uint8_t>
return total;
}
-struct curl_glue_t {
+struct curl_glue1_t {
CURL *curl_handle;
ssize_t content_length;
ssize_t total_read;
@@ -95,8 +99,8 @@ struct curl_glue_t {
IOUtil::StreamConsumerFunc consumer_fn;
};
-static size_t consume_curl0(void *ptr, size_t size, size_t nmemb, void *stream) {
- curl_glue_t * cg = (curl_glue_t*)stream;
+static size_t consume_curl1(void *ptr, size_t size, size_t nmemb, void *stream) noexcept {
+ curl_glue1_t * cg = (curl_glue1_t*)stream;
if( 0 > cg->content_length ) {
curl_off_t v = 0;
@@ -110,7 +114,8 @@ static size_t consume_curl0(void *ptr, size_t size, size_t nmemb, void *stream)
memcpy(cg->buffer.data(), ptr, realsize);
cg->total_read += realsize;
- bool is_final = 0 < cg->content_length ? cg->total_read >= cg->content_length : false;
+ const bool is_final = 0 == realsize ||
+ ( 0 < cg->content_length ) ? cg->total_read >= cg->content_length : false;
cg->consumer_fn(cg->buffer, is_final);
return realsize;
@@ -118,36 +123,270 @@ static size_t consume_curl0(void *ptr, size_t size, size_t nmemb, void *stream)
ssize_t IOUtil::read_http_get(const std::string& url, Botan::secure_vector<uint8_t>& buffer,
StreamConsumerFunc consumer_fn) {
- /* init the curl session */
- CURL *curl_handle = curl_easy_init();
+ std::vector<char> errorbuffer;
+ errorbuffer.reserve(CURL_ERROR_SIZE);
+ CURLcode res;
+
+ /* init the curl session */
+ CURL *curl_handle = curl_easy_init();
+ if( nullptr == curl_handle ) {
+ ERR_PRINT("Error setting up http url %s, null curl handle", url.c_str());
+ return -1;
+ }
+
+ curl_glue1_t cg = { curl_handle, -1, 0, buffer, consumer_fn };
+
+ res = curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errorbuffer.data());
+ if( CURLE_OK != res ) {
+ ERR_PRINT("Error setting up http url %s, error %d %d",
+ url.c_str(), (int)res, curl_easy_strerror(res));
+ goto errout;
+ }
+
+ /* set URL to get here */
+ res = curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
+ if( CURLE_OK != res ) {
+ ERR_PRINT("Error setting up http url %s, error %d %d",
+ url.c_str(), (int)res, errorbuffer.data());
+ goto errout;
+ }
+
+ /* Switch on full protocol/debug output while testing */
+ res = curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 0L);
+ if( CURLE_OK != res ) {
+ ERR_PRINT("Error setting up http url %s, error %d %d",
+ url.c_str(), (int)res, errorbuffer.data());
+ goto errout;
+ }
+
+ /* disable progress meter, set to 0L to enable it */
+ res = curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L);
+ if( CURLE_OK != res ) {
+ ERR_PRINT("Error setting up http url %s, error %d %d",
+ url.c_str(), (int)res, errorbuffer.data());
+ goto errout;
+ }
+
+ /* send all data to this function */
+ res = curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, consume_curl1);
+ if( CURLE_OK != res ) {
+ ERR_PRINT("Error setting up http url %s, error %d %d",
+ url.c_str(), (int)res, errorbuffer.data());
+ goto errout;
+ }
+
+ /* write the page body to this file handle */
+ res = curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void*)&cg);
+ if( CURLE_OK != res ) {
+ ERR_PRINT("Error setting up http url %s, error %d %d",
+ url.c_str(), (int)res, errorbuffer.data());
+ goto errout;
+ }
+
+ /* performs the tast, blocking! */
+ res = curl_easy_perform(curl_handle);
+ if( CURLE_OK != res ) {
+ ERR_PRINT("Error processing http url %s, error %d %d",
+ url.c_str(), (int)res, errorbuffer.data());
+ goto errout;
+ }
+
+ /* cleanup curl stuff */
+ curl_easy_cleanup(curl_handle);
+ return cg.total_read;
+
+errout:
+ curl_easy_cleanup(curl_handle);
+ return -1;
+}
+
+struct curl_glue2_t {
+ curl_glue2_t(CURL *_curl_handle,
+ jau::relaxed_atomic_ssize_t* _content_length,
+ bool _content_length_mine,
+ jau::relaxed_atomic_ssize_t* _total_read,
+ bool _total_read_mine,
+ IOUtil::ByteRingbuffer& _buffer,
+ IOUtil::relaxed_atomic_result_t& _result)
+ : curl_handle(_curl_handle),
+ content_length(_content_length),
+ content_length_mine(_content_length_mine),
+ total_read(_total_read),
+ total_read_mine(_total_read_mine),
+ buffer(_buffer),
+ result(_result)
+ {}
+
+ CURL *curl_handle;
+ jau::relaxed_atomic_ssize_t* content_length;
+ bool content_length_mine;
+ jau::relaxed_atomic_ssize_t* total_read;
+ bool total_read_mine;
+ IOUtil::ByteRingbuffer& buffer;
+ IOUtil::relaxed_atomic_result_t& result;
+};
+
+static size_t consume_curl2(void *ptr, size_t size, size_t nmemb, void *stream) noexcept {
+ curl_glue2_t * cg = (curl_glue2_t*)stream;
+
+ if( 0 > *cg->content_length ) {
+ curl_off_t v = 0;
+ const CURLcode r = curl_easy_getinfo(cg->curl_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &v);
+ if( CURLE_OK == r ) {
+ *cg->content_length = v;
+ }
+ }
+ ssize_t total_read = *cg->total_read;
+ const size_t realsize = size * nmemb;
+ DBG_PRINT("consume_curl2.0 realsize % " PRIu64 ", rb %s", realsize, cg->buffer.toString().c_str() );
+ cg->buffer.putBlocking(reinterpret_cast<uint8_t*>(ptr),
+ reinterpret_cast<uint8_t*>(ptr)+realsize, 0 /* timeoutMS */);
+
+ total_read += realsize;
+ *cg->total_read = total_read;
+ const bool is_final = 0 == realsize ||
+ ( 0 < *cg->content_length ) ? total_read >= *cg->content_length : false;
+ if( is_final ) {
+ cg->result = IOUtil::result_t::SUCCESS;
+ }
+
+ DBG_PRINT("consume_curl2.X realsize % " PRIu64 ", total %" PRIi64 ", result %d, rb %s",
+ realsize, total_read, cg->result.load(), cg->buffer.toString().c_str() );
- curl_glue_t cg = { curl_handle, -1, 0, buffer, consumer_fn };
+ return realsize;
+}
+
+static void read_http_get_thread(const char *url, std::unique_ptr<curl_glue2_t> && cg) noexcept {
+ std::vector<char> errorbuffer;
+ errorbuffer.reserve(CURL_ERROR_SIZE);
+ CURLcode res;
+
+ /* init the curl session */
+ CURL *curl_handle = curl_easy_init();
+ if( nullptr == curl_handle ) {
+ ERR_PRINT("Error setting up http url %s, null curl handle", url);
+ goto errout;
+ }
+ cg->curl_handle = curl_handle;
+
+ res = curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errorbuffer.data());
+ if( CURLE_OK != res ) {
+ ERR_PRINT("Error setting up http url %s, error %d %d",
+ url, (int)res, curl_easy_strerror(res));
+ goto errout;
+ }
+
+ /* set URL to get here */
+ res = curl_easy_setopt(curl_handle, CURLOPT_URL, url);
+ if( CURLE_OK != res ) {
+ ERR_PRINT("Error setting up http url %s, error %d %d",
+ url, (int)res, errorbuffer.data());
+ goto errout;
+ }
+
+ /* Switch on full protocol/debug output while testing */
+ res = curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 0L);
+ if( CURLE_OK != res ) {
+ ERR_PRINT("Error setting up http url %s, error %d %d",
+ url, (int)res, errorbuffer.data());
+ goto errout;
+ }
+
+ /* disable progress meter, set to 0L to enable it */
+ res = curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L);
+ if( CURLE_OK != res ) {
+ ERR_PRINT("Error setting up http url %s, error %d %d",
+ url, (int)res, errorbuffer.data());
+ goto errout;
+ }
+
+ /* send all data to this function */
+ res = curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, consume_curl2);
+ if( CURLE_OK != res ) {
+ ERR_PRINT("Error setting up http url %s, error %d %d",
+ url, (int)res, errorbuffer.data());
+ goto errout;
+ }
+
+ /* write the page body to this file handle */
+ res = curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void*)cg.get());
+ if( CURLE_OK != res ) {
+ ERR_PRINT("Error setting up http url %s, error %d %d",
+ url, (int)res, errorbuffer.data());
+ goto errout;
+ }
+
+ /* performs the tast, blocking! */
+ res = curl_easy_perform(curl_handle);
+ if( CURLE_OK != res ) {
+ ERR_PRINT("Error processing http url %s, error %d %d",
+ url, (int)res, errorbuffer.data());
+ goto errout;
+ }
+
+ /* cleanup curl stuff */
+ cg->result = IOUtil::result_t::SUCCESS;
+ goto cleanup;
+
+errout:
+ cg->result = IOUtil::result_t::FAILED;
+
+cleanup:
+ if( nullptr != curl_handle ) {
+ curl_easy_cleanup(curl_handle);
+ }
+
+ if( cg->content_length_mine ) {
+ delete cg->content_length;
+ cg->content_length = nullptr;
+ }
+ if( cg->total_read_mine ) {
+ delete cg->total_read;
+ cg->total_read = nullptr;
+ }
+ return;
+}
+
+const size_t IOUtil::BEST_HTTP_RINGBUFFER_SIZE = 2*CURL_MAX_WRITE_SIZE;
- /* set URL to get here */
- curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
+void IOUtil::read_http_get(const std::string& url, ByteRingbuffer& buffer,
+ jau::relaxed_atomic_ssize_t& content_length,
+ jau::relaxed_atomic_ssize_t& total_read,
+ relaxed_atomic_result_t& result) noexcept {
+ /* init user referenced values */
+ content_length = -1;
+ total_read = 0;
+ result = IOUtil::result_t::NONE;
- /* Switch on full protocol/debug output while testing */
- curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 0L);
+ if( buffer.capacity() < BEST_HTTP_RINGBUFFER_SIZE ) {
+ buffer.recapacity( BEST_HTTP_RINGBUFFER_SIZE );
+ }
+
+ std::unique_ptr<curl_glue2_t> cg ( std::make_unique<curl_glue2_t>(nullptr, &content_length, false, &total_read, false, buffer, result ) );
- /* disable progress meter, set to 0L to enable it */
- curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L);
+ std::thread http_thread00(&::read_http_get_thread, url.c_str(), std::move(cg)); // @suppress("Invalid arguments")
+ http_thread00.detach();
+}
- /* send all data to this function */
- curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, consume_curl0);
+void IOUtil::read_http_get(const std::string& url, ByteRingbuffer& buffer,
+ relaxed_atomic_result_t& result) noexcept {
- /* write the page body to this file handle */
- curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void*)&cg);
+ /* init user referenced values */
+ result = IOUtil::result_t::NONE;
- /* performs the tast, blocking! */
- curl_easy_perform(curl_handle);
+ if( buffer.capacity() < BEST_HTTP_RINGBUFFER_SIZE ) {
+ buffer.recapacity( BEST_HTTP_RINGBUFFER_SIZE );
+ }
- /* cleanup curl stuff */
- curl_easy_cleanup(curl_handle);
+ jau::relaxed_atomic_ssize_t* content_length = new jau::relaxed_atomic_ssize_t(-1);
+ jau::relaxed_atomic_ssize_t* total_read = new jau::relaxed_atomic_ssize_t(0);
+ std::unique_ptr<curl_glue2_t> cg ( std::make_unique<curl_glue2_t>(nullptr, content_length, true, total_read, true, buffer, result ) );
- return cg.total_read;
+ std::thread http_thread00(&::read_http_get_thread, url.c_str(), std::move(cg)); // @suppress("Invalid arguments")
+ http_thread00.detach();
}
-void IOUtil::print_stats(const std::string &prefix, const uint64_t out_bytes_total, uint64_t td_ms) {
+void IOUtil::print_stats(const std::string &prefix, const uint64_t out_bytes_total, uint64_t td_ms) noexcept {
if( jau::environment::get().verbose ) {
jau::PLAIN_PRINT(true, "%s: Duration %s s, %s ms", prefix.c_str(),
diff --git a/test/elevator/CMakeLists.txt b/test/elevator/CMakeLists.txt
index 28c07ad..751747a 100644
--- a/test/elevator/CMakeLists.txt
+++ b/test/elevator/CMakeLists.txt
@@ -25,7 +25,7 @@ string( REPLACE ".cpp" "" BASENAMES_IDIOMATIC_EXAMPLES "${SOURCES_IDIOMATIC_EXAM
set( TARGETS_IDIOMATIC_EXAMPLES ${BASENAMES_IDIOMATIC_EXAMPLES} )
foreach( name ${TARGETS_IDIOMATIC_EXAMPLES} )
- add_executable(${name} ${name}.cpp)
+ add_executable(${name} ${name}.cpp test_data.cpp)
target_link_libraries(${name} elevator catch2l)
add_dependencies(${name} elevator catch2l)
add_test (NAME ${name} COMMAND ${name})
diff --git a/test/elevator/test_02_iostream.cpp b/test/elevator/test_02_iostream.cpp
index 7a7d112..e2b66a5 100644
--- a/test/elevator/test_02_iostream.cpp
+++ b/test/elevator/test_02_iostream.cpp
@@ -11,6 +11,9 @@
#include <fstream>
#include <iostream>
+#include <thread>
+#include <pthread.h>
+
// #define CATCH_CONFIG_RUNNER
#define CATCH_CONFIG_MAIN
#include <catch2/catch_amalgamated.hpp>
@@ -18,6 +21,8 @@
#include <elevator/elevator.hpp>
+#include "test_data.hpp"
+
#include <jau/debug.hpp>
extern "C" {
@@ -26,21 +31,105 @@ extern "C" {
using namespace elevator;
-TEST_CASE( "Elevator Test 01 IOStream", "[io][stream]" ) {
+class Test02IOStream : public TestData {
+ public:
+ static void test01() {
+ const std::string url_input = url_input_root + basename_64kB + ".enc";
- // const std::string url_input("http://jordan/deployment/data-382MB.mkv.enc");
- const std::string url_input("http://jordan/deployment/data-64kB.bin.enc");
+ std::ofstream outfile("test02_01_out.bin", std::ios::out | std::ios::binary);
+ REQUIRE( outfile.good() );
+ REQUIRE( outfile.is_open() );
- Botan::secure_vector<uint8_t> buffer(4096);
- ssize_t calls0 = 0;
- ssize_t total0 = 0;
- auto consume = [&](Botan::secure_vector<uint8_t>& data, bool is_final) noexcept {
- calls0++;
- total0 += data.size();
- jau::PLAIN_PRINT("test", "#% " PRIi64 ": consumed size % " PRIu64 ", total %" PRIi64 ", capacity %" PRIu64 ", final %d",
- calls0, data.size(), total0, data.capacity(), is_final );
- };
- ssize_t total1 = IOUtil::read_http_get(url_input, buffer, consume);
+ Botan::secure_vector<uint8_t> buffer(4096);
+ ssize_t consumed_calls = 0;
+ ssize_t consumed_total_bytes = 0;
+ auto consume = [&](Botan::secure_vector<uint8_t>& data, bool is_final) noexcept {
+ consumed_calls++;
+ consumed_total_bytes += data.size();
+ outfile.write(reinterpret_cast<char*>(data.data()), data.size());
+ jau::PLAIN_PRINT(true, "test02io01 #% " PRIi64 ": consumed size % " PRIu64 ", total %" PRIi64 ", capacity %" PRIu64 ", final %d",
+ consumed_calls, data.size(), consumed_total_bytes, data.capacity(), is_final );
+ };
+ ssize_t http_total_bytes = IOUtil::read_http_get(url_input, buffer, consume);
+ const ssize_t out_bytes_total = outfile.tellp();
+ jau::PLAIN_PRINT(true, "test02io01 Done: total %" PRIi64 ", capacity %" PRIu64,
+ consumed_total_bytes, buffer.capacity());
- REQUIRE( total0 == total1 );
-}
+ REQUIRE( consumed_total_bytes == http_total_bytes );
+ REQUIRE( consumed_total_bytes == out_bytes_total );
+ }
+
+ static void test02() {
+ const std::string url_input = url_input_root + basename_64kB + ".enc";
+
+ std::ofstream outfile("test02_02_out.bin", std::ios::out | std::ios::binary);
+ REQUIRE( outfile.good() );
+ REQUIRE( outfile.is_open() );
+
+ constexpr const size_t buffer_size = 4096;
+ IOUtil::ByteRingbuffer rb(0x00, IOUtil::BEST_HTTP_RINGBUFFER_SIZE);
+ jau::relaxed_atomic_ssize_t content_length;
+ jau::relaxed_atomic_ssize_t http_total_bytes;
+ IOUtil::relaxed_atomic_result_t result;
+
+ IOUtil::read_http_get(url_input, rb, content_length, http_total_bytes, result);
+
+ Botan::secure_vector<uint8_t> buffer(buffer_size);
+ ssize_t consumed_loops = 0;
+ size_t consumed_total_bytes = 0;
+
+ while( IOUtil::result_t::NONE == result || !rb.isEmpty() ) {
+ consumed_loops++;
+ // const size_t consumed_bytes = content_length >= 0 ? std::min(buffer_size, content_length - consumed_total_bytes) : rb.getSize();
+ const size_t consumed_bytes = rb.getBlocking(buffer.data(), buffer_size, 1, 0 /* timeoutMS */);
+ consumed_total_bytes += consumed_bytes;
+ jau::PLAIN_PRINT(true, "test02io02.0 #% " PRIi64 ": consumed size % " PRIu64 ", total %" PRIi64 ", result %d, rb %s",
+ consumed_loops, consumed_bytes, consumed_total_bytes, result.load(), rb.toString().c_str() );
+ outfile.write(reinterpret_cast<char*>(buffer.data()), consumed_bytes);
+ }
+ const ssize_t out_bytes_total = outfile.tellp();
+ jau::PLAIN_PRINT(true, "test02io02.X Done: total %" PRIi64 ", result %d, rb %s",
+ consumed_total_bytes, result.load(), rb.toString().c_str() );
+
+ REQUIRE( content_length == http_total_bytes );
+ REQUIRE( content_length == out_bytes_total );
+ REQUIRE( IOUtil::result_t::SUCCESS == result );
+ }
+
+ static void test03() {
+ const std::string url_input = url_input_root + basename_64kB + ".enc";
+
+ std::ofstream outfile("test02_02_out.bin", std::ios::out | std::ios::binary);
+ REQUIRE( outfile.good() );
+ REQUIRE( outfile.is_open() );
+
+ constexpr const size_t buffer_size = 4096;
+ IOUtil::ByteRingbuffer rb(0x00, IOUtil::BEST_HTTP_RINGBUFFER_SIZE);
+ IOUtil::relaxed_atomic_result_t result;
+
+ IOUtil::read_http_get(url_input, rb, result);
+
+ Botan::secure_vector<uint8_t> buffer(buffer_size);
+ ssize_t consumed_loops = 0;
+ size_t consumed_total_bytes = 0;
+
+ while( IOUtil::result_t::NONE == result || !rb.isEmpty() ) {
+ consumed_loops++;
+ const size_t consumed_bytes = rb.getBlocking(buffer.data(), buffer_size, 1, 0 /* timeoutMS */);
+ consumed_total_bytes += consumed_bytes;
+ jau::PLAIN_PRINT(true, "test02io03.0 #% " PRIi64 ": consumed size % " PRIu64 ", total %" PRIi64 ", result %d, rb %s",
+ consumed_loops, consumed_bytes, consumed_total_bytes, result.load(), rb.toString().c_str() );
+ outfile.write(reinterpret_cast<char*>(buffer.data()), consumed_bytes);
+ }
+ const ssize_t out_bytes_total = outfile.tellp();
+ jau::PLAIN_PRINT(true, "test02io03.X Done: total %" PRIi64 ", result %d, rb %s",
+ consumed_total_bytes, result.load(), rb.toString().c_str() );
+
+ REQUIRE( 0 < out_bytes_total );
+ REQUIRE( IOUtil::result_t::SUCCESS == result );
+ }
+};
+
+METHOD_AS_TEST_CASE( Test02IOStream::test01, "Elevator Test 02 IOStream 01");
+METHOD_AS_TEST_CASE( Test02IOStream::test02, "Elevator Test 02 IOStream 02");
+METHOD_AS_TEST_CASE( Test02IOStream::test03, "Elevator Test 02 IOStream 03");
diff --git a/test/elevator/test_data.cpp b/test/elevator/test_data.cpp
new file mode 100644
index 0000000..14a3aac
--- /dev/null
+++ b/test/elevator/test_data.cpp
@@ -0,0 +1,13 @@
+#include "test_data.hpp"
+
+const std::string TestData::enc_pub_key_fname = "../../../keys/terminal_rsa.pub.pem";
+const std::string TestData::dec_sec_key_fname = "../../../keys/terminal_rsa";
+const std::string TestData::dec_sec_key_passphrase = "";
+const std::string TestData::sign_pub_key_fname = "../../../keys/host_rsa.pub.pem";
+const std::string TestData::sign_sec_key_fname = "../../../keys/host_rsa";
+const std::string TestData::sign_sec_key_passphrase = "";
+
+const std::string TestData::url_input_root = "http://jordan/deployment/elevator/";
+const std::string TestData::basename_64kB = "data-64kB.bin"; // + '.enc' for encrypted
+const std::string TestData::basename_382MB = "data-382MB.mkv"; // + '.enc' for encrypted
+const std::string TestData::basename_1GB = "data-1GB.mkv"; // + '.enc' for encrypted
diff --git a/test/elevator/test_data.hpp b/test/elevator/test_data.hpp
new file mode 100644
index 0000000..542787b
--- /dev/null
+++ b/test/elevator/test_data.hpp
@@ -0,0 +1,30 @@
+/*
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2020 ZAFENA AB
+ */
+
+#ifndef TEST_DATA_HPP_
+#define TEST_DATA_HPP_
+
+#include <iostream>
+#include <cassert>
+#include <cinttypes>
+#include <cstring>
+
+class TestData {
+ public:
+ static constexpr const bool overwrite = true;
+ static const std::string enc_pub_key_fname;
+ static const std::string dec_sec_key_fname;
+ static const std::string dec_sec_key_passphrase;
+ static const std::string sign_pub_key_fname;
+ static const std::string sign_sec_key_fname;
+ static const std::string sign_sec_key_passphrase;
+
+ static const std::string url_input_root;
+ static const std::string basename_64kB;
+ static const std::string basename_382MB;
+ static const std::string basename_1GB;
+};
+
+#endif /* TEST_DATA_HPP_ */