aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2022-06-04 12:17:49 +0200
committerSven Gothel <[email protected]>2022-06-04 12:17:49 +0200
commitc968b79825902597f85c235e3e20b61e1a25e24b (patch)
tree6d5d4b8d66a5fb8663cbeb49766cafae0b2b4040
parent4239b2623dc979c10eeeda32467d20bd04363a3d (diff)
Bump v0.13.0: jau::io changes for robust and simplified ByteInStream usage sourced as file or via remote URLv0.13.0
* `string_util.hpp`: Add `jau::to_string()` support for `std::string` and `std::string_view` as well as for `std::vector<T>` lists * Add namespace `jau::io::uri`, limited URI scheme functionality to query whether implementation may handle the protocol. - Query *libcurl* supported protocols at runtime - Test for local file protocol - Test whether protocol in given uri is supported by *libcurl* * `jau::io::read_url_stream()`, sync and async, return immediately if protocol in given url is not supportet - async variant returns `std::unique_ptr<std::thread>`, where a nullptr is used for no support * `jau::io::ByteInStream_File` recognizes the local file protocol and cuts off `file://` is used. - Fix: Recognition of a non-existing path, unaccessbile path or non-file case properly * `jau::io::ByteInStream_URL` recognizes a non supported protocol via async `jau::io::read_url_stream()`. * Added convenient `jau::io::std::unique_ptr<ByteInStream> to_ByteInStream()` - Returning either a `jau::io::ByteInStream_File`, `jau::io::ByteInStream_URL` or nullptr if `path_or_url` is not supported * Make Java class `org.jau.ney.Uri` standalone, drop dependencies for easier reusage.
-rw-r--r--README.md16
-rw-r--r--include/jau/byte_stream.hpp36
-rw-r--r--include/jau/io_util.hpp72
-rw-r--r--include/jau/string_util.hpp48
-rw-r--r--java_net/org/jau/net/Uri.java106
-rw-r--r--src/byte_stream.cpp80
-rw-r--r--src/io_util.cpp76
-rw-r--r--test/test_bytestream01.cpp148
-rw-r--r--test/test_iostream01.cpp90
9 files changed, 595 insertions, 77 deletions
diff --git a/README.md b/README.md
index 1d80284..8d47e9b 100644
--- a/README.md
+++ b/README.md
@@ -159,6 +159,22 @@ a Raspi-arm64, Raspi-armhf or PC-amd64 target image.
* First stable release (TODO)
+**0.13.0**
+
+* `string_util.hpp`: Add `jau::to_string()` support for `std::string` and `std::string_view` as well as for `std::vector<T>` lists
+* Add namespace `jau::io::uri`, limited URI scheme functionality to query whether implementation may handle the protocol.
+ - Query *libcurl* supported protocols at runtime
+ - Test for local file protocol
+ - Test whether protocol in given uri is supported by *libcurl*
+* `jau::io::read_url_stream()`, sync and async, return immediately if protocol in given url is not supportet
+ - async variant returns `std::unique_ptr<std::thread>`, where a nullptr is used for no support
+* `jau::io::ByteInStream_File` recognizes the local file protocol and cuts off `file://` is used.
+ - Fix: Recognition of a non-existing path, unaccessbile path or non-file case properly
+* `jau::io::ByteInStream_URL` recognizes a non supported protocol via async `jau::io::read_url_stream()`.
+* Added convenient `jau::io::std::unique_ptr<ByteInStream> to_ByteInStream()`
+ - Returning either a `jau::io::ByteInStream_File`, `jau::io::ByteInStream_URL` or nullptr if `path_or_url` is not supported
+* Make Java class `org.jau.ney.Uri` standalone, drop dependencies for easier reusage.
+
**0.12.0**
* Minor changes
diff --git a/include/jau/byte_stream.hpp b/include/jau/byte_stream.hpp
index e68c431..f15c292 100644
--- a/include/jau/byte_stream.hpp
+++ b/include/jau/byte_stream.hpp
@@ -42,6 +42,8 @@
// Include Botan header files before this one to be integrated w/ Botan!
// #include <botan_all.h>
+using namespace jau::fractions_i64_literals;
+
namespace jau::io {
/** \addtogroup IOUtils
@@ -365,15 +367,19 @@ namespace jau::io {
size_t peek(uint8_t[], size_t, size_t) const NOEXCEPT_BOTAN override;
bool check_available(size_t n) NOEXCEPT_BOTAN override;
bool end_of_data() const NOEXCEPT_BOTAN override;
- bool error() const noexcept override { return m_source.bad(); }
+ bool error() const noexcept override { return nullptr == m_source || m_source->bad(); }
std::string id() const NOEXCEPT_BOTAN override;
/**
* Construct a Stream-Based byte input stream from filesystem path
- * @param file the path to the file
+ *
+ * In case the given path is a local file URI starting with `file://`, see jau::io::uri::is_local_file_protocol(),
+ * the leading `file://` is cut off and the remainder being used.
+ *
+ * @param path the path to the file, maybe a local file URI
* @param use_binary whether to treat the file as binary (default) or use platform character conversion
*/
- ByteInStream_File(const std::string& file, bool use_binary = true) noexcept;
+ ByteInStream_File(const std::string& path, bool use_binary = true) noexcept;
ByteInStream_File(const ByteInStream_File&) = delete;
@@ -395,7 +401,7 @@ namespace jau::io {
private:
const std::string m_identifier;
- mutable std::ifstream m_source;
+ mutable std::unique_ptr<std::ifstream> m_source;
uint64_t m_content_size;
uint64_t m_bytes_consumed;
};
@@ -403,7 +409,9 @@ namespace jau::io {
/**
* This class represents a Ringbuffer-Based byte input stream with a URL connection provisioned data feed.
*
- * Standard implementation uses curl, hence all protocols supported by curl are supported.
+ * Standard implementation uses [curl](https://curl.se/),
+ * hence all [*libcurl* network protocols](https://curl.se/docs/url-syntax.html) are supported,
+ * jau::io::uri::supported_protocols().
*/
class ByteInStream_URL final : public ByteInStream {
public:
@@ -450,7 +458,7 @@ namespace jau::io {
/**
* Construct a ringbuffer backed Http byte input stream
* @param url the URL of the data to read
- * @param timeout maximum duration in fractions of seconds to wait @ check_available(), where fractions_i64::zero waits infinitely
+ * @param timeout maximum duration in fractions of seconds to wait @ check_available() for next bytes, where fractions_i64::zero waits infinitely
*/
ByteInStream_URL(const std::string& url, const jau::fraction_i64& timeout) noexcept;
@@ -483,11 +491,25 @@ namespace jau::io {
jau::relaxed_atomic_uint64 m_content_size;
jau::relaxed_atomic_uint64 m_total_xfered;
relaxed_atomic_async_io_result_t m_result;
- std::thread m_url_thread;
+ std::unique_ptr<std::thread> m_url_thread;
uint64_t m_bytes_consumed;
};
/**
+ * Parses the given path_or_uri, if it matches a supported protocol, see jau::io::uri::protocol_supported(),
+ * but is not a local file, see jau::io::uri::is_local_file_protocol(), ByteInStream_URL is being attempted.
+ *
+ * If the above fails, ByteInStream_File is attempted.
+ *
+ * If non of the above leads to a ByteInStream without ByteInStream::error(), nullptr is returned.
+ *
+ * @param path_or_uri given path or uri for with a ByteInStream instance shall be established.
+ * @param timeout a timeout in case ByteInStream_URL is being used as maximum dureation to wait for next bytes at ByteInStream_URL::check_available(), defaults to 20_s
+ * @return a working ByteInStream w/o ByteInStream::error() or nullptr
+ */
+ std::unique_ptr<ByteInStream> to_ByteInStream(const std::string& path_or_uri, jau::fraction_i64 timeout=20_s) noexcept;
+
+ /**
* This class represents a Ringbuffer-Based byte input stream with an externally provisioned data feed.
*/
class ByteInStream_Feed final : public ByteInStream {
diff --git a/include/jau/io_util.hpp b/include/jau/io_util.hpp
index c74a875..2ca8475 100644
--- a/include/jau/io_util.hpp
+++ b/include/jau/io_util.hpp
@@ -84,14 +84,63 @@ namespace jau::io {
StreamConsumerFunc consumer_fn) noexcept;
/**
+ * Limited URI scheme functionality to query whether implementation may handle the protocol.
+ *
+ * The URI scheme functionality exposed here is limited and only provided to decide whether the used implementation
+ * is able to handle the protocol. This is not a replacement for a proper URI class.
+ */
+ namespace uri {
+ /**
+ * Returns a list of supported protocol supported by [*libcurl* network protocols](https://curl.se/docs/url-syntax.html),
+ * queried at runtime.
+ * @see protocol_supported()
+ */
+ std::vector<std::string_view> supported_protocols() noexcept;
+
+ /**
+ * Returns the valid uri-scheme-view from given uri-view,
+ * which is empty if no valid scheme is included.
+ *
+ * @param uri an uri-view
+ * @return valid uri-scheme-view, empty if non found
+ */
+ std::string_view get_scheme(const std::string_view& uri) noexcept;
+
+ /**
+ * Returns true if the uri-scheme of given uri-view matches a supported by [*libcurl* network protocols](https://curl.se/docs/url-syntax.html) otherwise false.
+ *
+ * The *libcurl* supported protocols is queried at runtime, see supported_protocols().
+ *
+ * @param uri an uri-view to test
+ * @return true if the uri-scheme of given uri is supported, otherwise false.
+ * @see supported_protocols()
+ * @see get_scheme()
+ */
+ bool protocol_supported(const std::string_view& uri) noexcept;
+
+ /**
+ * Returns true if the uri-scheme of given uri-view matches the local `file` protocol, i.e. starts with `file://`.
+ * @param uri an uri-view to test
+ */
+ bool is_local_file_protocol(const std::string_view& uri) noexcept;
+ }
+
+ /**
* Synchronous URL stream reader using the given StreamConsumerFunc consumer_fn.
*
* To abort streaming, user may return `false` from the given `consumer_func`.
*
+ * Standard implementation uses [curl](https://curl.se/),
+ * hence all [*libcurl* network protocols](https://curl.se/docs/url-syntax.html) are supported,
+ * see jau::io::uri::supported_protocols().
+ *
+ * If the uri-sheme doesn't match a supported protocol, see jau::io::uri::protocol_supported(),
+ * function returns immediately with zero bytes.
+ *
* @param url the URL to open a connection to and stream bytes from
* @param buffer secure std::vector buffer, passed down to consumer_fn
* @param consumer_fn StreamConsumerFunc consumer for each received heap of bytes, returning true to continue stream of false to abort.
- * @return total bytes read or 0 if error
+ * @return total bytes read or 0 if transmission error or protocol of given url is not supported
*/
uint64_t read_url_stream(const std::string& url,
secure_vector<uint8_t>& buffer,
@@ -102,20 +151,27 @@ namespace jau::io {
*
* To abort streaming, user may set given reference `results` to a value other than async_io_result_t::NONE.
*
+ * Standard implementation uses [curl](https://curl.se/),
+ * hence all [*libcurl* network protocols](https://curl.se/docs/url-syntax.html) are supported,
+ * see jau::io::uri::supported_protocols().
+ *
+ * If the uri-sheme doesn't match a supported protocol, see jau::io::uri::protocol_supported(),
+ * function returns with nullptr.
+ *
* @param url the URL to open a connection to and stream bytes from
* @param buffer the ringbuffer destination to write into
* @param has_content_length indicating whether content_length is known from server
* @param content_length tracking the content_length
* @param total_read tracking the total_read
* @param result reference to tracking async_io_result_t. If set to other than async_io_result_t::NONE while streaming, streaming is aborted.
- * @return the url background reading thread
+ * @return the url background reading thread unique-pointer or nullptr if protocol of given url is not supported
*/
- std::thread read_url_stream(const std::string& url,
- ByteRingbuffer& buffer,
- jau::relaxed_atomic_bool& has_content_length,
- jau::relaxed_atomic_uint64& content_length,
- jau::relaxed_atomic_uint64& total_read,
- relaxed_atomic_async_io_result_t& result) noexcept;
+ std::unique_ptr<std::thread> read_url_stream(const std::string& url,
+ ByteRingbuffer& buffer,
+ jau::relaxed_atomic_bool& has_content_length,
+ jau::relaxed_atomic_uint64& content_length,
+ jau::relaxed_atomic_uint64& total_read,
+ relaxed_atomic_async_io_result_t& result) noexcept;
void print_stats(const std::string& prefix, const uint64_t& out_bytes_total, const jau::fraction_i64& td) noexcept;
diff --git a/include/jau/string_util.hpp b/include/jau/string_util.hpp
index 6673171..8a6a37f 100644
--- a/include/jau/string_util.hpp
+++ b/include/jau/string_util.hpp
@@ -201,9 +201,31 @@ namespace jau {
{
return std::to_string(ref);
}
+
+ template< class value_type,
+ std::enable_if_t<!std::is_integral_v<value_type> &&
+ !std::is_floating_point_v<value_type> &&
+ std::is_base_of_v<std::string, value_type>,
+ bool> = true>
+ inline std::string to_string(const value_type & ref) {
+ return ref;
+ }
+
+ template< class value_type,
+ std::enable_if_t<!std::is_integral_v<value_type> &&
+ !std::is_floating_point_v<value_type> &&
+ !std::is_base_of_v<std::string, value_type> &&
+ std::is_base_of_v<std::string_view, value_type>,
+ bool> = true>
+ inline std::string to_string(const value_type & ref) {
+ return std::string(ref);
+ }
+
template< class value_type,
std::enable_if_t<!std::is_integral_v<value_type> &&
!std::is_floating_point_v<value_type> &&
+ !std::is_base_of_v<std::string, value_type> &&
+ !std::is_base_of_v<std::string_view, value_type> &&
std::is_pointer_v<value_type>,
bool> = true>
inline std::string to_string(const value_type & ref)
@@ -214,6 +236,8 @@ namespace jau {
template< class value_type,
std::enable_if_t<!std::is_integral_v<value_type> &&
!std::is_floating_point_v<value_type> &&
+ !std::is_base_of_v<std::string, value_type> &&
+ !std::is_base_of_v<std::string_view, value_type> &&
!std::is_pointer_v<value_type> &&
jau::has_toString_v<value_type>,
bool> = true>
@@ -224,6 +248,8 @@ namespace jau {
template< class value_type,
std::enable_if_t<!std::is_integral_v<value_type> &&
!std::is_floating_point_v<value_type> &&
+ !std::is_base_of_v<std::string, value_type> &&
+ !std::is_base_of_v<std::string_view, value_type> &&
!std::is_pointer_v<value_type> &&
!jau::has_toString_v<value_type> &&
jau::has_to_string_v<value_type>,
@@ -235,6 +261,8 @@ namespace jau {
template< class value_type,
std::enable_if_t<!std::is_integral_v<value_type> &&
!std::is_floating_point_v<value_type> &&
+ !std::is_base_of_v<std::string, value_type> &&
+ !std::is_base_of_v<std::string_view, value_type> &&
!std::is_pointer_v<value_type> &&
!jau::has_toString_v<value_type> &&
!jau::has_to_string_v<value_type> &&
@@ -247,6 +275,8 @@ namespace jau {
template< class value_type,
std::enable_if_t<!std::is_integral_v<value_type> &&
!std::is_floating_point_v<value_type> &&
+ !std::is_base_of_v<std::string, value_type> &&
+ !std::is_base_of_v<std::string_view, value_type> &&
!std::is_pointer_v<value_type> &&
!jau::has_toString_v<value_type> &&
!jau::has_to_string_v<value_type> &&
@@ -257,6 +287,24 @@ namespace jau {
return "jau::to_string<T> not available for "+type_cue<value_type>::print("unknown", TypeTraitGroup::ALL);
}
+ template<typename T>
+ std::string to_string(std::vector<T> const &list, const std::string& delim)
+ {
+ if ( list.empty() ) {
+ return std::string();
+ }
+ bool need_delim = false;
+ std::string res;
+ for(const T& e : list) {
+ if( need_delim ) {
+ res.append( delim ).append( " " );
+ }
+ res.append( to_string( e ) );
+ need_delim = true;
+ }
+ return res;
+ }
+
/**@}*/
} // namespace jau
diff --git a/java_net/org/jau/net/Uri.java b/java_net/org/jau/net/Uri.java
index c243e8e..f4fb09b 100644
--- a/java_net/org/jau/net/Uri.java
+++ b/java_net/org/jau/net/Uri.java
@@ -36,10 +36,6 @@ import java.net.URISyntaxException;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
-import org.jau.io.IOUtil;
-import org.jau.sys.Debug;
-import org.jau.sys.PropertyAccess;
-
/**
* This class implements an immutable Uri as defined by <a href="https://tools.ietf.org/html/rfc2396">RFC 2396</a>.
* <p>
@@ -164,6 +160,9 @@ import org.jau.sys.PropertyAccess;
* @since 0.3.0
*/
public class Uri {
+ private static final boolean DEBUG = false;
+ private static final boolean DEBUG_SHOWFIX = false;
+ /**
private static final boolean DEBUG;
private static final boolean DEBUG_SHOWFIX;
@@ -171,7 +170,7 @@ public class Uri {
Debug.initSingleton();
DEBUG = IOUtil.DEBUG || Debug.debug("Uri");
DEBUG_SHOWFIX = PropertyAccess.isPropertyDefined("jau.debug.Uri.ShowFix", true);
- }
+ } */
/**
* Usually used to fix a path from a previously contained and opaque Uri,
@@ -1125,7 +1124,7 @@ public class Uri {
* specification RFC2396 or could not be parsed correctly.
*/
public static Uri valueOf(final File file) throws URISyntaxException {
- return Uri.valueOfFilepath(IOUtil.slashify(file.getAbsolutePath(), true, file.isDirectory()));
+ return Uri.valueOfFilepath(Util.slashify(file.getAbsolutePath(), true, file.isDirectory()));
}
/**
@@ -1457,7 +1456,7 @@ public class Uri {
return false; // nothing to cut-off
}
pathBuf.setLength(0);
- pathBuf.append( IOUtil.cleanPathString( pathS ) );
+ pathBuf.append( Util.cleanPathString( pathS ) );
cleaned = pathBuf.length() != pathS.length();
}
@@ -1489,7 +1488,7 @@ public class Uri {
// 2nd round of cleaning!
final String pathS = pathBuf.toString();
pathBuf.setLength(0);
- pathBuf.append( IOUtil.cleanPathString( pathS ) );
+ pathBuf.append( Util.cleanPathString( pathS ) );
}
return true; // continue processing w/ buffer
}
@@ -2543,4 +2542,95 @@ public class Uri {
private static void failExpecting(final Encoded input, final String expected, final int p) throws URISyntaxException {
fail(input, "Expecting " + expected, p);
}
+
+ static class Util {
+ private static final Pattern patternSingleBS = Pattern.compile("\\\\{1}");
+ /**
+ *
+ * @param path
+ * @param startWithSlash
+ * @param endWithSlash
+ * @return
+ * @throws URISyntaxException if path is empty or has no parent directory available while resolving <code>../</code>
+ */
+ public static String slashify(final String path, final boolean startWithSlash, final boolean endWithSlash) throws URISyntaxException {
+ String p = patternSingleBS.matcher(path).replaceAll("/");
+ if (startWithSlash && !p.startsWith("/")) {
+ p = "/" + p;
+ }
+ if (endWithSlash && !p.endsWith("/")) {
+ p = p + "/";
+ }
+ return cleanPathString(p);
+ }
+ /**
+ * @param path assuming a slashified path, either denotes a file or directory, either relative or absolute.
+ * @return parent of path
+ * @throws URISyntaxException if path is empty or has no parent directory available
+ */
+ public static String getParentOf(final String path) throws URISyntaxException {
+ final int pl = null!=path ? path.length() : 0;
+ if(pl == 0) {
+ throw new IllegalArgumentException("path is empty <"+path+">");
+ }
+
+ final int e = path.lastIndexOf("/");
+ if( e < 0 ) {
+ throw new URISyntaxException(path, "path contains no '/': <"+path+">");
+ }
+ if( e == 0 ) {
+ // path is root directory
+ throw new URISyntaxException(path, "path has no parents: <"+path+">");
+ }
+ if( e < pl - 1 ) {
+ // path is file, return it's parent directory
+ return path.substring(0, e+1);
+ }
+ final int j = path.lastIndexOf("!") + 1; // '!' Separates JARFile entry -> local start of path
+ // path is a directory ..
+ final int p = path.lastIndexOf("/", e-1);
+ if( p >= j) {
+ // parent itself has '/' - post '!' or no '!' at all
+ return path.substring(0, p+1);
+ } else {
+ // parent itself has no '/'
+ final String parent = path.substring(j, e);
+ if( parent.equals("..") ) {
+ throw new URISyntaxException(path, "parent is unresolved: <"+path+">");
+ } else {
+ // parent is '!' or empty (relative path)
+ return path.substring(0, j);
+ }
+ }
+ }
+
+ /**
+ * @param path assuming a slashified path, either denoting a file or directory, either relative or absolute.
+ * @return clean path string where {@code ./} and {@code ../} is resolved,
+ * while keeping a starting {@code ../} at the beginning of a relative path.
+ * @throws URISyntaxException if path is empty or has no parent directory available while resolving <code>../</code>
+ */
+ public static String cleanPathString(String path) throws URISyntaxException {
+ // Resolve './' before '../' to handle case 'parent/./../a.txt' properly.
+ int idx = path.length() - 1;
+ while ( idx >= 1 && ( idx = path.lastIndexOf("./", idx) ) >= 0 ) {
+ if( 0 < idx && path.charAt(idx-1) == '.' ) {
+ idx-=2; // skip '../' -> idx upfront
+ } else {
+ path = path.substring(0, idx) + path.substring(idx+2);
+ idx--; // idx upfront
+ }
+ }
+ idx = 0;
+ while ( ( idx = path.indexOf("../", idx) ) >= 0 ) {
+ if( 0 == idx ) {
+ idx += 3; // skip starting '../'
+ } else {
+ path = getParentOf(path.substring(0, idx)) + path.substring(idx+3);
+ idx = 0;
+ }
+ }
+ return path;
+ }
+ }
} \ No newline at end of file
diff --git a/src/byte_stream.cpp b/src/byte_stream.cpp
index 81ca200..320e464 100644
--- a/src/byte_stream.cpp
+++ b/src/byte_stream.cpp
@@ -223,12 +223,12 @@ size_t ByteInStream_File::read(uint8_t out[], size_t length) NOEXCEPT_BOTAN {
if( 0 == length || end_of_data() ) {
return 0;
}
- m_source.read(cast_uint8_ptr_to_char(out), length);
+ m_source->read(cast_uint8_ptr_to_char(out), length);
if( error() ) {
DBG_PRINT("ByteInStream_File::read: Error occurred in %s", to_string().c_str());
return 0;
}
- const size_t got = static_cast<size_t>(m_source.gcount());
+ const size_t got = static_cast<size_t>(m_source->gcount());
m_bytes_consumed += got;
return got;
}
@@ -241,37 +241,37 @@ size_t ByteInStream_File::peek(uint8_t out[], size_t length, size_t offset) cons
if(offset) {
secure_vector<uint8_t> buf(offset);
- m_source.read(cast_uint8_ptr_to_char(buf.data()), buf.size());
+ m_source->read(cast_uint8_ptr_to_char(buf.data()), buf.size());
if( error() ) {
DBG_PRINT("ByteInStream_File::peek: Error occurred (offset) in %s", to_string().c_str());
return 0;
}
- got = static_cast<size_t>(m_source.gcount());
+ got = static_cast<size_t>(m_source->gcount());
}
if(got == offset) {
- m_source.read(cast_uint8_ptr_to_char(out), length);
+ m_source->read(cast_uint8_ptr_to_char(out), length);
if( error() ) {
DBG_PRINT("ByteInStream_File::peek: Error occurred (read) in %s", to_string().c_str());
return 0;
}
- got = static_cast<size_t>(m_source.gcount());
+ got = static_cast<size_t>(m_source->gcount());
}
- if(m_source.eof()) {
- m_source.clear();
+ if(m_source->eof()) {
+ m_source->clear();
}
- m_source.seekg(m_bytes_consumed, std::ios::beg);
+ m_source->seekg(m_bytes_consumed, std::ios::beg);
return got;
}
bool ByteInStream_File::check_available(size_t n) NOEXCEPT_BOTAN {
- return m_content_size - m_bytes_consumed >= (uint64_t)n;
+ return nullptr != m_source && m_content_size - m_bytes_consumed >= (uint64_t)n;
};
bool ByteInStream_File::end_of_data() const NOEXCEPT_BOTAN {
- return !m_source.good() || m_bytes_consumed >= m_content_size;
+ return nullptr == m_source || !m_source->good() || m_bytes_consumed >= m_content_size;
}
std::string ByteInStream_File::id() const NOEXCEPT_BOTAN {
@@ -280,25 +280,35 @@ std::string ByteInStream_File::id() const NOEXCEPT_BOTAN {
ByteInStream_File::ByteInStream_File(const std::string& path, bool use_binary) noexcept
: m_identifier(path),
- m_source(path, use_binary ? std::ios::binary : std::ios::in),
+ m_source(),
m_content_size(0), m_bytes_consumed(0)
{
- if( error() ) {
- DBG_PRINT("ByteInStream_File::ctor: Error occurred in %s", to_string().c_str());
+ std::unique_ptr<jau::fs::file_stats> stats;
+ if( jau::io::uri::is_local_file_protocol(path) ) {
+ // cut of leading `file://`
+ std::string path2 = path.substr(7);
+ stats = std::make_unique<jau::fs::file_stats>(path2);
+ } else {
+ stats = std::make_unique<jau::fs::file_stats>(path);
+ }
+ if( !stats->exists() || !stats->has_access() ) {
+ DBG_PRINT("ByteInStream_File::ctor: Error, not an existing or accessible file in %s, %s", stats->to_string(true).c_str(), to_string().c_str());
+ } else if( !stats->is_file() ) {
+ DBG_PRINT("ByteInStream_File::ctor: Error, not a file in %s, %s", stats->to_string(true).c_str(), to_string().c_str());
} else {
- const jau::fs::file_stats in_stats(path);
- if( !in_stats.exists() || !in_stats.has_access() ) {
- DBG_PRINT("ByteInStream_File::ctor: Error, not an existing or accessible file in %s", to_string().c_str());
- } else if( !in_stats.is_file() ) {
- DBG_PRINT("ByteInStream_File::ctor: Error, not a file in %s", to_string().c_str());
- } else {
- m_content_size = in_stats.size();
+ m_content_size = stats->size();
+ m_source = std::make_unique<std::ifstream>(stats->path(), use_binary ? std::ios::binary : std::ios::in);
+ if( error() ) {
+ DBG_PRINT("ByteInStream_File::ctor: Error occurred in %s, %s", stats->to_string(true), to_string().c_str());
+ m_source = nullptr;
}
}
}
void ByteInStream_File::close() noexcept {
- m_source.close();
+ if( nullptr != m_source ) {
+ m_source->close();
+ }
}
std::string ByteInStream_File::to_string() const noexcept {
@@ -310,6 +320,7 @@ std::string ByteInStream_File::to_string() const noexcept {
"]";
}
+
ByteInStream_URL::ByteInStream_URL(const std::string& url, const jau::fraction_i64& timeout) noexcept
: m_url(url), m_timeout(timeout), m_buffer(0x00, BEST_URLSTREAM_RINGBUFFER_SIZE),
m_has_content_length( false ), m_content_size( 0 ), m_total_xfered( 0 ), m_result( io::async_io_result_t::NONE ),
@@ -317,6 +328,10 @@ ByteInStream_URL::ByteInStream_URL(const std::string& url, const jau::fraction_i
{
m_url_thread = read_url_stream(m_url, m_buffer, m_has_content_length, m_content_size, m_total_xfered, m_result);
+ if( nullptr == m_url_thread ) {
+ // url protocol not supported
+ m_result = async_io_result_t::FAILED;
+ }
}
void ByteInStream_URL::close() noexcept {
@@ -327,10 +342,11 @@ void ByteInStream_URL::close() noexcept {
}
m_buffer.drop(m_buffer.size()); // unblock putBlocking(..)
- if( m_url_thread.joinable() ) {
+ if( nullptr != m_url_thread && m_url_thread->joinable() ) {
DBG_PRINT("ByteInStream_URL: close.1 %s, %s", id().c_str(), m_buffer.toString().c_str());
- m_url_thread.join();
+ m_url_thread->join();
}
+ m_url_thread = nullptr;
DBG_PRINT("ByteInStream_URL: close.X %s, %s", id().c_str(), to_string_int().c_str());
}
@@ -384,6 +400,22 @@ std::string ByteInStream_URL::to_string() const noexcept {
return "ByteInStream_URL["+to_string_int()+"]";
}
+std::unique_ptr<ByteInStream> jau::io::to_ByteInStream(const std::string& path_or_uri, jau::fraction_i64 timeout) noexcept {
+ if( !jau::io::uri::is_local_file_protocol(path_or_uri) &&
+ jau::io::uri::protocol_supported(path_or_uri) )
+ {
+ std::unique_ptr<ByteInStream> res = std::make_unique<ByteInStream_URL>(path_or_uri, timeout);
+ if( nullptr != res && !res->error() ) {
+ return res;
+ }
+ }
+ std::unique_ptr<ByteInStream> res = std::make_unique<ByteInStream_File>(path_or_uri);
+ if( nullptr != res && !res->error() ) {
+ return res;
+ }
+ return nullptr;
+}
+
ByteInStream_Feed::ByteInStream_Feed(const std::string& id_name, const jau::fraction_i64& timeout) noexcept
: m_id(id_name), m_timeout(timeout), m_buffer(0x00, BEST_URLSTREAM_RINGBUFFER_SIZE),
m_has_content_length( false ), m_content_size( 0 ), m_total_xfered( 0 ), m_result( io::async_io_result_t::NONE ),
diff --git a/src/io_util.cpp b/src/io_util.cpp
index 3a65d05..cdede16 100644
--- a/src/io_util.cpp
+++ b/src/io_util.cpp
@@ -82,6 +82,53 @@ uint64_t jau::io::read_stream(ByteInStream& in,
return total;
}
+std::vector<std::string_view> jau::io::uri::supported_protocols() noexcept {
+ const curl_version_info_data* cvid = curl_version_info(CURLVERSION_NOW);
+ if( nullptr == cvid || nullptr == cvid->protocols ) {
+ return std::vector<std::string_view>();
+ }
+ std::vector<std::string_view> res;
+ for(int i=0; nullptr != cvid->protocols[i]; ++i) {
+ res.push_back( std::string_view(cvid->protocols[i]) );
+ }
+ return res;
+}
+
+static bool _is_scheme_valid(const std::string_view& scheme) noexcept {
+ if ( scheme.empty() ) {
+ return false;
+ }
+ auto pos = std::find_if_not(scheme.begin(), scheme.end(), [&](char c){
+ return std::isalnum(c) || c == '+' || c == '.' || c == '-';
+ });
+ return pos == scheme.end();
+}
+std::string_view jau::io::uri::get_scheme(const std::string_view& uri) noexcept {
+ std::size_t pos = uri.find(':');
+ if (pos == std::string_view::npos) {
+ return uri.substr(0, 0);
+ }
+ std::string_view scheme = uri.substr(0, pos);
+ if( !_is_scheme_valid( scheme ) ) {
+ return uri.substr(0, 0);
+ }
+ return scheme;
+}
+
+bool jau::io::uri::protocol_supported(const std::string_view& uri) noexcept {
+ const std::string_view scheme = get_scheme(uri);
+ if( scheme.empty() ) {
+ return false;
+ }
+ const std::vector<std::string_view> protos = supported_protocols();
+ auto it = std::find(protos.cbegin(), protos.cend(), scheme);
+ return protos.cend() != it;
+}
+
+bool jau::io::uri::is_local_file_protocol(const std::string_view& uri) noexcept {
+ return 0 == uri.find("file://");
+}
+
struct curl_glue1_t {
CURL *curl_handle;
bool has_content_length;
@@ -178,6 +225,13 @@ uint64_t jau::io::read_url_stream(const std::string& url,
errorbuffer.reserve(CURL_ERROR_SIZE);
CURLcode res;
+ if( !uri::protocol_supported(url) ) {
+ const std::string_view scheme = uri::get_scheme(url);
+ DBG_PRINT("Protocol of given uri-scheme '%s' not supported. Supported protocols [%s].",
+ std::string(scheme).c_str(), to_string(uri::supported_protocols(), ",").c_str());
+ return 0;
+ }
+
/* init the curl session */
CURL *curl_handle = curl_easy_init();
if( nullptr == curl_handle ) {
@@ -519,16 +573,24 @@ cleanup:
return;
}
-std::thread jau::io::read_url_stream(const std::string& url,
- ByteRingbuffer& buffer,
- jau::relaxed_atomic_bool& has_content_length,
- jau::relaxed_atomic_uint64& content_length,
- jau::relaxed_atomic_uint64& total_read,
- relaxed_atomic_async_io_result_t& result) noexcept {
+std::unique_ptr<std::thread> jau::io::read_url_stream(const std::string& url,
+ ByteRingbuffer& buffer,
+ jau::relaxed_atomic_bool& has_content_length,
+ jau::relaxed_atomic_uint64& content_length,
+ jau::relaxed_atomic_uint64& total_read,
+ relaxed_atomic_async_io_result_t& result) noexcept {
/* init user referenced values */
has_content_length = false;
content_length = 0;
total_read = 0;
+
+ if( !uri::protocol_supported(url) ) {
+ result = io::async_io_result_t::FAILED;
+ const std::string_view scheme = uri::get_scheme(url);
+ DBG_PRINT("Protocol of given uri-scheme '%s' not supported. Supported protocols [%s].",
+ std::string(scheme).c_str(), to_string(uri::supported_protocols(), ",").c_str());
+ return nullptr;
+ }
result = io::async_io_result_t::NONE;
if( buffer.capacity() < BEST_URLSTREAM_RINGBUFFER_SIZE ) {
@@ -537,7 +599,7 @@ std::thread jau::io::read_url_stream(const std::string& url,
std::unique_ptr<curl_glue2_t> cg ( std::make_unique<curl_glue2_t>(nullptr, has_content_length, content_length, total_read, buffer, result ) );
- return std::thread(&::read_url_stream_thread, url.c_str(), std::move(cg)); // @suppress("Invalid arguments")
+ return std::make_unique<std::thread>(&::read_url_stream_thread, url.c_str(), std::move(cg)); // @suppress("Invalid arguments")
}
void jau::io::print_stats(const std::string& prefix, const uint64_t& out_bytes_total, const jau::fraction_i64& td) noexcept {
diff --git a/test/test_bytestream01.cpp b/test/test_bytestream01.cpp
index 46435e8..63cd1fb 100644
--- a/test/test_bytestream01.cpp
+++ b/test/test_bytestream01.cpp
@@ -80,8 +80,8 @@ class TestByteStream01 {
fname_payload_size_lst.push_back( size );
}
data() {
- add_test_file("test_cipher_01_11kiB", 1024*11);
- add_test_file("test_cipher_02_65MiB", 1024*1024*65);
+ add_test_file("testfile_blob_01_11kiB.bin", 1024*11);
+ add_test_file("testfile_blob_02_65MiB.bin", 1024*1024*65);
}
public:
static const data& get() {
@@ -99,13 +99,14 @@ class TestByteStream01 {
~TestByteStream01() {
std::system("killall mini_httpd");
- std::system("killall mini_httpd");
}
static void httpd_start() {
std::system("killall mini_httpd");
- std::system("killall mini_httpd");
- std::system("/usr/sbin/mini_httpd -p 8080");
+ const std::string cwd = jau::fs::get_cwd();
+ const std::string cmd = "/usr/sbin/mini_httpd -p 8080 -l "+cwd+"/mini_httpd.log";
+ jau::PLAIN_PRINT(true, "%s", cmd.c_str());
+ std::system(cmd.c_str());
}
static bool transfer(jau::io::ByteInStream& input, const std::string& output_fname) {
@@ -160,6 +161,130 @@ class TestByteStream01 {
return true;
}
+ void test00a_protocols_error() {
+ httpd_start();
+ {
+ std::vector<std::string_view> protos = jau::io::uri::supported_protocols();
+ jau::PLAIN_PRINT(true, "test00_protocols: Supported protocols: %zu: %s", protos.size(), jau::to_string(protos, ",").c_str());
+ REQUIRE( 0 < protos.size() );
+ }
+ const size_t file_idx = IDX_11kiB;
+ {
+ const std::string url = "not_exiting_file.txt";
+ REQUIRE( false == jau::io::uri::is_local_file_protocol(url) );
+ REQUIRE( false == jau::io::uri::protocol_supported(url) );
+
+ std::unique_ptr<jau::io::ByteInStream> in = jau::io::to_ByteInStream(url);
+ if( nullptr != in ) {
+ jau::PLAIN_PRINT(true, "test00_protocols: not_exiting_file: %s", in->to_string().c_str());
+ }
+ REQUIRE( nullptr == in );
+ }
+ {
+ const std::string url = "file://not_exiting_file_uri.txt";
+ REQUIRE( true == jau::io::uri::is_local_file_protocol(url) );
+ REQUIRE( true == jau::io::uri::protocol_supported(url) );
+
+ std::unique_ptr<jau::io::ByteInStream> in = jau::io::to_ByteInStream(url);
+ if( nullptr != in ) {
+ jau::PLAIN_PRINT(true, "test00_protocols: not_exiting_file_uri: %s", in->to_string().c_str());
+ }
+ REQUIRE( nullptr == in );
+ }
+ {
+ const std::string url = "lala://localhost:8080/" + fname_payload_lst[file_idx];
+ REQUIRE( false == jau::io::uri::is_local_file_protocol(url) );
+ REQUIRE( false == jau::io::uri::protocol_supported(url) );
+
+ std::unique_ptr<jau::io::ByteInStream> in = jau::io::to_ByteInStream(url);
+ if( nullptr != in ) {
+ jau::PLAIN_PRINT(true, "test00_protocols: not_exiting_protocol_uri: %s", in->to_string().c_str());
+ }
+ REQUIRE( nullptr == in );
+ }
+ {
+ const std::string url = url_input_root + "not_exiting_http_uri.txt";
+ REQUIRE( false == jau::io::uri::is_local_file_protocol(url) );
+ REQUIRE( true == jau::io::uri::protocol_supported(url) );
+
+ std::unique_ptr<jau::io::ByteInStream> in = jau::io::to_ByteInStream(url);
+ REQUIRE( nullptr != in );
+ jau::sleep_for( 10_ms );
+ jau::PLAIN_PRINT(true, "test00_protocols: not_exiting_http_uri: %s", in->to_string().c_str());
+ REQUIRE( true == in->end_of_data() );
+ REQUIRE( true == in->error() );
+ REQUIRE( 0 == in->content_size() );
+ }
+ }
+
+ void test00b_protocols_ok() {
+ httpd_start();
+ const size_t file_idx = IDX_11kiB;
+ {
+ const std::string url = fname_payload_lst[file_idx];
+ REQUIRE( false == jau::io::uri::is_local_file_protocol(url) );
+ REQUIRE( false == jau::io::uri::protocol_supported(url) );
+
+ std::unique_ptr<jau::io::ByteInStream> in = jau::io::to_ByteInStream(url);
+ if( nullptr != in ) {
+ jau::PLAIN_PRINT(true, "test00_protocols: local-file-0: %s", in->to_string().c_str());
+ }
+ REQUIRE( nullptr != in );
+ REQUIRE( false == in->error() );
+
+ bool res = transfer(*in, fname_payload_copy_lst[file_idx]);
+ REQUIRE( true == res );
+
+ jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
+ REQUIRE( true == out_stats.exists() );
+ REQUIRE( true == out_stats.is_file() );
+ REQUIRE( in->content_size() == out_stats.size() );
+ REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
+ }
+ {
+ const std::string url = "file://" + fname_payload_lst[file_idx];
+ REQUIRE( true == jau::io::uri::is_local_file_protocol(url) );
+ REQUIRE( true == jau::io::uri::protocol_supported(url) );
+
+ std::unique_ptr<jau::io::ByteInStream> in = jau::io::to_ByteInStream(url);
+ if( nullptr != in ) {
+ jau::PLAIN_PRINT(true, "test00_protocols: local-file-1: %s", in->to_string().c_str());
+ }
+ REQUIRE( nullptr != in );
+ REQUIRE( false == in->error() );
+
+ bool res = transfer(*in, fname_payload_copy_lst[file_idx]);
+ REQUIRE( true == res );
+
+ jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
+ REQUIRE( true == out_stats.exists() );
+ REQUIRE( true == out_stats.is_file() );
+ REQUIRE( in->content_size() == out_stats.size() );
+ REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
+ }
+ {
+ const std::string url = url_input_root + fname_payload_lst[file_idx];
+ REQUIRE( false == jau::io::uri::is_local_file_protocol(url) );
+ REQUIRE( true == jau::io::uri::protocol_supported(url) );
+
+ std::unique_ptr<jau::io::ByteInStream> in = jau::io::to_ByteInStream(url);
+ if( nullptr != in ) {
+ jau::PLAIN_PRINT(true, "test00_protocols: http: %s", in->to_string().c_str());
+ }
+ REQUIRE( nullptr != in );
+ REQUIRE( false == in->error() );
+
+ bool res = transfer(*in, fname_payload_copy_lst[file_idx]);
+ REQUIRE( true == res );
+
+ jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
+ REQUIRE( true == out_stats.exists() );
+ REQUIRE( true == out_stats.is_file() );
+ REQUIRE( in->content_size() == out_stats.size() );
+ REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
+ }
+ }
+
void test01_copy_file_ok() {
{
const size_t file_idx = IDX_11kiB;
@@ -475,12 +600,13 @@ std::vector<std::string> TestByteStream01::fname_payload_lst;
std::vector<std::string> TestByteStream01::fname_payload_copy_lst;
std::vector<uint64_t> TestByteStream01::fname_payload_size_lst;
-METHOD_AS_TEST_CASE( TestByteStream01::test01_copy_file_ok, "TestByteStream01 test01_copy_file_ok");
-
-METHOD_AS_TEST_CASE( TestByteStream01::test11_copy_http_ok, "TestByteStream01 test11_copy_http_ok");
-METHOD_AS_TEST_CASE( TestByteStream01::test12_copy_http_404, "TestByteStream01 test12_copy_http_404");
+METHOD_AS_TEST_CASE( TestByteStream01::test00a_protocols_error, "TestByteStream01 test00a_protocols_error");
+METHOD_AS_TEST_CASE( TestByteStream01::test00b_protocols_ok, "TestByteStream01 test00b_protocols_ok");
-METHOD_AS_TEST_CASE( TestByteStream01::test21_copy_fed_ok, "TestByteStream01 test21_copy_fed_ok");
-METHOD_AS_TEST_CASE( TestByteStream01::test22_copy_fed_irq, "TestByteStream01 test22_copy_fed_irq");
+METHOD_AS_TEST_CASE( TestByteStream01::test01_copy_file_ok, "TestByteStream01 test01_copy_file_ok");
+METHOD_AS_TEST_CASE( TestByteStream01::test11_copy_http_ok, "TestByteStream01 test11_copy_http_ok");
+METHOD_AS_TEST_CASE( TestByteStream01::test12_copy_http_404, "TestByteStream01 test12_copy_http_404");
+METHOD_AS_TEST_CASE( TestByteStream01::test21_copy_fed_ok, "TestByteStream01 test21_copy_fed_ok");
+METHOD_AS_TEST_CASE( TestByteStream01::test22_copy_fed_irq, "TestByteStream01 test22_copy_fed_irq");
diff --git a/test/test_iostream01.cpp b/test/test_iostream01.cpp
index d82f7d2..d1809ff 100644
--- a/test/test_iostream01.cpp
+++ b/test/test_iostream01.cpp
@@ -53,7 +53,7 @@ using namespace jau::fractions_i64_literals;
class TestIOStream01 {
public:
const std::string url_input_root = "http://localhost:8080/";
- const std::string basename_10kiB = "data-10kiB.bin";
+ const std::string basename_10kiB = "testfile_data_10kiB.bin";
TestIOStream01() {
// produce fresh demo data
@@ -71,13 +71,76 @@ class TestIOStream01 {
}
}
std::system("killall mini_httpd");
- std::system("killall mini_httpd");
- std::system("/usr/sbin/mini_httpd -p 8080");
+ const std::string cwd = jau::fs::get_cwd();
+ const std::string cmd = "/usr/sbin/mini_httpd -p 8080 -l "+cwd+"/mini_httpd.log";
+ jau::PLAIN_PRINT(true, "%s", cmd.c_str());
+ std::system(cmd.c_str());
}
~TestIOStream01() {
std::system("killall mini_httpd");
- std::system("killall mini_httpd");
+ }
+
+ void test00_protocols() {
+ {
+ std::vector<std::string_view> protos = jau::io::uri::supported_protocols();
+ jau::PLAIN_PRINT(true, "test00_protocols: Supported protocols: %zu: %s", protos.size(), jau::to_string(protos, ",").c_str());
+ REQUIRE( 0 < protos.size() );
+ }
+ {
+ const std::string url = url_input_root + basename_10kiB;
+ REQUIRE( false == jau::io::uri::is_local_file_protocol(url) );
+ REQUIRE( true == jau::io::uri::protocol_supported(url) );
+ }
+ {
+ const std::string url = "https://localhost:8080/" + basename_10kiB;
+ REQUIRE( false == jau::io::uri::is_local_file_protocol(url) );
+ REQUIRE( true == jau::io::uri::protocol_supported(url) );
+ }
+ {
+ const std::string url = "file://" + basename_10kiB;
+ REQUIRE( true == jau::io::uri::is_local_file_protocol(url) );
+ REQUIRE( true == jau::io::uri::protocol_supported(url) );
+ }
+ {
+ const std::string url = "lala://localhost:8080/" + basename_10kiB;
+ REQUIRE( false == jau::io::uri::is_local_file_protocol(url) );
+ REQUIRE( false == jau::io::uri::protocol_supported(url) );
+ }
+ {
+ // sync read_url_stream w/ unknown protocol
+ const std::string url = "lala://localhost:8080/" + basename_10kiB;
+ jau::io::secure_vector<uint8_t> buffer(4096);
+ size_t consumed_calls = 0;
+ uint64_t consumed_total_bytes = 0;
+ jau::io::StreamConsumerFunc consume = [&](jau::io::secure_vector<uint8_t>& data, bool is_final) noexcept -> bool {
+ (void)is_final;
+ consumed_calls++;
+ consumed_total_bytes += data.size();
+ return true;
+ };
+ uint64_t http_total_bytes = jau::io::read_url_stream(url, buffer, consume);
+ REQUIRE( 0 == http_total_bytes );
+ REQUIRE( consumed_total_bytes == http_total_bytes );
+ REQUIRE( 0 == consumed_calls );
+ }
+ {
+ // async read_url_stream w/ unknown protocol
+ const std::string url = "lala://localhost:8080/" + basename_10kiB;
+
+ jau::io::ByteRingbuffer rb(0x00, jau::io::BEST_URLSTREAM_RINGBUFFER_SIZE);
+ jau::relaxed_atomic_bool url_has_content_length;
+ jau::relaxed_atomic_uint64 url_content_length;
+ jau::relaxed_atomic_uint64 url_total_read;
+ jau::io::relaxed_atomic_async_io_result_t result;
+
+ std::unique_ptr<std::thread> http_thread = jau::io::read_url_stream(url, rb, url_has_content_length, url_content_length, url_total_read, result);
+ REQUIRE( nullptr == http_thread );
+ REQUIRE( url_has_content_length == false );
+ REQUIRE( url_content_length == 0 );
+ REQUIRE( url_content_length == url_total_read );
+ REQUIRE( jau::io::async_io_result_t::FAILED == result );
+ }
}
void test01_sync_ok() {
@@ -85,7 +148,7 @@ class TestIOStream01 {
const size_t file_size = in_stats.size();
const std::string url_input = url_input_root + basename_10kiB;
- std::ofstream outfile("test01_01_out.bin", std::ios::out | std::ios::binary);
+ std::ofstream outfile("testfile01_01_out.bin", std::ios::out | std::ios::binary);
REQUIRE( outfile.good() );
REQUIRE( outfile.is_open() );
@@ -112,7 +175,7 @@ class TestIOStream01 {
void test02_sync_404() {
const std::string url_input = url_input_root + "doesnt_exists.txt";
- std::ofstream outfile("test02_01_out.bin", std::ios::out | std::ios::binary);
+ std::ofstream outfile("testfile02_01_out.bin", std::ios::out | std::ios::binary);
REQUIRE( outfile.good() );
REQUIRE( outfile.is_open() );
@@ -141,7 +204,7 @@ class TestIOStream01 {
const size_t file_size = in_stats.size();
const std::string url_input = url_input_root + basename_10kiB;
- std::ofstream outfile("test11_01_out.bin", std::ios::out | std::ios::binary);
+ std::ofstream outfile("testfile11_01_out.bin", std::ios::out | std::ios::binary);
REQUIRE( outfile.good() );
REQUIRE( outfile.is_open() );
@@ -152,7 +215,8 @@ class TestIOStream01 {
jau::relaxed_atomic_uint64 url_total_read;
jau::io::relaxed_atomic_async_io_result_t result;
- std::thread http_thread = jau::io::read_url_stream(url_input, rb, url_has_content_length, url_content_length, url_total_read, result);
+ std::unique_ptr<std::thread> http_thread = jau::io::read_url_stream(url_input, rb, url_has_content_length, url_content_length, url_total_read, result);
+ REQUIRE( nullptr != http_thread );
jau::io::secure_vector<uint8_t> buffer(buffer_size);
size_t consumed_loops = 0;
@@ -171,7 +235,7 @@ class TestIOStream01 {
jau::PLAIN_PRINT(true, "test11_async_ok.X Done: total %" PRIu64 ", result %d, rb %s",
consumed_total_bytes, (int)result.load(), rb.toString().c_str() );
- http_thread.join();
+ http_thread->join();
REQUIRE( url_has_content_length == true );
REQUIRE( url_content_length == file_size );
@@ -184,7 +248,7 @@ class TestIOStream01 {
void test12_async_404() {
const std::string url_input = url_input_root + "doesnt_exists.txt";
- std::ofstream outfile("test12_01_out.bin", std::ios::out | std::ios::binary);
+ std::ofstream outfile("testfile12_01_out.bin", std::ios::out | std::ios::binary);
REQUIRE( outfile.good() );
REQUIRE( outfile.is_open() );
@@ -195,7 +259,8 @@ class TestIOStream01 {
jau::relaxed_atomic_uint64 url_total_read;
jau::io::relaxed_atomic_async_io_result_t result;
- std::thread http_thread = jau::io::read_url_stream(url_input, rb, url_has_content_length, url_content_length, url_total_read, result);
+ std::unique_ptr<std::thread> http_thread = jau::io::read_url_stream(url_input, rb, url_has_content_length, url_content_length, url_total_read, result);
+ REQUIRE( nullptr != http_thread );
jau::io::secure_vector<uint8_t> buffer(buffer_size);
size_t consumed_loops = 0;
@@ -214,7 +279,7 @@ class TestIOStream01 {
jau::PLAIN_PRINT(true, "test12_async_404.X Done: total %" PRIu64 ", result %d, rb %s",
consumed_total_bytes, (int)result.load(), rb.toString().c_str() );
- http_thread.join();
+ http_thread->join();
REQUIRE( url_has_content_length == false );
REQUIRE( url_content_length == 0 );
@@ -226,6 +291,7 @@ class TestIOStream01 {
};
+METHOD_AS_TEST_CASE( TestIOStream01::test00_protocols, "TestIOStream01 - test00_protocols");
METHOD_AS_TEST_CASE( TestIOStream01::test01_sync_ok, "TestIOStream01 - test01_sync_ok");
METHOD_AS_TEST_CASE( TestIOStream01::test02_sync_404, "TestIOStream01 - test02_sync_404");
METHOD_AS_TEST_CASE( TestIOStream01::test11_async_ok, "TestIOStream01 - test11_async_ok");