Skip to content

Commit

Permalink
Support Input Stream from read-only file (#582)
Browse files Browse the repository at this point in the history

Co-authored-by: Michael Graeb <[email protected]>
  • Loading branch information
TingDaoK and graebm authored Jul 6, 2023
1 parent 1f9a085 commit e87baa2
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 51 deletions.
2 changes: 1 addition & 1 deletion source/stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ struct aws_input_stream *aws_input_stream_new_from_file(struct aws_allocator *al

struct aws_input_stream_file_impl *impl = aws_mem_calloc(allocator, 1, sizeof(struct aws_input_stream_file_impl));

impl->file = aws_fopen(file_name, "r+b");
impl->file = aws_fopen(file_name, "rb");
if (impl->file == NULL) {
goto on_error;
}
Expand Down
54 changes: 29 additions & 25 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,18 @@ add_pipe_test_case(pipe_clean_up_cancels_pending_writes)

add_test_case(event_loop_xthread_scheduled_tasks_execute)
add_test_case(event_loop_canceled_tasks_run_in_el_thread)
if (USE_IO_COMPLETION_PORTS)

if(USE_IO_COMPLETION_PORTS)
add_test_case(event_loop_completion_events)
else ()
else()
add_test_case(event_loop_subscribe_unsubscribe)
add_test_case(event_loop_writable_event_on_subscribe)
add_test_case(event_loop_no_readable_event_before_write)
add_test_case(event_loop_readable_event_after_write)
add_test_case(event_loop_readable_event_on_subscribe_if_data_present)
add_test_case(event_loop_readable_event_on_2nd_time_readable)
add_test_case(event_loop_no_events_after_unsubscribe)
endif ()
endif()

add_test_case(event_loop_stop_then_restart)
add_test_case(event_loop_multiple_stops)
Expand All @@ -58,9 +59,10 @@ add_net_test_case(udp_socket_communication)
add_test_case(udp_bind_connect_communication)
add_net_test_case(connect_timeout)
add_net_test_case(connect_timeout_cancelation)
if (USE_VSOCK)
add_test_case(vsock_loopback_socket_communication)
endif ()

if(USE_VSOCK)
add_test_case(vsock_loopback_socket_communication)
endif()

add_test_case(outgoing_local_sock_errors)
add_test_case(outgoing_tcp_sock_error)
Expand All @@ -75,7 +77,7 @@ add_test_case(cleanup_in_accept_doesnt_explode)
add_test_case(cleanup_in_write_cb_doesnt_explode)
add_test_case(sock_write_cb_is_async)

if (WIN32)
if(WIN32)
add_test_case(local_socket_pipe_connected_race)
endif()

Expand Down Expand Up @@ -120,8 +122,8 @@ add_test_case(socket_handler_echo_and_backpressure)
add_test_case(socket_handler_close)
add_test_case(socket_pinned_event_loop)

if (NOT BYO_CRYPTO)
if (USE_S2N)
if(NOT BYO_CRYPTO)
if(USE_S2N)
add_net_test_case(default_pki_path_exists)
endif()

Expand All @@ -131,11 +133,11 @@ if (NOT BYO_CRYPTO)
#
# We don't use the interception suite since that's a host configuration issue
# We also don't check the domain security policy suite:
# 1. s2n does not support revocation checks and we do not currently enable any revocation checks
# in windows or osx, although we may add configurable support to those platforms(off-by-default) at a
# later date
# 2. s2n does not support public key pinning and, given its deprecated and http-centric position, there are no
# plans to add support nor investigate osx/windows support as well.
# 1. s2n does not support revocation checks and we do not currently enable any revocation checks
# in windows or osx, although we may add configurable support to those platforms(off-by-default) at a
# later date
# 2. s2n does not support public key pinning and, given its deprecated and http-centric position, there are no
# plans to add support nor investigate osx/windows support as well.

# Badssl - Certificate Validation endpoint suite
# For each failure case, we also include a positive test that verifies success when peer verification is disabled
Expand All @@ -161,13 +163,13 @@ if (NOT BYO_CRYPTO)

# Badssl - Legacy crypto suite, includes both negative and positive tests, with override checks where appropriate
# Our current baseline/default is platform-specific, whereas badssl expects a baseline of 1.2
# Linux - tls1.1
# Windows - system default (1.0 is the only thing we could reasonable fixate to given win7 support)
# Mac - system default
# Linux - tls1.1
# Windows - system default (1.0 is the only thing we could reasonable fixate to given win7 support)
# Mac - system default
# We skip the cbc and 3des checks, as a positive connection result there does not yet represent a security risk
# We don't include dh2048 as it succeeds on the windows baseline configuration and there does not seem
# to be a way to disable it
if(NOT (WIN32 AND NOT CMAKE_SYSTEM_VERSION MATCHES "10\.0\.1.*"))
if(NOT(WIN32 AND NOT CMAKE_SYSTEM_VERSION MATCHES "10\.0\.1.*"))
# Skip TLS 1.0 and TLS 1.1 test for windows later than windows server 2022, as they droped old TLS
add_net_test_case(tls_client_channel_negotiation_error_legacy_crypto_tls10)
add_net_test_case(tls_client_channel_negotiation_override_legacy_crypto_tls10)
Expand Down Expand Up @@ -197,6 +199,7 @@ if (NOT BYO_CRYPTO)
add_net_test_case(tls_client_channel_negotiation_success_rsa2048)
add_net_test_case(tls_client_channel_negotiation_success_ecc256)
add_net_test_case(tls_client_channel_negotiation_success_ecc384)

# add_net_test_case(tls_client_channel_negotiation_success_extended_validation) test disabled until badssl updates cert (expired 2022.08.10)
add_net_test_case(tls_client_channel_negotiation_success_mozilla_modern)

Expand Down Expand Up @@ -253,6 +256,7 @@ add_test_case(test_input_stream_file_seek_end)
add_test_case(test_input_stream_memory_length)
add_test_case(test_input_stream_file_length)
add_test_case(test_input_stream_binary)
add_test_case(test_input_stream_read_only)

add_test_case(async_input_stream_fill_completes_on_thread)
add_test_case(async_input_stream_fill_completes_immediately)
Expand All @@ -264,7 +268,7 @@ add_test_case(open_channel_statistics_test)

add_test_case(shared_library_open_failure)

if (BUILD_SHARED_LIBS)
if(BUILD_SHARED_LIBS)
add_test_case(shared_library_open_success)
add_test_case(shared_library_find_function_failure)
add_test_case(shared_library_find_function_success)
Expand All @@ -283,7 +287,7 @@ add_test_case(test_standard_retry_strategy_failure_exhausts_bucket)
add_test_case(test_standard_retry_strategy_failure_recovers)

# See PKCS11.md for instructions on running these tests
if (ENABLE_PKCS11_TESTS)
if(ENABLE_PKCS11_TESTS)
add_test_case(pkcs11_lib_sanity_check)
add_test_case(pkcs11_lib_behavior_default)
add_test_case(pkcs11_lib_behavior_omit_initialize)
Expand All @@ -306,7 +310,7 @@ if (ENABLE_PKCS11_TESTS)
add_test_case(pkcs11_login_tests)

# TLS with PKCS#11 not currently supported on every platform
if (USE_S2N)
if(USE_S2N)
add_test_case(pkcs11_tls_rsa_negotiation_succeeds)
add_test_case(pkcs11_tls_ec_negotiation_succeeds)
endif()
Expand All @@ -315,11 +319,11 @@ endif()
set(TEST_BINARY_NAME ${PROJECT_NAME}-tests)
generate_test_driver(${TEST_BINARY_NAME})

if (USE_S2N)
if(USE_S2N)
target_compile_definitions(${PROJECT_NAME}-tests PRIVATE "-DUSE_S2N")
endif()

#SSL certificates to use for testing.
# SSL certificates to use for testing.
add_custom_command(TARGET ${TEST_BINARY_NAME} PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/resources $<TARGET_FILE_DIR:${TEST_BINARY_NAME}>)
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/resources $<TARGET_FILE_DIR:${TEST_BINARY_NAME}>)
94 changes: 69 additions & 25 deletions tests/stream_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@
#include <aws/common/string.h>
#include <aws/io/stream.h>

#include <sys/stat.h>

#ifdef _WIN32
# include <io.h>
#endif

AWS_STATIC_STRING_FROM_LITERAL(s_simple_test, "SimpleTest");

/* 0x1A represents the Windows end-of-file character. Having this in the test data set allows us to verify that file
* stream reads on binary files do not terminate early on Windows.*/
const uint8_t s_simple_binary_test[] = {'a', 'b', 'c', 'd', 'e', 'f', 0x1A, 'g', 'h', 'i', 'j', 'k'};

const char *s_test_file_name = "stream.dat";

static struct aws_input_stream *s_create_memory_stream(struct aws_allocator *allocator) {
struct aws_byte_cursor test_cursor = aws_byte_cursor_from_string(s_simple_test);
return aws_input_stream_new_from_cursor(allocator, &test_cursor);
Expand All @@ -26,30 +30,48 @@ static void s_destroy_memory_stream(struct aws_input_stream *stream) {
aws_input_stream_destroy(stream);
}

static struct aws_input_stream *s_create_file_stream(struct aws_allocator *allocator) {
remove(s_test_file_name);
static struct aws_input_stream *s_create_file_stream(struct aws_allocator *allocator, const char *file_path) {
remove(file_path);

FILE *file = aws_fopen(s_test_file_name, "w+");
FILE *file = aws_fopen(file_path, "w");
fprintf(file, "%s", (char *)s_simple_test->bytes);
fclose(file);

return aws_input_stream_new_from_file(allocator, s_test_file_name);
return aws_input_stream_new_from_file(allocator, file_path);
}

static struct aws_input_stream *s_create_binary_file_stream(struct aws_allocator *allocator) {
remove(s_test_file_name);
static struct aws_input_stream *s_create_binary_file_stream(struct aws_allocator *allocator, const char *file_path) {
remove(file_path);

FILE *file = aws_fopen(s_test_file_name, "w+b");
FILE *file = aws_fopen(file_path, "wb");
fwrite(s_simple_binary_test, sizeof(uint8_t), sizeof(s_simple_binary_test), file);
fclose(file);

return aws_input_stream_new_from_file(allocator, s_test_file_name);
return aws_input_stream_new_from_file(allocator, file_path);
}

static void s_destroy_file_stream(struct aws_input_stream *stream) {
static struct aws_input_stream *s_create_read_only_file_stream(struct aws_allocator *allocator, const char *file_path) {
remove(file_path);

FILE *file = aws_fopen(file_path, "w");
fprintf(file, "%s", (char *)s_simple_test->bytes);
fclose(file);
#ifdef _WIN32
if (_chmod(file_path, _S_IREAD)) {
return NULL;
}
#else
if (chmod(file_path, S_IRUSR | S_IRGRP | S_IROTH)) {
return NULL;
}
#endif
return aws_input_stream_new_from_file(allocator, file_path);
}

static void s_destroy_file_stream(struct aws_input_stream *stream, const char *file_path) {
aws_input_stream_destroy(stream);

remove(s_test_file_name);
remove(file_path);
}

static int s_do_simple_input_stream_test(
Expand Down Expand Up @@ -125,12 +147,13 @@ AWS_TEST_CASE(test_input_stream_memory_iterate, s_test_input_stream_memory_itera
static int s_test_input_stream_file_simple(struct aws_allocator *allocator, void *ctx) {
(void)ctx;

struct aws_input_stream *stream = s_create_file_stream(allocator);
const char *file_path = "test_input_stream_file_simple.txt"; /* unique name */
struct aws_input_stream *stream = s_create_file_stream(allocator, file_path);

struct aws_byte_cursor test_cursor = aws_byte_cursor_from_string(s_simple_test);
ASSERT_TRUE(s_do_simple_input_stream_test(stream, allocator, 100, &test_cursor) == AWS_OP_SUCCESS);

s_destroy_file_stream(stream);
s_destroy_file_stream(stream, file_path);

return AWS_OP_SUCCESS;
}
Expand All @@ -140,12 +163,13 @@ AWS_TEST_CASE(test_input_stream_file_simple, s_test_input_stream_file_simple);
static int s_test_input_stream_file_iterate(struct aws_allocator *allocator, void *ctx) {
(void)ctx;

struct aws_input_stream *stream = s_create_file_stream(allocator);
const char *file_path = "test_input_stream_file_iterate.txt"; /* unique name */
struct aws_input_stream *stream = s_create_file_stream(allocator, file_path);

struct aws_byte_cursor test_cursor = aws_byte_cursor_from_string(s_simple_test);
ASSERT_TRUE(s_do_simple_input_stream_test(stream, allocator, 2, &test_cursor) == AWS_OP_SUCCESS);

s_destroy_file_stream(stream);
s_destroy_file_stream(stream, file_path);

return AWS_OP_SUCCESS;
}
Expand Down Expand Up @@ -196,15 +220,16 @@ AWS_TEST_CASE(test_input_stream_memory_seek_beginning, s_test_input_stream_memor
static int s_test_input_stream_file_seek_beginning(struct aws_allocator *allocator, void *ctx) {
(void)ctx;

struct aws_input_stream *stream = s_create_file_stream(allocator);
const char *file_path = "test_input_stream_file_seek_beginning.txt"; /* unique name */
struct aws_input_stream *stream = s_create_file_stream(allocator, file_path);

struct aws_byte_cursor test_cursor = aws_byte_cursor_from_string(s_simple_test);
aws_byte_cursor_advance(&test_cursor, SEEK_BEGINNING_OFFSET);
ASSERT_TRUE(
s_do_input_stream_seek_test(stream, allocator, SEEK_BEGINNING_OFFSET, AWS_SSB_BEGIN, &test_cursor) ==
AWS_OP_SUCCESS);

s_destroy_file_stream(stream);
s_destroy_file_stream(stream, file_path);

return AWS_OP_SUCCESS;
}
Expand Down Expand Up @@ -279,14 +304,15 @@ AWS_TEST_CASE(test_input_stream_memory_seek_multiple_times, s_test_input_stream_
static int s_test_input_stream_file_seek_end(struct aws_allocator *allocator, void *ctx) {
(void)ctx;

struct aws_input_stream *stream = s_create_file_stream(allocator);
const char *file_path = "test_input_stream_file_seek_end.txt"; /* unique name */
struct aws_input_stream *stream = s_create_file_stream(allocator, file_path);

struct aws_byte_cursor test_cursor = aws_byte_cursor_from_string(s_simple_test);
aws_byte_cursor_advance(&test_cursor, (size_t)((int64_t)s_simple_test->len + SEEK_END_OFFSET));
ASSERT_TRUE(
s_do_input_stream_seek_test(stream, allocator, SEEK_END_OFFSET, AWS_SSB_END, &test_cursor) == AWS_OP_SUCCESS);

s_destroy_file_stream(stream);
s_destroy_file_stream(stream, file_path);

return AWS_OP_SUCCESS;
}
Expand Down Expand Up @@ -360,8 +386,8 @@ AWS_TEST_CASE(test_input_stream_memory_length, s_test_input_stream_memory_length
static int s_test_input_stream_file_length(struct aws_allocator *allocator, void *ctx) {
(void)ctx;

struct aws_input_stream *stream = s_create_file_stream(allocator);

const char *file_path = "test_input_stream_file_length.txt"; /* unique name */
struct aws_input_stream *stream = s_create_file_stream(allocator, file_path);
int64_t length = 0;
ASSERT_TRUE(aws_input_stream_get_length(stream, &length) == AWS_OP_SUCCESS);
ASSERT_TRUE(length == (int64_t)s_simple_test->len);
Expand All @@ -372,7 +398,7 @@ static int s_test_input_stream_file_length(struct aws_allocator *allocator, void
ASSERT_TRUE(aws_input_stream_get_length(stream, &length) == AWS_OP_SUCCESS);
ASSERT_TRUE(length == (int64_t)s_simple_test->len);

s_destroy_file_stream(stream);
s_destroy_file_stream(stream, file_path);

return AWS_OP_SUCCESS;
}
Expand All @@ -382,7 +408,8 @@ AWS_TEST_CASE(test_input_stream_file_length, s_test_input_stream_file_length);
static int s_test_input_stream_binary(struct aws_allocator *allocator, void *ctx) {
(void)ctx;

struct aws_input_stream *stream = s_create_binary_file_stream(allocator);
const char *file_path = "test_input_stream_binary.dat"; /* unique name */
struct aws_input_stream *stream = s_create_binary_file_stream(allocator, file_path);

struct aws_byte_cursor test_cursor = {
.ptr = (uint8_t *)s_simple_binary_test,
Expand All @@ -391,9 +418,26 @@ static int s_test_input_stream_binary(struct aws_allocator *allocator, void *ctx

ASSERT_TRUE(s_do_simple_input_stream_test(stream, allocator, 100, &test_cursor) == AWS_OP_SUCCESS);

s_destroy_file_stream(stream);
s_destroy_file_stream(stream, file_path);

return AWS_OP_SUCCESS;
}

AWS_TEST_CASE(test_input_stream_binary, s_test_input_stream_binary);

static int s_test_input_stream_read_only(struct aws_allocator *allocator, void *ctx) {
(void)ctx;

const char *file_path = "test_input_stream_read_only.txt"; /* unique name */
struct aws_input_stream *stream = s_create_read_only_file_stream(allocator, file_path);
ASSERT_NOT_NULL(stream);

struct aws_byte_cursor test_cursor = aws_byte_cursor_from_string(s_simple_test);
ASSERT_TRUE(s_do_simple_input_stream_test(stream, allocator, 100, &test_cursor) == AWS_OP_SUCCESS);

s_destroy_file_stream(stream, file_path);

return AWS_OP_SUCCESS;
}

AWS_TEST_CASE(test_input_stream_read_only, s_test_input_stream_read_only);

0 comments on commit e87baa2

Please sign in to comment.