jaulib v1.3.0
Jau Support Library (C++, Java, ..)
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 <cstring>
28
30
31#include <jau/debug.hpp>
32#include <jau/file_util.hpp>
33#include <jau/byte_stream.hpp>
34#include <jau/io_util.hpp>
35
36#include "test_httpd.hpp"
37
38extern "C" {
39 #include <unistd.h>
40}
41
42using namespace jau::fractions_i64_literals;
43
45 private:
46 const size_t IDX_11kiB = 0;
47 const size_t IDX_65MiB = 1;
48 static std::vector<std::string> fname_payload_lst;
49 static std::vector<std::string> fname_payload_copy_lst;
50 static std::vector<uint64_t> fname_payload_size_lst;
51 const std::string url_input_root = "http://localhost:8080/";
52
53 class data {
54 private:
55 static bool add_test_file(const std::string& name, const size_t size_limit) {
56 jau::fs::remove(name);
57 jau::fs::remove(name+".enc");
58 jau::fs::remove(name+".enc.dec");
59 jau::fs::remove(name+".copy");
60 size_t size;
61 {
62 static const std::string one_line = "Hello World, this is a test and I like it. Exactly 100 characters long. 0123456780 abcdefghjklmnop..";
64
65 REQUIRE( ofs.good() == true );
66 REQUIRE( ofs.is_open() == true );
67
68 for(size=0; size < size_limit; size+=one_line.size()) {
69 if( one_line.size() != ofs.write(one_line.data(), one_line.size()) ) {
70 ERR_PRINT("Write %zu bytes to test file failed: %s", one_line.size(), ofs.to_string().c_str());
71 return false;
72 }
73 }
74 if( 1 != ofs.write("X", 1) ) { // make it odd
75 ERR_PRINT("Write %zu bytes to test file failed: %s", 1, ofs.to_string().c_str());
76 return false;
77 }
78 size += 1;
79 }
80 fname_payload_lst.push_back(name);
81 fname_payload_copy_lst.push_back(name+".copy");
82 fname_payload_size_lst.push_back( size );
83 return true;
84 }
85 data() {
86 REQUIRE( true == add_test_file("testfile_blob_01_11kiB.bin", (size_t)(1024*11)) );
87 REQUIRE( true == add_test_file("testfile_blob_02_65MiB.bin", (size_t)(1024*1024*65)) );
88 }
89 public:
90 static const data& get() {
91 static data instance;
92 return instance;
93 }
94 };
95
96 public:
98 // produce fresh demo data once per whole test class
99 const data& d = data::get();
100 (void)d;
101 }
102
105 int res = std::system("killall mini_httpd");
106 (void)res;
107 }
108 }
109
110 static void httpd_start() {
112 int res = std::system("killall mini_httpd");
113 (void)res;
114 const std::string cwd = jau::fs::get_cwd();
115 const std::string cmd = std::string(mini_httpd_exe)+" -p 8080 -l "+cwd+"/mini_httpd.log";
116 jau::PLAIN_PRINT(true, "%s", cmd.c_str());
117 res = std::system(cmd.c_str());
118 (void)res;
119 }
120 }
121
122 static bool transfer(jau::io::ByteInStream& input, const std::string& output_fname, const size_t buffer_size=4096) {
124 jau::PLAIN_PRINT(true, "Transfer Start: %s", input.to_string().c_str());
125 {
126 const jau::fs::file_stats output_stats(output_fname);
127 if( output_stats.exists() ) {
128 if( output_stats.is_file() ) {
129 if( !jau::fs::remove(output_fname) ) {
130 ERR_PRINT2("ByteStream copy failed: Failed deletion of existing output file %s", output_fname.c_str());
131 return false;
132 }
133 } else {
134 ERR_PRINT2("ByteStream copy failed: Not overwriting existing output file %s", output_fname.c_str());
135 return false;
136 }
137 }
138 }
139 jau::io::ByteOutStream_File outfile(output_fname);
140 if ( !outfile.good() || !outfile.is_open() ) {
141 ERR_PRINT2("ByteStream copy failed: Couldn't open output file %s", output_fname.c_str());
142 return false;
143 }
144
145 uint64_t out_bytes_payload = 0;
146 jau::io::StreamConsumerFunc consume_data = [&](jau::io::secure_vector<uint8_t>& data, bool is_final) -> bool {
147 if( !is_final && ( !input.has_content_size() || out_bytes_payload + data.size() < input.content_size() ) ) {
148 const size_t written = outfile.write(data.data(), data.size());
149 out_bytes_payload += written;
150 return data.size() == written; // continue ..
151 } else {
152 const size_t written = outfile.write(data.data(), data.size());
153 out_bytes_payload += written;
154 return false; // EOS
155 }
156 };
158 io_buffer.reserve(buffer_size);
159 const uint64_t in_bytes_total = jau::io::read_stream(input, io_buffer, consume_data);
160 input.close();
161
162 if ( 0==in_bytes_total || input.fail() ) {
163 IRQ_PRINT("ByteStream copy failed: Input file read failed in %s, out %s", input.to_string().c_str(), outfile.to_string().c_str());
164 return false;
165 }
166 if ( outfile.fail() ) {
167 IRQ_PRINT("ByteStream copy failed: Output file write failed in %s, out %s", input.to_string().c_str(), outfile.to_string().c_str());
168 return false;
169 }
170
172 jau::io::print_stats("Transfer "+output_fname, out_bytes_payload, _td);
173 jau::PLAIN_PRINT(true, "Transfer End: %s", input.to_string().c_str());
174
175 return true;
176 }
177
179 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
180 const bool http_support_expected = jau::io::uri_tk::protocol_supported("http:");
181 const bool file_support_expected = jau::io::uri_tk::protocol_supported("file:");
182 httpd_start();
183 {
184 std::vector<std::string_view> protos = jau::io::uri_tk::supported_protocols();
185 jau::PLAIN_PRINT(true, "test00_protocols: Supported protocols: %zu: %s", protos.size(), jau::to_string(protos, ",").c_str());
186 if( http_support_expected ) { // assume no http -> no curl
187 REQUIRE( 0 < protos.size() );
188 } else {
189 REQUIRE( 0 == protos.size() );
190 }
191 }
192 const size_t file_idx = IDX_11kiB;
193 {
194 const std::string url = "not_exiting_file.txt";
195 REQUIRE( false == jau::io::uri_tk::is_local_file_protocol(url) );
196 REQUIRE( false == jau::io::uri_tk::protocol_supported(url) );
197
198 std::unique_ptr<jau::io::ByteInStream> in = jau::io::to_ByteInStream(url);
199 if( nullptr != in ) {
200 jau::PLAIN_PRINT(true, "test00_protocols: not_exiting_file: %s", in->to_string().c_str());
201 }
202 REQUIRE( nullptr == in );
203 }
204 {
205 const std::string url = "file://not_exiting_file_uri.txt";
206 REQUIRE( true == jau::io::uri_tk::is_local_file_protocol(url) );
207 REQUIRE( file_support_expected == jau::io::uri_tk::protocol_supported(url) );
208
209 std::unique_ptr<jau::io::ByteInStream> in = jau::io::to_ByteInStream(url);
210 if( nullptr != in ) {
211 jau::PLAIN_PRINT(true, "test00_protocols: not_exiting_file_uri: %s", in->to_string().c_str());
212 }
213 REQUIRE( nullptr == in );
214 }
215 {
216 const std::string url = "lala://localhost:8080/" + fname_payload_lst[file_idx];
217 REQUIRE( false == jau::io::uri_tk::is_local_file_protocol(url) );
218 REQUIRE( false == jau::io::uri_tk::protocol_supported(url) );
219
220 std::unique_ptr<jau::io::ByteInStream> in = jau::io::to_ByteInStream(url);
221 if( nullptr != in ) {
222 jau::PLAIN_PRINT(true, "test00_protocols: not_exiting_protocol_uri: %s", in->to_string().c_str());
223 }
224 REQUIRE( nullptr == in );
225 }
226 {
227 const std::string url = url_input_root + "not_exiting_http_uri.txt";
228 REQUIRE( false == jau::io::uri_tk::is_local_file_protocol(url) );
229 REQUIRE( http_support_expected == jau::io::uri_tk::protocol_supported(url) );
230
231 std::unique_ptr<jau::io::ByteInStream> in = jau::io::to_ByteInStream(url);
232 if( http_support_expected ) {
233 REQUIRE( nullptr != in );
234 jau::sleep_for( 100_ms ); // time to read 404 response
235 jau::PLAIN_PRINT(true, "test00_protocols: not_exiting_http_uri: %s", in->to_string().c_str());
236 REQUIRE( false == in->good() );
237 REQUIRE( true == in->fail() );
238 REQUIRE( 0 == in->content_size() );
239 } else {
240 REQUIRE( nullptr == in );
241 }
242 }
243 }
244
246 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
247 const bool http_support_expected = jau::io::uri_tk::protocol_supported("http:");
248 const bool file_support_expected = jau::io::uri_tk::protocol_supported("file:");
249 httpd_start();
250 const size_t file_idx = IDX_11kiB;
251 {
252 const std::string url = fname_payload_lst[file_idx];
253 REQUIRE( false == jau::io::uri_tk::is_local_file_protocol(url) );
254 REQUIRE( false == jau::io::uri_tk::protocol_supported(url) );
255
256 std::unique_ptr<jau::io::ByteInStream> in = jau::io::to_ByteInStream(url);
257 if( nullptr != in ) {
258 jau::PLAIN_PRINT(true, "test00_protocols: local-file-0: %s", in->to_string().c_str());
259 }
260 REQUIRE( nullptr != in );
261 REQUIRE( false == in->fail() );
262
263 bool res = transfer(*in, fname_payload_copy_lst[file_idx]);
264 REQUIRE( true == res );
265
266 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
267 REQUIRE( true == out_stats.exists() );
268 REQUIRE( true == out_stats.is_file() );
269 REQUIRE( in->content_size() == out_stats.size() );
270 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
271 REQUIRE( true == jau::fs::compare(in->id(), out_stats.path(), true /* verbose */) );
272 }
273 {
274 const std::string url = "file://" + fname_payload_lst[file_idx];
275 REQUIRE( true == jau::io::uri_tk::is_local_file_protocol(url) );
276 REQUIRE( file_support_expected == jau::io::uri_tk::protocol_supported(url) );
277
278 std::unique_ptr<jau::io::ByteInStream> in = jau::io::to_ByteInStream(url);
279 if( nullptr != in ) {
280 jau::PLAIN_PRINT(true, "test00_protocols: local-file-1: %s", in->to_string().c_str());
281 } else {
282 jau::PLAIN_PRINT(true, "test00_protocols: local-file-1: NULL from url '%s'", url.c_str());
283 }
284 REQUIRE( nullptr != in );
285 REQUIRE( false == in->fail() );
286
287 bool res = transfer(*in, fname_payload_copy_lst[file_idx]);
288 REQUIRE( true == res );
289
290 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
291 REQUIRE( true == out_stats.exists() );
292 REQUIRE( true == out_stats.is_file() );
293 REQUIRE( in->content_size() == out_stats.size() );
294 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
295 REQUIRE( true == jau::fs::compare(fname_payload_lst[file_idx], out_stats.path(), true /* verbose */) );
296 }
297 {
298 const std::string url = url_input_root + fname_payload_lst[file_idx];
299 REQUIRE( false == jau::io::uri_tk::is_local_file_protocol(url) );
300 REQUIRE( http_support_expected == jau::io::uri_tk::protocol_supported(url) );
301
302 std::unique_ptr<jau::io::ByteInStream> in = jau::io::to_ByteInStream(url);
303 if( nullptr != in ) {
304 jau::PLAIN_PRINT(true, "test00_protocols: http: %s", in->to_string().c_str());
305 }
306 if( http_support_expected ) {
307 REQUIRE( nullptr != in );
308 REQUIRE( false == in->fail() );
309
310 bool res = transfer(*in, fname_payload_copy_lst[file_idx]);
311 REQUIRE( true == res );
312
313 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
314 REQUIRE( true == out_stats.exists() );
315 REQUIRE( true == out_stats.is_file() );
316 REQUIRE( in->content_size() == out_stats.size() );
317 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
318 REQUIRE( true == jau::fs::compare(fname_payload_lst[file_idx], out_stats.path(), true /* verbose */) );
319 } else {
320 REQUIRE( nullptr == in );
321 }
322 }
323 }
324
326 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
327 const size_t file_idx = IDX_11kiB;
328 jau::io::ByteInStream_File data_stream(fname_payload_lst[file_idx]);
329
330 bool res = transfer(data_stream, fname_payload_copy_lst[file_idx], 4096);
331 REQUIRE( true == res );
332
333 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
334 REQUIRE( true == out_stats.exists() );
335 REQUIRE( true == out_stats.is_file() );
336 REQUIRE( data_stream.content_size() == out_stats.size() );
337 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
338 REQUIRE( true == jau::fs::compare(data_stream.id(), out_stats.path(), true /* verbose */) );
339 }
340
342 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
343 const size_t file_idx = IDX_65MiB;
344 jau::io::ByteInStream_File data_stream(fname_payload_lst[file_idx]);
345
346 bool res = transfer(data_stream, fname_payload_copy_lst[file_idx], 4096);
347 REQUIRE( true == res );
348
349 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
350 REQUIRE( true == out_stats.exists() );
351 REQUIRE( true == out_stats.is_file() );
352 REQUIRE( data_stream.content_size() == out_stats.size() );
353 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
354 REQUIRE( true == jau::fs::compare(data_stream.id(), out_stats.path(), true /* verbose */) );
355 }
356
358 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
359 const size_t file_idx = IDX_65MiB;
360 jau::io::ByteInStream_File data_stream(fname_payload_lst[file_idx]);
361
362 bool res = transfer(data_stream, fname_payload_copy_lst[file_idx], 32768);
363 REQUIRE( true == res );
364
365 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
366 REQUIRE( true == out_stats.exists() );
367 REQUIRE( true == out_stats.is_file() );
368 REQUIRE( data_stream.content_size() == out_stats.size() );
369 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
370 REQUIRE( true == jau::fs::compare(data_stream.id(), out_stats.path(), true /* verbose */) );
371 }
372
374 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
376 jau::PLAIN_PRINT(true, "http not supported, abort\n");
377 return;
378 }
379 httpd_start();
380 {
381 const size_t file_idx = IDX_11kiB;
382
383 const std::string uri_original = url_input_root + fname_payload_lst[file_idx];
384
385 jau::io::ByteInStream_URL data_stream(uri_original, 500_ms);
386
387 bool res = transfer(data_stream, fname_payload_copy_lst[file_idx], 32768);
388 REQUIRE( true == res );
389
390 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
391 REQUIRE( true == out_stats.exists() );
392 REQUIRE( true == out_stats.is_file() );
393 REQUIRE( data_stream.content_size() == out_stats.size() );
394 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
395 REQUIRE( true == jau::fs::compare(fname_payload_lst[file_idx], out_stats.path(), true /* verbose */) );
396 }
397 {
398 const size_t file_idx = IDX_65MiB;
399
400 const std::string uri_original = url_input_root + fname_payload_lst[file_idx];
401
402 jau::io::ByteInStream_URL data_stream(uri_original, 500_ms);
403
404 bool res = transfer(data_stream, fname_payload_copy_lst[file_idx], 32768);
405 REQUIRE( true == res );
406
407 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
408 REQUIRE( true == out_stats.exists() );
409 REQUIRE( true == out_stats.is_file() );
410 REQUIRE( data_stream.content_size() == out_stats.size() );
411 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
412 REQUIRE( true == jau::fs::compare(fname_payload_lst[file_idx], out_stats.path(), true /* verbose */) );
413 }
414 }
415
417 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
419 jau::PLAIN_PRINT(true, "http not supported, abort\n");
420 return;
421 }
422 httpd_start();
423 {
424 const size_t file_idx = IDX_11kiB;
425
426 const std::string uri_original = url_input_root + "doesnt_exists.txt";
427
428 jau::io::ByteInStream_URL data_stream(uri_original, 500_ms);
429
430 bool res = transfer(data_stream, fname_payload_copy_lst[file_idx]);
431 REQUIRE( false == res );
432
433 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
434 REQUIRE( true == out_stats.exists() );
435 REQUIRE( true == out_stats.is_file() );
436 REQUIRE( data_stream.fail() == true );
437 REQUIRE( data_stream.has_content_size() == false );
438 REQUIRE( data_stream.content_size() == 0 );
439 REQUIRE( 0 == out_stats.size() );
440 }
441 }
442
443 // throttled, no content size, interruptReader() via set_eof() will avoid timeout
444 static void feed_source_00(jau::io::ByteInStream_Feed * data_feed, const size_t feed_size=1024) {
445 uint64_t xfer_total = 0;
446 jau::io::ByteInStream_File data_stream(data_feed->id());
447 uint8_t buffer[feed_size];
448 while( data_stream.good() ) {
449 size_t count = data_stream.read(buffer, sizeof(buffer));
450 if( 0 < count ) {
451 xfer_total += count;
452 if( data_feed->write(buffer, count) ) {
453 jau::sleep_for( 16_ms );
454 } else {
455 break;
456 }
457 }
458 }
459 // probably set after transfering due to above sleep, which also ends when total size has been reached.
461 (void)xfer_total; // not used yet ..
462 }
463
464 // throttled, with content size
465 static void feed_source_01(jau::io::ByteInStream_Feed * data_feed, const size_t feed_size=1024) {
466 uint64_t xfer_total = 0;
467 jau::io::ByteInStream_File data_stream(data_feed->id());
468 const uint64_t file_size = data_stream.content_size();
469 data_feed->set_content_size( file_size );
470 uint8_t buffer[feed_size];
471 while( data_stream.good() && xfer_total < file_size ) {
472 size_t count = data_stream.read(buffer, sizeof(buffer));
473 if( 0 < count ) {
474 xfer_total += count;
475 if( data_feed->write(buffer, count) ) {
476 jau::sleep_for( 16_ms );
477 } else {
478 break;
479 }
480 }
481 }
482 // probably set after transfering due to above sleep, which also ends when total size has been reached.
483 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 );
484 }
485
486 // full speed, with content size
487 static void feed_source_10(jau::io::ByteInStream_Feed * data_feed, const size_t feed_size=1024) {
488 uint64_t xfer_total = 0;
489 jau::io::ByteInStream_File data_stream(data_feed->id());
490 const uint64_t file_size = data_stream.content_size();
491 data_feed->set_content_size( data_stream.content_size() );
492 uint8_t buffer[feed_size];
493 while( data_stream.good() && xfer_total < file_size ) {
494 size_t count = data_stream.read(buffer, sizeof(buffer));
495 if( 0 < count ) {
496 xfer_total += count;
497 if( !data_feed->write(buffer, count) ) {
498 break;
499 }
500 }
501 }
502 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 );
503 }
504
505 // full speed, no content size, interrupting @ 1024 bytes within our header
506 static void feed_source_20(jau::io::ByteInStream_Feed * data_feed, const size_t feed_size=1024) {
507 uint64_t xfer_total = 0;
508 jau::io::ByteInStream_File data_stream(data_feed->id());
509 uint8_t buffer[feed_size];
510 while( data_stream.good() ) {
511 size_t count = data_stream.read(buffer, sizeof(buffer));
512 if( 0 < count ) {
513 xfer_total += count;
514 if( data_feed->write(buffer, count) ) {
515 if( xfer_total >= 1024 ) {
516 data_feed->set_eof( jau::io::async_io_result_t::FAILED ); // calls data_feed->interruptReader();
517 return;
518 }
519 } else {
520 break;
521 }
522 }
523 }
524 // probably set after transfering due to above sleep, which also ends when total size has been reached.
525 // data_feed->set_eof( jau::io::async_io_result_t::SUCCESS );
526 }
527
528 // full speed, with content size, interrupting 1/4 way
529 static void feed_source_21(jau::io::ByteInStream_Feed * data_feed, const size_t feed_size=1024) {
530 uint64_t xfer_total = 0;
531 jau::io::ByteInStream_File data_stream(data_feed->id());
532 const uint64_t file_size = data_stream.content_size();
533 data_feed->set_content_size( data_stream.content_size() );
534 uint8_t buffer[feed_size];
535 while( data_stream.good() ) {
536 size_t count = data_stream.read(buffer, sizeof(buffer));
537 if( 0 < count ) {
538 xfer_total += count;
539 if( data_feed->write(buffer, count) ) {
540 if( xfer_total >= file_size/4 ) {
541 data_feed->set_eof( jau::io::async_io_result_t::FAILED ); // calls data_feed->interruptReader();
542 return;
543 }
544 } else {
545 break;
546 }
547 }
548 }
549 // probably set after transfering due to above sleep, which also ends when total size has been reached.
550 // data_feed->set_eof( jau::io::async_io_result_t::SUCCESS );
551 }
552
554 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
555 const size_t buffer_size = 4096;
556 const size_t feed_size = 1024;
557 {
558 const size_t file_idx = IDX_11kiB;
559 {
560 // full speed, with content size
561 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
562 std::thread feeder_thread= std::thread(&feed_source_10, &data_feed, feed_size);
563
564 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
565 if( feeder_thread.joinable() ) {
566 feeder_thread.join();
567 }
568 REQUIRE( true == res );
569
570 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
571 REQUIRE( true == out_stats.exists() );
572 REQUIRE( true == out_stats.is_file() );
573 REQUIRE( data_feed.content_size() == out_stats.size() );
574 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
575 REQUIRE( true == jau::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
576 }
577 {
578 // throttled, with content size
579 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
580 std::thread feeder_thread= std::thread(&feed_source_01, &data_feed, feed_size);
581
582 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
583 if( feeder_thread.joinable() ) {
584 feeder_thread.join();
585 }
586 REQUIRE( true == res );
587
588 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
589 REQUIRE( true == out_stats.exists() );
590 REQUIRE( true == out_stats.is_file() );
591 REQUIRE( data_feed.content_size() == out_stats.size() );
592 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
593 REQUIRE( true == jau::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
594 }
595 {
596 // throttled, no content size, interruptReader() via set_eof() will avoid timeout
597 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
598 std::thread feeder_thread= std::thread(&feed_source_00, &data_feed, feed_size);
599
600 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
601 if( feeder_thread.joinable() ) {
602 feeder_thread.join();
603 }
604 REQUIRE( true == res );
605
606 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
607 REQUIRE( true == out_stats.exists() );
608 REQUIRE( true == out_stats.is_file() );
609 REQUIRE( data_feed.content_size() == 0 );
610 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
611 REQUIRE( true == jau::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
612 }
613 }
614 {
615 const size_t file_idx = IDX_65MiB;
616 {
617 // full speed, with content size
618 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
619 std::thread feeder_thread= std::thread(&feed_source_10, &data_feed, feed_size);
620
621 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
622 if( feeder_thread.joinable() ) {
623 feeder_thread.join();
624 }
625 REQUIRE( true == res );
626
627 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
628 REQUIRE( true == out_stats.exists() );
629 REQUIRE( true == out_stats.is_file() );
630 REQUIRE( data_feed.content_size() == out_stats.size() );
631 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
632 REQUIRE( true == jau::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
633 }
634 }
635 }
636
638 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
639 const size_t buffer_size = 32768;
640 const size_t feed_size = 32768;
641 {
642 const size_t file_idx = IDX_11kiB;
643 {
644 // full speed, with content size
645 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
646 std::thread feeder_thread= std::thread(&feed_source_10, &data_feed, feed_size);
647
648 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
649 if( feeder_thread.joinable() ) {
650 feeder_thread.join();
651 }
652 REQUIRE( true == res );
653
654 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
655 REQUIRE( true == out_stats.exists() );
656 REQUIRE( true == out_stats.is_file() );
657 REQUIRE( data_feed.content_size() == out_stats.size() );
658 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
659 REQUIRE( true == jau::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
660 }
661 {
662 // throttled, with content size
663 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
664 std::thread feeder_thread= std::thread(&feed_source_01, &data_feed, feed_size);
665
666 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
667 if( feeder_thread.joinable() ) {
668 feeder_thread.join();
669 }
670 REQUIRE( true == res );
671
672 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
673 REQUIRE( true == out_stats.exists() );
674 REQUIRE( true == out_stats.is_file() );
675 REQUIRE( data_feed.content_size() == out_stats.size() );
676 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
677 REQUIRE( true == jau::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
678 }
679 {
680 // throttled, no content size, interruptReader() via set_eof() will avoid timeout
681 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
682 std::thread feeder_thread= std::thread(&feed_source_00, &data_feed, feed_size);
683
684 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
685 if( feeder_thread.joinable() ) {
686 feeder_thread.join();
687 }
688 REQUIRE( true == res );
689
690 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
691 REQUIRE( true == out_stats.exists() );
692 REQUIRE( true == out_stats.is_file() );
693 REQUIRE( data_feed.content_size() == 0 );
694 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
695 REQUIRE( true == jau::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
696 }
697 }
698 }
699
701 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
702 const size_t buffer_size = 32768;
703 const size_t feed_size = 32768;
704 {
705 const size_t file_idx = IDX_65MiB;
706 {
707 // full speed, with content size
708 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
709 std::thread feeder_thread= std::thread(&feed_source_10, &data_feed, feed_size);
710
711 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
712 if( feeder_thread.joinable() ) {
713 feeder_thread.join();
714 }
715 REQUIRE( true == res );
716
717 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
718 REQUIRE( true == out_stats.exists() );
719 REQUIRE( true == out_stats.is_file() );
720 REQUIRE( data_feed.content_size() == out_stats.size() );
721 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
722 REQUIRE( true == jau::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
723 }
724 }
725 }
726
728 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
729 const size_t buffer_size = 4096;
730 const size_t feed_size = 1024;
731 {
732 const size_t file_idx = IDX_65MiB;
733 {
734 // full speed, no content size, interrupting @ 1024 bytes within our header
735 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
736 std::thread feeder_thread= std::thread(&feed_source_20, &data_feed, feed_size);
737
738 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
739 if( feeder_thread.joinable() ) {
740 feeder_thread.join();
741 }
742 REQUIRE( false == res );
743
744 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
745 REQUIRE( true == out_stats.exists() );
746 REQUIRE( true == out_stats.is_file() );
747 REQUIRE( false == data_feed.has_content_size() );
748 REQUIRE( 0 == data_feed.content_size() );
749 REQUIRE( fname_payload_size_lst[file_idx] > out_stats.size() ); // interrupted...
750 REQUIRE( false == jau::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
751 }
752 {
753 // full speed, with content size, interrupting 1/4 way
754 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
755 std::thread feeder_thread= std::thread(&feed_source_21, &data_feed, feed_size);
756
757 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
758 if( feeder_thread.joinable() ) {
759 feeder_thread.join();
760 }
761 REQUIRE( false == res );
762
763 jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
764 REQUIRE( true == out_stats.exists() );
765 REQUIRE( true == out_stats.is_file() );
766 REQUIRE( true == data_feed.has_content_size() );
767 REQUIRE( fname_payload_size_lst[file_idx] == data_feed.content_size() );
768 REQUIRE( data_feed.content_size() > out_stats.size() ); // interrupted...
769 REQUIRE( false == jau::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
770 }
771 }
772 }
773
774};
775
776std::vector<std::string> TestByteStream01::fname_payload_lst;
777std::vector<std::string> TestByteStream01::fname_payload_copy_lst;
778std::vector<uint64_t> TestByteStream01::fname_payload_size_lst;
779
782
786
789
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.
Definition: file_util.hpp:406
constexpr bool exists() const noexcept
Returns true if entity does not exist, exclusive bit.
Definition: file_util.hpp:648
constexpr bool is_file() const noexcept
Returns true if entity is a file, might be in combination with is_link().
Definition: file_util.hpp:639
uint64_t size() const noexcept
Returns the size in bytes of this element if is_file(), otherwise zero.
Definition: file_util.hpp:597
std::string path() const noexcept
Returns the unix path representation.
Definition: file_util.hpp:530
Class template jau::function is a general-purpose static-polymorphic function wrapper.
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.
std::string get_cwd() noexcept
Return the current working directory or empty on failure.
Definition: file_util.cpp:82
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.
Definition: file_util.cpp:1268
bool remove(const std::string &path, const traverse_options topts=traverse_options::none) noexcept
Remove the given path.
Definition: file_util.cpp:1173
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.
Definition: basic_types.cpp:52
std::vector< T, jau::callocator_sec< T > > secure_vector
Definition: io_util.hpp:46
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
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
void print_stats(const std::string &prefix, const uint64_t &out_bytes_total, const jau::fraction_i64 &td) noexcept
Definition: io_util.cpp:761
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.
@ SUCCESS
Operation succeeded.
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
Definition: test_httpd.hpp:33