jaulib v1.3.6
Jau Support Library (C++, Java, ..)
Loading...
Searching...
No Matches
test_bytestream01.cpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2022 Gothel Software e.K.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25#include <cassert>
26#include <cinttypes>
27#include <cstdint>
28#include <cstring>
29#include <vector>
30
31#include <jau/test/catch2_ext.hpp>
32
33#include <jau/debug.hpp>
34#include <jau/file_util.hpp>
35#include <jau/byte_stream.hpp>
36#include <jau/io_util.hpp>
37
38#include "test_httpd.hpp"
39
40extern "C" {
41 #include <unistd.h>
42}
43
44using namespace jau::fractions_i64_literals;
45
47 private:
48 const size_t IDX_11kiB = 0;
49 const size_t IDX_65MiB = 1;
50 static std::vector<std::string> fname_payload_lst;
51 static std::vector<std::string> fname_payload_copy_lst;
52 static std::vector<uint64_t> fname_payload_size_lst;
53 const std::string url_input_root = "http://localhost:8080/";
54
55 class data {
56 private:
57 static bool add_test_file(const std::string& name, const size_t size_limit) {
59 jau::fs::remove(name+".enc");
60 jau::fs::remove(name+".enc.dec");
61 jau::fs::remove(name+".copy");
62 size_t size;
63 {
64 static const std::string one_line = "Hello World, this is a test and I like it. Exactly 100 characters long. 0123456780 abcdefghjklmnop..";
66
67 REQUIRE( ofs.good() == true );
68 REQUIRE( ofs.is_open() == true );
69
70 for(size=0; size < size_limit; size+=one_line.size()) {
71 if( one_line.size() != ofs.write(one_line.data(), one_line.size()) ) {
72 ERR_PRINT("Write %zu bytes to test file failed: %s", one_line.size(), ofs.to_string().c_str());
73 return false;
74 }
75 }
76 if( 1 != ofs.write("X", 1) ) { // make it odd
77 ERR_PRINT("Write %zu bytes to test file failed: %s", 1, ofs.to_string().c_str());
78 return false;
79 }
80 size += 1;
81 }
82 fname_payload_lst.push_back(name);
83 fname_payload_copy_lst.push_back(name+".copy");
84 fname_payload_size_lst.push_back( size );
85 return true;
86 }
87 data() {
88 REQUIRE( true == add_test_file("testfile_blob_01_11kiB.bin", (size_t)(1024*11)) );
89 REQUIRE( true == add_test_file("testfile_blob_02_65MiB.bin", (size_t)(1024*1024*65)) );
90 }
91 public:
92 static const data& get() {
93 static data instance;
94 return instance;
95 }
96 };
97
98 public:
100 // produce fresh demo data once per whole test class
101 const data& d = data::get();
102 (void)d;
103 }
104
107 int res = std::system("killall mini_httpd");
108 (void)res;
109 }
110 }
111
112 static void httpd_start() {
114 int res = std::system("killall mini_httpd");
115 (void)res;
116 const std::string cwd = jau::fs::get_cwd();
117 const std::string cmd = std::string(mini_httpd_exe)+" -p 8080 -l "+cwd+"/mini_httpd.log";
118 jau::PLAIN_PRINT(true, "%s", cmd.c_str());
119 res = std::system(cmd.c_str());
120 (void)res;
121 }
122 }
123
124 static bool transfer(jau::io::ByteInStream& input, const std::string& output_fname, const size_t buffer_size=4096) {
126 jau::PLAIN_PRINT(true, "Transfer Start: %s", input.to_string().c_str());
127 {
128 const jau::fs::file_stats output_stats(output_fname);
129 if( output_stats.exists() ) {
130 if( output_stats.is_file() ) {
131 if( !jau::fs::remove(output_fname) ) {
132 ERR_PRINT2("ByteStream copy failed: Failed deletion of existing output file %s", output_fname.c_str());
133 return false;
134 }
135 } else {
136 ERR_PRINT2("ByteStream copy failed: Not overwriting existing output file %s", output_fname.c_str());
137 return false;
138 }
139 }
140 }
141 jau::io::ByteOutStream_File outfile(output_fname);
142 if ( !outfile.good() || !outfile.is_open() ) {
143 ERR_PRINT2("ByteStream copy failed: Couldn't open output file %s", output_fname.c_str());
144 return false;
145 }
146
147 uint64_t out_bytes_payload = 0;
148 jau::io::StreamConsumerFunc consume_data = [&](jau::io::secure_vector<uint8_t>& data, bool is_final) -> bool {
149 if( !is_final && ( !input.has_content_size() || out_bytes_payload + data.size() < input.content_size() ) ) {
150 const size_t written = outfile.write(data.data(), data.size());
151 out_bytes_payload += written;
152 return data.size() == written; // continue ..
153 } else {
154 const size_t written = outfile.write(data.data(), data.size());
155 out_bytes_payload += written;
156 return false; // EOS
157 }
158 };
160 io_buffer.reserve(buffer_size);
161 const uint64_t in_bytes_total = jau::io::read_stream(input, io_buffer, consume_data);
162 input.close();
163
164 if ( 0==in_bytes_total || input.fail() ) {
165 IRQ_PRINT("ByteStream copy failed: Input file read failed in %s, out %s", input.to_string().c_str(), outfile.to_string().c_str());
166 return false;
167 }
168 if ( outfile.fail() ) {
169 IRQ_PRINT("ByteStream copy failed: Output file write failed in %s, out %s", input.to_string().c_str(), outfile.to_string().c_str());
170 return false;
171 }
172
174 jau::io::print_stats("Transfer "+output_fname, out_bytes_payload, _td);
175 jau::PLAIN_PRINT(true, "Transfer End: %s", input.to_string().c_str());
176
177 return true;
178 }
179
181 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
182 const bool http_support_expected = jau::io::uri_tk::protocol_supported("http:");
183 const bool file_support_expected = jau::io::uri_tk::protocol_supported("file:");
184 httpd_start();
185 {
186 std::vector<std::string_view> protos = jau::io::uri_tk::supported_protocols();
187 jau::PLAIN_PRINT(true, "test00_protocols: Supported protocols: %zu: %s", protos.size(), jau::to_string(protos, ",").c_str());
188 if( http_support_expected ) { // assume no http -> no curl
189 REQUIRE( 0 < protos.size() );
190 } else {
191 REQUIRE( 0 == protos.size() );
192 }
193 }
194 const size_t file_idx = IDX_11kiB;
195 {
196 const std::string url = "not_exiting_file.txt";
197 REQUIRE( false == jau::io::uri_tk::is_local_file_protocol(url) );
198 REQUIRE( false == jau::io::uri_tk::protocol_supported(url) );
199
200 std::unique_ptr<jau::io::ByteInStream> in = jau::io::to_ByteInStream(url);
201 if( nullptr != in ) {
202 jau::PLAIN_PRINT(true, "test00_protocols: not_exiting_file: %s", in->to_string().c_str());
203 }
204 REQUIRE( nullptr == in );
205 }
206 {
207 const std::string url = "file://not_exiting_file_uri.txt";
208 REQUIRE( true == jau::io::uri_tk::is_local_file_protocol(url) );
209 REQUIRE( file_support_expected == jau::io::uri_tk::protocol_supported(url) );
210
211 std::unique_ptr<jau::io::ByteInStream> in = jau::io::to_ByteInStream(url);
212 if( nullptr != in ) {
213 jau::PLAIN_PRINT(true, "test00_protocols: not_exiting_file_uri: %s", in->to_string().c_str());
214 }
215 REQUIRE( nullptr == in );
216 }
217 {
218 const std::string url = "lala://localhost:8080/" + fname_payload_lst[file_idx];
219 REQUIRE( false == jau::io::uri_tk::is_local_file_protocol(url) );
220 REQUIRE( false == jau::io::uri_tk::protocol_supported(url) );
221
222 std::unique_ptr<jau::io::ByteInStream> in = jau::io::to_ByteInStream(url);
223 if( nullptr != in ) {
224 jau::PLAIN_PRINT(true, "test00_protocols: not_exiting_protocol_uri: %s", in->to_string().c_str());
225 }
226 REQUIRE( nullptr == in );
227 }
228 {
229 const std::string url = url_input_root + "not_exiting_http_uri.txt";
230 REQUIRE( false == jau::io::uri_tk::is_local_file_protocol(url) );
231 REQUIRE( http_support_expected == jau::io::uri_tk::protocol_supported(url) );
232
233 std::unique_ptr<jau::io::ByteInStream> in = jau::io::to_ByteInStream(url);
234 if( http_support_expected ) {
235 REQUIRE( nullptr != in );
236 jau::sleep_for( 100_ms ); // time to read 404 response
237 jau::PLAIN_PRINT(true, "test00_protocols: not_exiting_http_uri: %s", in->to_string().c_str());
238 REQUIRE( false == in->good() );
239 REQUIRE( true == in->fail() );
240 REQUIRE( 0 == in->content_size() );
241 } else {
242 REQUIRE( nullptr == in );
243 }
244 }
245 }
246
248 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
249 const bool http_support_expected = jau::io::uri_tk::protocol_supported("http:");
250 const bool file_support_expected = jau::io::uri_tk::protocol_supported("file:");
251 httpd_start();
252 const size_t file_idx = IDX_11kiB;
253 {
254 const std::string url = fname_payload_lst[file_idx];
255 REQUIRE( false == jau::io::uri_tk::is_local_file_protocol(url) );
256 REQUIRE( false == jau::io::uri_tk::protocol_supported(url) );
257
258 std::unique_ptr<jau::io::ByteInStream> in = jau::io::to_ByteInStream(url);
259 if( nullptr != in ) {
260 jau::PLAIN_PRINT(true, "test00_protocols: local-file-0: %s", in->to_string().c_str());
261 }
262 REQUIRE( nullptr != in );
263 REQUIRE( false == in->fail() );
264
265 bool res = transfer(*in, fname_payload_copy_lst[file_idx]);
266 REQUIRE( true == res );
267
268 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
269 REQUIRE( true == out_stats.exists() );
270 REQUIRE( true == out_stats.is_file() );
271 REQUIRE( in->content_size() == out_stats.size() );
272 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
273 REQUIRE( true == jau::fs::compare(in->id(), out_stats.path(), true /* verbose */) );
274 }
275 {
276 const std::string url = "file://" + fname_payload_lst[file_idx];
277 REQUIRE( true == jau::io::uri_tk::is_local_file_protocol(url) );
278 REQUIRE( file_support_expected == jau::io::uri_tk::protocol_supported(url) );
279
280 std::unique_ptr<jau::io::ByteInStream> in = jau::io::to_ByteInStream(url);
281 if( nullptr != in ) {
282 jau::PLAIN_PRINT(true, "test00_protocols: local-file-1: %s", in->to_string().c_str());
283 } else {
284 jau::PLAIN_PRINT(true, "test00_protocols: local-file-1: NULL from url '%s'", url.c_str());
285 }
286 REQUIRE( nullptr != in );
287 REQUIRE( false == in->fail() );
288
289 bool res = transfer(*in, fname_payload_copy_lst[file_idx]);
290 REQUIRE( true == res );
291
292 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
293 REQUIRE( true == out_stats.exists() );
294 REQUIRE( true == out_stats.is_file() );
295 REQUIRE( in->content_size() == out_stats.size() );
296 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
297 REQUIRE( true == jau::fs::compare(fname_payload_lst[file_idx], out_stats.path(), true /* verbose */) );
298 }
299 {
300 const std::string url = url_input_root + fname_payload_lst[file_idx];
301 REQUIRE( false == jau::io::uri_tk::is_local_file_protocol(url) );
302 REQUIRE( http_support_expected == jau::io::uri_tk::protocol_supported(url) );
303
304 std::unique_ptr<jau::io::ByteInStream> in = jau::io::to_ByteInStream(url);
305 if( nullptr != in ) {
306 jau::PLAIN_PRINT(true, "test00_protocols: http: %s", in->to_string().c_str());
307 }
308 if( http_support_expected ) {
309 REQUIRE( nullptr != in );
310 REQUIRE( false == in->fail() );
311
312 bool res = transfer(*in, fname_payload_copy_lst[file_idx]);
313 REQUIRE( true == res );
314
315 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
316 REQUIRE( true == out_stats.exists() );
317 REQUIRE( true == out_stats.is_file() );
318 REQUIRE( in->content_size() == out_stats.size() );
319 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
320 REQUIRE( true == jau::fs::compare(fname_payload_lst[file_idx], out_stats.path(), true /* verbose */) );
321 } else {
322 REQUIRE( nullptr == in );
323 }
324 }
325 }
326
328 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
329 const size_t file_idx = IDX_11kiB;
330 jau::io::ByteInStream_File data_stream(fname_payload_lst[file_idx]);
331
332 bool res = transfer(data_stream, fname_payload_copy_lst[file_idx], 4096);
333 REQUIRE( true == res );
334
335 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
336 REQUIRE( true == out_stats.exists() );
337 REQUIRE( true == out_stats.is_file() );
338 REQUIRE( data_stream.content_size() == out_stats.size() );
339 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
340 REQUIRE( true == jau::fs::compare(data_stream.id(), out_stats.path(), true /* verbose */) );
341 }
342
344 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
345 const size_t file_idx = IDX_65MiB;
346 jau::io::ByteInStream_File data_stream(fname_payload_lst[file_idx]);
347
348 bool res = transfer(data_stream, fname_payload_copy_lst[file_idx], 4096);
349 REQUIRE( true == res );
350
351 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
352 REQUIRE( true == out_stats.exists() );
353 REQUIRE( true == out_stats.is_file() );
354 REQUIRE( data_stream.content_size() == out_stats.size() );
355 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
356 REQUIRE( true == jau::fs::compare(data_stream.id(), out_stats.path(), true /* verbose */) );
357 }
358
360 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
361 const size_t file_idx = IDX_65MiB;
362 jau::io::ByteInStream_File data_stream(fname_payload_lst[file_idx]);
363
364 bool res = transfer(data_stream, fname_payload_copy_lst[file_idx], 32768);
365 REQUIRE( true == res );
366
367 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
368 REQUIRE( true == out_stats.exists() );
369 REQUIRE( true == out_stats.is_file() );
370 REQUIRE( data_stream.content_size() == out_stats.size() );
371 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
372 REQUIRE( true == jau::fs::compare(data_stream.id(), out_stats.path(), true /* verbose */) );
373 }
374
376 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
378 jau::PLAIN_PRINT(true, "http not supported, abort\n");
379 return;
380 }
381 httpd_start();
382 {
383 const size_t file_idx = IDX_11kiB;
384
385 const std::string uri_original = url_input_root + fname_payload_lst[file_idx];
386
387 jau::io::ByteInStream_URL data_stream(uri_original, 500_ms);
388
389 bool res = transfer(data_stream, fname_payload_copy_lst[file_idx], 32768);
390 REQUIRE( true == res );
391
392 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
393 REQUIRE( true == out_stats.exists() );
394 REQUIRE( true == out_stats.is_file() );
395 REQUIRE( data_stream.content_size() == out_stats.size() );
396 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
397 REQUIRE( true == jau::fs::compare(fname_payload_lst[file_idx], out_stats.path(), true /* verbose */) );
398 }
399 {
400 const size_t file_idx = IDX_65MiB;
401
402 const std::string uri_original = url_input_root + fname_payload_lst[file_idx];
403
404 jau::io::ByteInStream_URL data_stream(uri_original, 500_ms);
405
406 bool res = transfer(data_stream, fname_payload_copy_lst[file_idx], 32768);
407 REQUIRE( true == res );
408
409 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
410 REQUIRE( true == out_stats.exists() );
411 REQUIRE( true == out_stats.is_file() );
412 REQUIRE( data_stream.content_size() == out_stats.size() );
413 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
414 REQUIRE( true == jau::fs::compare(fname_payload_lst[file_idx], out_stats.path(), true /* verbose */) );
415 }
416 }
417
419 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
421 jau::PLAIN_PRINT(true, "http not supported, abort\n");
422 return;
423 }
424 httpd_start();
425 {
426 const size_t file_idx = IDX_11kiB;
427
428 const std::string uri_original = url_input_root + "doesnt_exists.txt";
429
430 jau::io::ByteInStream_URL data_stream(uri_original, 500_ms);
431
432 bool res = transfer(data_stream, fname_payload_copy_lst[file_idx]);
433 REQUIRE( false == res );
434
435 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
436 REQUIRE( true == out_stats.exists() );
437 REQUIRE( true == out_stats.is_file() );
438 REQUIRE( data_stream.fail() == true );
439 REQUIRE( data_stream.has_content_size() == false );
440 REQUIRE( data_stream.content_size() == 0 );
441 REQUIRE( 0 == out_stats.size() );
442 }
443 }
444
445 // throttled, no content size, interruptReader() via set_eof() will avoid timeout
446 static void feed_source_00(jau::io::ByteInStream_Feed * data_feed, const size_t feed_size=1024) {
447 uint64_t xfer_total = 0;
448 jau::io::ByteInStream_File data_stream(data_feed->id());
449 std::vector<uint8_t> buffer;
450 buffer.resize(feed_size);
451 while( data_stream.good() ) {
452 size_t count = data_stream.read(buffer.data(), buffer.size());
453 if( 0 < count ) {
454 xfer_total += count;
455 if( data_feed->write(buffer.data(), count) ) {
456 jau::sleep_for( 16_ms );
457 } else {
458 break;
459 }
460 }
461 }
462 // probably set after transfering due to above sleep, which also ends when total size has been reached.
464 (void)xfer_total; // not used yet ..
465 }
466
467 // throttled, with content size
468 static void feed_source_01(jau::io::ByteInStream_Feed * data_feed, const size_t feed_size=1024) {
469 uint64_t xfer_total = 0;
470 jau::io::ByteInStream_File data_stream(data_feed->id());
471 const uint64_t file_size = data_stream.content_size();
472 data_feed->set_content_size( file_size );
473 std::vector<uint8_t> buffer;
474 buffer.resize(feed_size);
475 while( data_stream.good() && xfer_total < file_size ) {
476 size_t count = data_stream.read(buffer.data(), buffer.size());
477 if( 0 < count ) {
478 xfer_total += count;
479 if( data_feed->write(buffer.data(), count) ) {
480 jau::sleep_for( 16_ms );
481 } else {
482 break;
483 }
484 }
485 }
486 // probably set after transfering due to above sleep, which also ends when total size has been reached.
487 data_feed->set_eof( !data_feed->fail() && xfer_total == file_size ? jau::io::async_io_result_t::SUCCESS : jau::io::async_io_result_t::FAILED );
488 }
489
490 // full speed, with content size
491 static void feed_source_10(jau::io::ByteInStream_Feed * data_feed, const size_t feed_size=1024) {
492 uint64_t xfer_total = 0;
493 jau::io::ByteInStream_File data_stream(data_feed->id());
494 const uint64_t file_size = data_stream.content_size();
495 data_feed->set_content_size( data_stream.content_size() );
496 std::vector<uint8_t> buffer;
497 buffer.resize(feed_size);
498 while( data_stream.good() && xfer_total < file_size ) {
499 size_t count = data_stream.read(buffer.data(), buffer.size());
500 if( 0 < count ) {
501 xfer_total += count;
502 if( !data_feed->write(buffer.data(), count) ) {
503 break;
504 }
505 }
506 }
507 data_feed->set_eof( !data_feed->fail() && xfer_total == file_size ? jau::io::async_io_result_t::SUCCESS : jau::io::async_io_result_t::FAILED );
508 }
509
510 // full speed, no content size, interrupting @ 1024 bytes within our header
511 static void feed_source_20(jau::io::ByteInStream_Feed * data_feed, const size_t feed_size=1024) {
512 uint64_t xfer_total = 0;
513 jau::io::ByteInStream_File data_stream(data_feed->id());
514 std::vector<uint8_t> buffer;
515 buffer.resize(feed_size);
516 while( data_stream.good() ) {
517 size_t count = data_stream.read(buffer.data(), buffer.size());
518 if( 0 < count ) {
519 xfer_total += count;
520 if( data_feed->write(buffer.data(), count) ) {
521 if( xfer_total >= 1024 ) {
522 data_feed->set_eof( jau::io::async_io_result_t::FAILED ); // calls data_feed->interruptReader();
523 return;
524 }
525 } else {
526 break;
527 }
528 }
529 }
530 // probably set after transfering due to above sleep, which also ends when total size has been reached.
531 // data_feed->set_eof( jau::io::async_io_result_t::SUCCESS );
532 }
533
534 // full speed, with content size, interrupting 1/4 way
535 static void feed_source_21(jau::io::ByteInStream_Feed * data_feed, const size_t feed_size=1024) {
536 uint64_t xfer_total = 0;
537 jau::io::ByteInStream_File data_stream(data_feed->id());
538 const uint64_t file_size = data_stream.content_size();
539 data_feed->set_content_size( data_stream.content_size() );
540 std::vector<uint8_t> buffer;
541 buffer.resize(feed_size);
542 while( data_stream.good() ) {
543 size_t count = data_stream.read(buffer.data(), buffer.size());
544 if( 0 < count ) {
545 xfer_total += count;
546 if( data_feed->write(buffer.data(), count) ) {
547 if( xfer_total >= file_size/4 ) {
548 data_feed->set_eof( jau::io::async_io_result_t::FAILED ); // calls data_feed->interruptReader();
549 return;
550 }
551 } else {
552 break;
553 }
554 }
555 }
556 // probably set after transfering due to above sleep, which also ends when total size has been reached.
557 // data_feed->set_eof( jau::io::async_io_result_t::SUCCESS );
558 }
559
561 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
562 const size_t buffer_size = 4096;
563 const size_t feed_size = 1024;
564 {
565 const size_t file_idx = IDX_11kiB;
566 {
567 // full speed, with content size
568 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
569 std::thread feeder_thread= std::thread(&feed_source_10, &data_feed, feed_size);
570
571 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
572 if( feeder_thread.joinable() ) {
573 feeder_thread.join();
574 }
575 REQUIRE( true == res );
576
577 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
578 REQUIRE( true == out_stats.exists() );
579 REQUIRE( true == out_stats.is_file() );
580 REQUIRE( data_feed.content_size() == out_stats.size() );
581 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
582 REQUIRE( true == jau::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
583 }
584 {
585 // throttled, with content size
586 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
587 std::thread feeder_thread= std::thread(&feed_source_01, &data_feed, feed_size);
588
589 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
590 if( feeder_thread.joinable() ) {
591 feeder_thread.join();
592 }
593 REQUIRE( true == res );
594
595 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
596 REQUIRE( true == out_stats.exists() );
597 REQUIRE( true == out_stats.is_file() );
598 REQUIRE( data_feed.content_size() == out_stats.size() );
599 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
600 REQUIRE( true == jau::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
601 }
602 {
603 // throttled, no content size, interruptReader() via set_eof() will avoid timeout
604 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
605 std::thread feeder_thread= std::thread(&feed_source_00, &data_feed, feed_size);
606
607 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
608 if( feeder_thread.joinable() ) {
609 feeder_thread.join();
610 }
611 REQUIRE( true == res );
612
613 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
614 REQUIRE( true == out_stats.exists() );
615 REQUIRE( true == out_stats.is_file() );
616 REQUIRE( data_feed.content_size() == 0 );
617 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
618 REQUIRE( true == jau::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
619 }
620 }
621 {
622 const size_t file_idx = IDX_65MiB;
623 {
624 // full speed, with content size
625 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
626 std::thread feeder_thread= std::thread(&feed_source_10, &data_feed, feed_size);
627
628 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
629 if( feeder_thread.joinable() ) {
630 feeder_thread.join();
631 }
632 REQUIRE( true == res );
633
634 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
635 REQUIRE( true == out_stats.exists() );
636 REQUIRE( true == out_stats.is_file() );
637 REQUIRE( data_feed.content_size() == out_stats.size() );
638 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
639 REQUIRE( true == jau::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
640 }
641 }
642 }
643
645 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
646 const size_t buffer_size = 32768;
647 const size_t feed_size = 32768;
648 {
649 const size_t file_idx = IDX_11kiB;
650 {
651 // full speed, with content size
652 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
653 std::thread feeder_thread= std::thread(&feed_source_10, &data_feed, feed_size);
654
655 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
656 if( feeder_thread.joinable() ) {
657 feeder_thread.join();
658 }
659 REQUIRE( true == res );
660
661 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
662 REQUIRE( true == out_stats.exists() );
663 REQUIRE( true == out_stats.is_file() );
664 REQUIRE( data_feed.content_size() == out_stats.size() );
665 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
666 REQUIRE( true == jau::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
667 }
668 {
669 // throttled, with content size
670 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
671 std::thread feeder_thread= std::thread(&feed_source_01, &data_feed, feed_size);
672
673 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
674 if( feeder_thread.joinable() ) {
675 feeder_thread.join();
676 }
677 REQUIRE( true == res );
678
679 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
680 REQUIRE( true == out_stats.exists() );
681 REQUIRE( true == out_stats.is_file() );
682 REQUIRE( data_feed.content_size() == out_stats.size() );
683 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
684 REQUIRE( true == jau::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
685 }
686 {
687 // throttled, no content size, interruptReader() via set_eof() will avoid timeout
688 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
689 std::thread feeder_thread= std::thread(&feed_source_00, &data_feed, feed_size);
690
691 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
692 if( feeder_thread.joinable() ) {
693 feeder_thread.join();
694 }
695 REQUIRE( true == res );
696
697 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
698 REQUIRE( true == out_stats.exists() );
699 REQUIRE( true == out_stats.is_file() );
700 REQUIRE( data_feed.content_size() == 0 );
701 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
702 REQUIRE( true == jau::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
703 }
704 }
705 }
706
708 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
709 const size_t buffer_size = 32768;
710 const size_t feed_size = 32768;
711 {
712 const size_t file_idx = IDX_65MiB;
713 {
714 // full speed, with content size
715 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
716 std::thread feeder_thread= std::thread(&feed_source_10, &data_feed, feed_size);
717
718 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
719 if( feeder_thread.joinable() ) {
720 feeder_thread.join();
721 }
722 REQUIRE( true == res );
723
724 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
725 REQUIRE( true == out_stats.exists() );
726 REQUIRE( true == out_stats.is_file() );
727 REQUIRE( data_feed.content_size() == out_stats.size() );
728 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
729 REQUIRE( true == jau::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
730 }
731 }
732 }
733
735 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
736 const size_t buffer_size = 4096;
737 const size_t feed_size = 1024;
738 {
739 const size_t file_idx = IDX_65MiB;
740 {
741 // full speed, no content size, interrupting @ 1024 bytes within our header
742 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
743 std::thread feeder_thread= std::thread(&feed_source_20, &data_feed, feed_size);
744
745 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
746 if( feeder_thread.joinable() ) {
747 feeder_thread.join();
748 }
749 REQUIRE( false == res );
750
751 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
752 REQUIRE( true == out_stats.exists() );
753 REQUIRE( true == out_stats.is_file() );
754 REQUIRE( false == data_feed.has_content_size() );
755 REQUIRE( 0 == data_feed.content_size() );
756 REQUIRE( fname_payload_size_lst[file_idx] > out_stats.size() ); // interrupted...
757 REQUIRE( false == jau::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
758 }
759 {
760 // full speed, with content size, interrupting 1/4 way
761 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
762 std::thread feeder_thread= std::thread(&feed_source_21, &data_feed, feed_size);
763
764 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
765 if( feeder_thread.joinable() ) {
766 feeder_thread.join();
767 }
768 REQUIRE( false == res );
769
770 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
771 REQUIRE( true == out_stats.exists() );
772 REQUIRE( true == out_stats.is_file() );
773 REQUIRE( true == data_feed.has_content_size() );
774 REQUIRE( fname_payload_size_lst[file_idx] == data_feed.content_size() );
775 REQUIRE( data_feed.content_size() > out_stats.size() ); // interrupted...
776 REQUIRE( false == jau::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
777 }
778 }
779 }
780
781};
782
783std::vector<std::string> TestByteStream01::fname_payload_lst;
784std::vector<std::string> TestByteStream01::fname_payload_copy_lst;
785std::vector<uint64_t> TestByteStream01::fname_payload_size_lst;
786
789
793
796
static void feed_source_00(jau::io::ByteInStream_Feed *data_feed, const size_t feed_size=1024)
static void feed_source_20(jau::io::ByteInStream_Feed *data_feed, const size_t feed_size=1024)
static bool transfer(jau::io::ByteInStream &input, const std::string &output_fname, const size_t buffer_size=4096)
void test04_copy_file_ok_65MiB_buff32k()
static void httpd_start()
static void feed_source_01(jau::io::ByteInStream_Feed *data_feed, const size_t feed_size=1024)
void test20_copy_fed_ok_buff4k_feed1k()
void test01_copy_file_ok_11kiB_buff4k()
void test02_copy_file_ok_65MiB_buff4k()
static void feed_source_10(jau::io::ByteInStream_Feed *data_feed, const size_t feed_size=1024)
static void feed_source_21(jau::io::ByteInStream_Feed *data_feed, const size_t feed_size=1024)
Platform agnostic representation of POSIX ::lstat() and ::stat() for a given pathname.
constexpr bool exists() const noexcept
Returns true if entity does not exist, exclusive bit.
constexpr bool is_file() const noexcept
Returns true if entity is a file, might be in combination with is_link().
uint64_t size() const noexcept
Returns the size in bytes of this element if is_file(), otherwise zero.
std::string path() const noexcept
Returns the unix path representation.
Ringbuffer-Based byte input stream with an externally provisioned data feed.
void set_content_size(const uint64_t size) noexcept
Set known content size, informal only.
bool has_content_size() const noexcept override
Returns true if implementation is aware of content_size(), otherwise false.
void set_eof(const async_io_result_t result) noexcept
Set end-of-data (EOS), i.e.
bool write(uint8_t in[], size_t length, const jau::fraction_i64 &timeout) noexcept
Write given bytes to the async ringbuffer using explicit given timeout.
uint64_t content_size() const noexcept override
Returns the content_size if known.
std::string id() const noexcept override
return the id of this data source
File based byte input stream, including named file descriptor.
uint64_t content_size() const noexcept override
Returns the content_size if known.
size_t read(void *, size_t) noexcept override
Read from the source.
std::string id() const noexcept override
return the id of this data source
Ringbuffer-Based byte input stream with a URL connection provisioned data feed.
uint64_t content_size() const noexcept override
Returns the content_size if known.
bool has_content_size() const noexcept override
Returns true if implementation is aware of content_size(), otherwise false.
Abstract byte input stream object.
virtual void close() noexcept=0
Close the stream if supported by the underlying mechanism.
virtual bool has_content_size() const noexcept=0
Returns true if implementation is aware of content_size(), otherwise false.
virtual uint64_t content_size() const noexcept=0
Returns the content_size if known.
virtual std::string to_string() const noexcept=0
File based byte output stream, including named file descriptor.
size_t write(const void *, size_t) noexcept override
Write to the data sink.
std::string to_string() const noexcept override
bool is_open() const noexcept override
Checks if the stream has an associated file.
bool fail() const noexcept
Checks if an error has occurred.
bool good() const noexcept
Checks if no error nor eof() has occurred i.e.
#define ERR_PRINT(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
Definition debug.hpp:109
#define ERR_PRINT2(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
Definition debug.hpp:112
#define IRQ_PRINT(...)
Use for unconditional interruption messages, prefix '[elapsed_time] Interrupted @ FILE:LINE FUNC: '.
Definition debug.hpp:115
std::string to_string(const endian_t v) noexcept
Return std::string representation of the given endian.
constexpr std::string_view name(const Bool v) noexcept
std::string get_cwd() noexcept
Return the current working directory or empty on failure.
Definition file_util.cpp:84
bool compare(const file_stats &source1, const file_stats &source2, const bool verbose=false) noexcept
Compare the bytes of both files, denoted by source1 and source2.
bool remove(const std::string &path, const traverse_options topts=traverse_options::none) noexcept
Remove the given path.
bool to_fraction_i64(fraction_i64 &result, const std::string &value, const fraction_i64 &min_allowed, const fraction_i64 &max_allowed) noexcept
Stores the fraction_i64 value of the given string value in format <num>/<denom>, which may contain wh...
fraction_timespec getMonotonicTime() noexcept
Returns current monotonic time since Unix Epoch 00:00:00 UTC on 1970-01-01.
fraction< int64_t > fraction_i64
fraction using int64_t as integral type
std::unique_ptr< ByteInStream > to_ByteInStream(const std::string &path_or_uri, jau::fraction_i64 timeout=20_s) noexcept
Parses the given path_or_uri, if it matches a supported protocol, see jau::io::uri::protocol_supporte...
bool is_local_file_protocol(const std::string_view &uri) noexcept
Returns true if the uri-scheme of given uri matches the local file protocol, i.e.
Definition io_util.cpp:204
uint64_t read_stream(ByteInStream &in, secure_vector< uint8_t > &buffer, const StreamConsumerFunc &consumer_fn) noexcept
Synchronous byte input stream reader using the given StreamConsumerFunc consumer_fn.
Definition io_util.cpp:59
jau::function< bool(secure_vector< uint8_t > &, bool)> StreamConsumerFunc
Stream consumer function.
Definition io_util.hpp:86
std::vector< std::string_view > supported_protocols() noexcept
Returns a list of supported protocol supported by libcurl network protocols, queried at runtime.
Definition io_util.cpp:159
std::vector< T, jau::callocator_sec< T > > secure_vector
Definition io_util.hpp:46
void print_stats(const std::string &prefix, const uint64_t &out_bytes_total, const jau::fraction_i64 &td) noexcept
Definition io_util.cpp:760
bool protocol_supported(const std::string_view &uri) noexcept
Returns true if the uri-scheme of given uri matches a supported by libcurl network protocols otherwis...
Definition io_util.cpp:194
@ FAILED
Operation failed.
Definition io_util.hpp:70
@ SUCCESS
Operation succeeded.
Definition io_util.hpp:76
void PLAIN_PRINT(const bool printPrefix, const char *format,...) noexcept
Use for unconditional plain messages, prefix '[elapsed_time] ' if printPrefix == true.
Definition debug.cpp:258
int fprintf_td(const uint64_t elapsed_ms, FILE *stream, const char *format,...) noexcept
Convenient fprintf() invocation, prepending the given elapsed_ms timestamp.
Definition debug.cpp:270
bool sleep_for(const fraction_timespec &relative_time, const bool monotonic=true, const bool ignore_irq=true) noexcept
sleep_for causes the current thread to block until a specific amount of time has passed.
Timespec structure using int64_t for its components in analogy to struct timespec_t on 64-bit platfor...
METHOD_AS_TEST_CASE(TestByteStream01::test00a_protocols_error, "test00a_protocols_error")
constexpr std::string_view mini_httpd_exe