Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package aws-c-http for openSUSE:Factory checked in at 2025-04-04 17:34:15 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/aws-c-http (Old) and /work/SRC/openSUSE:Factory/.aws-c-http.new.1907 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "aws-c-http" Fri Apr 4 17:34:15 2025 rev:15 rq:1267109 version:0.9.6 Changes: -------- --- /work/SRC/openSUSE:Factory/aws-c-http/aws-c-http.changes 2025-03-26 21:25:17.871180417 +0100 +++ /work/SRC/openSUSE:Factory/.aws-c-http.new.1907/aws-c-http.changes 2025-04-04 17:34:19.401041944 +0200 @@ -1,0 +2,8 @@ +Wed Apr 2 14:55:22 UTC 2025 - John Paul Adrian Glaubitz <adrian.glaub...@suse.com> + +- Update to 0.9.6 + * Fix Error Handling For Connection Manager by @waahm7 in (#507) + * h1_decoder error on multiple content-length headers by @quinnj in (#509) + * Apple Network Framework Support by @xiazhvera in (#502) + +------------------------------------------------------------------- Old: ---- v0.9.5.tar.gz New: ---- v0.9.6.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ aws-c-http.spec ++++++ --- /var/tmp/diff_new_pack.FYzwoX/_old 2025-04-04 17:34:20.821101255 +0200 +++ /var/tmp/diff_new_pack.FYzwoX/_new 2025-04-04 17:34:20.821101255 +0200 @@ -19,7 +19,7 @@ %define library_version 1.0.0 %define library_soversion 1_0_0 Name: aws-c-http -Version: 0.9.5 +Version: 0.9.6 Release: 0 Summary: C99 implementation of the HTTP/1.1 and HTTP/2 specifications License: Apache-2.0 ++++++ v0.9.5.tar.gz -> v0.9.6.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-http-0.9.5/.github/workflows/ci.yml new/aws-c-http-0.9.6/.github/workflows/ci.yml --- old/aws-c-http-0.9.5/.github/workflows/ci.yml 2025-03-14 21:12:18.000000000 +0100 +++ new/aws-c-http-0.9.6/.github/workflows/ci.yml 2025-03-28 22:24:03.000000000 +0100 @@ -204,7 +204,11 @@ python .\aws-c-http\build\deps\aws-c-common\scripts\appverifier_ctest.py --build_directory .\aws-c-http\build\aws-c-http macos: - runs-on: macos-14 # latest + runs-on: macos-14 + strategy: + fail-fast: false + matrix: + eventloop: ["kqueue", "dispatch_queue"] steps: - uses: aws-actions/configure-aws-credentials@v4 with: @@ -214,10 +218,10 @@ run: | python3 -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder')" chmod a+x builder - ./builder build -p ${{ env.PACKAGE_NAME }} + ./builder build -p ${{ env.PACKAGE_NAME }} --cmake-extra=-DAWS_USE_APPLE_NETWORK_FRAMEWORK=${{ matrix.eventloop == 'dispatch_queue' && 'ON' || 'OFF' }} macos-x64: - runs-on: macos-14-large # latest + runs-on: macos-14-large steps: - uses: aws-actions/configure-aws-credentials@v4 with: @@ -244,7 +248,11 @@ python3 builder.pyz build -p aws-c-http --cmake-extra=-DENABLE_LOCALHOST_INTEGRATION_TESTS=ON --config Debug localhost-test-macos: - runs-on: macos-14 # latest + runs-on: macos-14 + strategy: + fail-fast: false + matrix: + eventloop: ["kqueue", "dispatch_queue"] steps: - uses: aws-actions/configure-aws-credentials@v4 with: @@ -257,7 +265,7 @@ python3 -m venv .venv source .venv/bin/activate python3 -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder.pyz')" - python3 builder.pyz build -p aws-c-http --cmake-extra=-DENABLE_LOCALHOST_INTEGRATION_TESTS=ON --config Debug + python3 builder.pyz build -p aws-c-http --cmake-extra=-DENABLE_LOCALHOST_INTEGRATION_TESTS=ON --cmake-extra=-DAWS_USE_APPLE_NETWORK_FRAMEWORK=${{ matrix.eventloop == 'dispatch_queue' && 'ON' || 'OFF' }} --config Debug localhost-test-win: runs-on: windows-2022 # latest diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-http-0.9.5/source/connection.c new/aws-c-http-0.9.6/source/connection.c --- old/aws-c-http-0.9.5/source/connection.c 2025-03-14 21:12:18.000000000 +0100 +++ new/aws-c-http-0.9.6/source/connection.c 2025-03-28 22:24:03.000000000 +0100 @@ -16,6 +16,7 @@ #include <aws/common/string.h> #include <aws/http/request_response.h> #include <aws/io/channel_bootstrap.h> +#include <aws/io/future.h> #include <aws/io/logging.h> #include <aws/io/socket.h> #include <aws/io/socket_channel_handler.h> @@ -58,6 +59,7 @@ aws_http_server_on_incoming_connection_fn *on_incoming_connection; aws_http_server_on_destroy_fn *on_destroy_complete; struct aws_socket *socket; + struct aws_future_void *setup_future; /* Any thread may touch this data, but the lock must be held */ struct { @@ -576,6 +578,7 @@ } aws_hash_table_clean_up(&server->synced_data.channel_to_connection_map); aws_mutex_clean_up(&server->synced_data.lock); + aws_future_void_release(server->setup_future); aws_mem_release(server->alloc, server); } @@ -621,6 +624,20 @@ s_http_server_clean_up(server); } +/* the server listener has finished setup. We released the socket if error_code is not 0. */ +static void s_server_bootstrap_on_server_listener_setup( + struct aws_server_bootstrap *bootstrap, + int error_code, + void *user_data) { + (void)bootstrap; + struct aws_http_server *server = user_data; + if (error_code) { + aws_future_void_set_error(server->setup_future, error_code); + } else { + aws_future_void_set_result(server->setup_future); + } +} + struct aws_http_server *aws_http_server_new(const struct aws_http_server_options *options) { aws_http_fatal_assert_library_initialized(); @@ -654,7 +671,7 @@ if (err) { AWS_LOGF_ERROR( AWS_LS_HTTP_SERVER, "static: Failed to initialize mutex, error %d (%s).", err, aws_error_name(err)); - goto mutex_error; + goto server_error; } err = aws_hash_table_init( &server->synced_data.channel_to_connection_map, server->alloc, 16, aws_hash_ptr, aws_ptr_eq, NULL, NULL); @@ -664,7 +681,7 @@ "static: Cannot create server, error %d (%s).", aws_last_error(), aws_error_name(aws_last_error())); - goto hash_table_error; + goto server_error; } /* Protect against callbacks firing before server->socket is set */ s_server_lock_synced_data(server); @@ -672,6 +689,8 @@ server->is_using_tls = true; } + server->setup_future = aws_future_void_new(options->allocator); + struct aws_server_socket_channel_bootstrap_options bootstrap_options = { .enable_read_back_pressure = options->manual_window_management, .tls_options = options->tls_options, @@ -680,23 +699,38 @@ .incoming_callback = s_server_bootstrap_on_accept_channel_setup, .shutdown_callback = s_server_bootstrap_on_accept_channel_shutdown, .destroy_callback = s_server_bootstrap_on_server_listener_destroy, + .setup_callback = s_server_bootstrap_on_server_listener_setup, .host_name = options->endpoint->address, .port = options->endpoint->port, .user_data = server, }; - server->socket = aws_server_bootstrap_new_socket_listener(&bootstrap_options); + int listen_error = AWS_OP_SUCCESS; + /* + * WARNING & TODO!!!! + * aws_server_bootstrap_new_socket_listener has async callback, we would block here waiting for + * setup complete. aws-c-http library need to be updated with a proper async API. + */ + server->socket = aws_server_bootstrap_new_socket_listener(&bootstrap_options); + // if server setup properly, waiting for setup callback + if (server->socket) { + aws_future_void_wait(server->setup_future, UINT64_MAX /*timeout*/); + listen_error = aws_future_void_get_error(server->setup_future); + } else { + listen_error = aws_last_error(); + } s_server_unlock_synced_data(server); - if (!server->socket) { + if (listen_error) { AWS_LOGF_ERROR( AWS_LS_HTTP_SERVER, "static: Failed creating new socket listener, error %d (%s). Cannot create server.", - aws_last_error(), - aws_error_name(aws_last_error())); + listen_error, + aws_error_name(listen_error)); - goto socket_error; + aws_raise_error(listen_error); + goto server_error; } AWS_LOGF_INFO( @@ -708,12 +742,8 @@ return server; -socket_error: - aws_hash_table_clean_up(&server->synced_data.channel_to_connection_map); -hash_table_error: - aws_mutex_clean_up(&server->synced_data.lock); -mutex_error: - aws_mem_release(server->alloc, server); +server_error: + s_http_server_clean_up(server); return NULL; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-http-0.9.5/source/connection_manager.c new/aws-c-http-0.9.6/source/connection_manager.c --- old/aws-c-http-0.9.5/source/connection_manager.c 2025-03-14 21:12:18.000000000 +0100 +++ new/aws-c-http-0.9.6/source/connection_manager.c 2025-03-28 22:24:03.000000000 +0100 @@ -1164,7 +1164,6 @@ struct aws_http_connection_manager *manager = work->manager; - int representative_error = 0; size_t new_connection_failures = 0; /* @@ -1202,8 +1201,6 @@ */ struct aws_array_list errors; AWS_ZERO_STRUCT(errors); - /* Even if we can't init this array, we still need to invoke error callbacks properly */ - bool push_errors = false; if (work->new_connections > 0) { AWS_LOGF_INFO( @@ -1211,20 +1208,21 @@ "id=%p: Requesting %zu new connections from http", (void *)manager, work->new_connections); - push_errors = aws_array_list_init_dynamic(&errors, work->allocator, work->new_connections, sizeof(int)) == - AWS_ERROR_SUCCESS; + AWS_FATAL_ASSERT( + aws_array_list_init_dynamic(&errors, work->allocator, work->new_connections, sizeof(int)) == + AWS_OP_SUCCESS); } for (size_t i = 0; i < work->new_connections; ++i) { if (s_aws_http_connection_manager_new_connection(manager)) { ++new_connection_failures; - representative_error = aws_last_error(); - if (push_errors) { - AWS_FATAL_ASSERT(aws_array_list_push_back(&errors, &representative_error) == AWS_OP_SUCCESS); - } + int error = aws_last_error(); + AWS_FATAL_ASSERT(aws_array_list_push_back(&errors, &error) == AWS_OP_SUCCESS); } } + bool has_pending_acquisitions = false; + struct aws_connection_management_transaction pending_acquisitions_work; if (new_connection_failures > 0) { /* * We failed and aren't going to receive a callback, but the current state assumes we will receive @@ -1235,30 +1233,24 @@ AWS_FATAL_ASSERT(manager->internal_ref[AWS_HCMCT_PENDING_CONNECTIONS] >= new_connection_failures); s_connection_manager_internal_ref_decrease(manager, AWS_HCMCT_PENDING_CONNECTIONS, new_connection_failures); - /* - * Rather than failing one acquisition for each connection failure, if there's at least one - * connection failure, we instead fail all excess acquisitions, since there's no pending - * connect that will necessarily resolve them. - * - * Try to correspond an error with the acquisition failure, but as a fallback just use the - * representative error. - */ - size_t i = 0; - while (manager->pending_acquisition_count > manager->internal_ref[AWS_HCMCT_PENDING_CONNECTIONS]) { - int error = representative_error; - if (i < aws_array_list_length(&errors)) { - aws_array_list_get_at(&errors, &error, i); - } - + for (size_t i = 0; i < new_connection_failures && manager->pending_acquisition_count > 0; i++) { + int error; + aws_array_list_get_at(&errors, &error, i); AWS_LOGF_DEBUG( AWS_LS_HTTP_CONNECTION_MANAGER, - "id=%p: Failing excess connection acquisition with error code %d", + "id=%p: Failing connection acquisition with error code %d", (void *)manager, (int)error); s_aws_http_connection_manager_move_front_acquisition(manager, NULL, error, &work->completions); - ++i; } - + has_pending_acquisitions = + manager->internal_ref[AWS_HCMCT_PENDING_CONNECTIONS] + manager->pending_settings_count < + manager->pending_acquisition_count; + if (has_pending_acquisitions) { + /* If there are pending acquisitions, schedule work again to try acquiring them */ + s_aws_connection_management_transaction_init(&pending_acquisitions_work, manager); + s_aws_http_connection_manager_build_transaction(&pending_acquisitions_work); + } aws_mutex_unlock(&manager->lock); } @@ -1266,13 +1258,20 @@ * Step 4 - Perform acquisition callbacks */ s_aws_http_connection_manager_complete_acquisitions(&work->completions, work->allocator); - aws_array_list_clean_up(&errors); /* * Step 5 - Clean up work. Do this here rather than at the end of every caller. Destroy the manager if necessary */ s_aws_connection_management_transaction_clean_up(work); + + /* + * Step 6 - If some connection acquisition requests failed and we still have more pending acquisitions, try to + * acquire them. + */ + if (has_pending_acquisitions) { + s_aws_http_connection_manager_execute_transaction(&pending_acquisitions_work); + } } void aws_http_connection_manager_acquire_connection( @@ -1479,12 +1478,11 @@ work->connection_to_release = connection; } } else { - /* fail acquisition as one connection cannot be used any more */ - while (manager->pending_acquisition_count > - manager->internal_ref[AWS_HCMCT_PENDING_CONNECTIONS] + manager->pending_settings_count) { + if (manager->pending_acquisition_count > 0) { + /* fail acquisition as connection acquire failed */ AWS_LOGF_DEBUG( AWS_LS_HTTP_CONNECTION_MANAGER, - "id=%p: Failing excess connection acquisition with error code %d", + "id=%p: Failing connection acquisition with error code %d", (void *)manager, (int)error_code); s_aws_http_connection_manager_move_front_acquisition(manager, NULL, error_code, &work->completions); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-http-0.9.5/source/h1_decoder.c new/aws-c-http-0.9.6/source/h1_decoder.c --- old/aws-c-http-0.9.5/source/h1_decoder.c 2025-03-14 21:12:18.000000000 +0100 +++ new/aws-c-http-0.9.6/source/h1_decoder.c 2025-03-28 22:24:03.000000000 +0100 @@ -39,6 +39,8 @@ bool is_done; bool body_headers_ignored; bool body_headers_forbidden; + bool content_length_received; + enum aws_http_header_block header_block; const void *logging_id; @@ -214,6 +216,7 @@ decoder->is_done = false; decoder->body_headers_ignored = false; decoder->body_headers_forbidden = false; + decoder->content_length_received = false; /* set to normal by default */ decoder->header_block = AWS_HTTP_HEADER_BLOCK_MAIN; } @@ -411,6 +414,14 @@ switch (header.name) { case AWS_HTTP_HEADER_CONTENT_LENGTH: + if (decoder->content_length_received) { + AWS_LOGF_ERROR( + AWS_LS_HTTP_STREAM, + "id=%p: Multiple incoming headers for content-length received. This is illegal.", + decoder->logging_id); + return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR); + } + if (decoder->transfer_encoding) { AWS_LOGF_ERROR( AWS_LS_HTTP_STREAM, @@ -441,10 +452,11 @@ return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR); } + decoder->content_length_received = true; break; case AWS_HTTP_HEADER_TRANSFER_ENCODING: { - if (decoder->content_length) { + if (decoder->content_length_received) { AWS_LOGF_ERROR( AWS_LS_HTTP_STREAM, "id=%p: Incoming headers for both content-length and transfer-encoding received. This is illegal.", @@ -469,7 +481,23 @@ AWS_ZERO_STRUCT(split); while (aws_byte_cursor_next_split(&header.value_data, ',', &split)) { struct aws_byte_cursor coding = aws_strutil_trim_http_whitespace(split); - int prev_flags = decoder->transfer_encoding; + + /* A sender MUST NOT apply chunked more than once to a message body. + * If any transfer coding other than chunked is applied to a request payload body, the sender MUST + * apply chunked as the final transfer coding to ensure that the message is properly framed. + * RFC-7230 3.3.1 */ + if (decoder->transfer_encoding & AWS_HTTP_TRANSFER_ENCODING_CHUNKED) { + AWS_LOGF_ERROR( + AWS_LS_HTTP_STREAM, + "id=%p: Incoming transfer-encoding header lists a coding after 'chunked', this is illegal.", + decoder->logging_id); + AWS_LOGF_DEBUG( + AWS_LS_HTTP_STREAM, + "id=%p: Misplaced coding is '" PRInSTR "'", + decoder->logging_id, + AWS_BYTE_CURSOR_PRI(coding)); + return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR); + } if (aws_string_eq_byte_cursor_ignore_case(s_transfer_coding_chunked, &coding)) { decoder->transfer_encoding |= AWS_HTTP_TRANSFER_ENCODING_CHUNKED; @@ -500,21 +528,11 @@ decoder->logging_id, AWS_BYTE_CURSOR_PRI(coding)); return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR); - } - - /* If any transfer coding other than chunked is applied to a request payload body, the sender MUST - * apply chunked as the final transfer coding to ensure that the message is properly framed. - * RFC-7230 3.3.1 */ - if ((prev_flags & AWS_HTTP_TRANSFER_ENCODING_CHUNKED) && (decoder->transfer_encoding != prev_flags)) { + } else { AWS_LOGF_ERROR( AWS_LS_HTTP_STREAM, - "id=%p: Incoming transfer-encoding header lists a coding after 'chunked', this is illegal.", + "id=%p: Incoming transfer-encoding header has invalid blank entry.", decoder->logging_id); - AWS_LOGF_DEBUG( - AWS_LS_HTTP_STREAM, - "id=%p: Misplaced coding is '" PRInSTR "'", - decoder->logging_id, - AWS_BYTE_CURSOR_PRI(coding)); return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-http-0.9.5/tests/CMakeLists.txt new/aws-c-http-0.9.6/tests/CMakeLists.txt --- old/aws-c-http-0.9.5/tests/CMakeLists.txt 2025-03-14 21:12:18.000000000 +0100 +++ new/aws-c-http-0.9.6/tests/CMakeLists.txt 2025-03-28 22:24:03.000000000 +0100 @@ -529,6 +529,8 @@ add_net_test_case(connection_manager_acquire_release_mix_synchronous) add_net_test_case(connection_manager_acquisition_timeout) add_net_test_case(connection_manager_connect_callback_failure) +add_net_test_case(test_connection_manager_connect_callback_async_failure) +add_net_test_case(test_connection_manager_connect_callback_async_with_immediate_failure) add_net_test_case(connection_manager_connect_immediate_failure) add_net_test_case(connection_manager_proxy_setup_shutdown) add_net_test_case(connection_manager_idle_culling_single) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-http-0.9.5/tests/test_connection_manager.c new/aws-c-http-0.9.6/tests/test_connection_manager.c --- old/aws-c-http-0.9.5/tests/test_connection_manager.c 2025-03-14 21:12:18.000000000 +0100 +++ new/aws-c-http-0.9.6/tests/test_connection_manager.c 2025-03-28 22:24:03.000000000 +0100 @@ -819,7 +819,7 @@ connection_manager_max_pending_acquisitions_with_vended_connections, s_test_connection_manager_max_pending_acquisitions_with_vended_connections); -static int s_aws_http_connection_manager_create_connection_sync_mock( +static int s_aws_http_connection_manager_create_connection_validate( const struct aws_http_client_connection_options *options) { struct cm_tester *tester = &s_tester; @@ -830,8 +830,6 @@ ASSERT_TRUE(aws_byte_cursor_eq_c_str(&interface_name, options->socket_options->network_interface_name)); } - size_t next_connection_id = aws_atomic_fetch_add(&tester->next_connection_id, 1); - ASSERT_SUCCESS(aws_mutex_lock(&tester->lock)); tester->release_connection_fn = options->on_shutdown; ASSERT_SUCCESS(aws_mutex_unlock(&tester->lock)); @@ -847,7 +845,15 @@ ASSERT_UINT_EQUALS(options->proxy_options->connection_type, tester->verify_proxy_options->connection_type); } + return AWS_OP_SUCCESS; +} +static int s_aws_http_connection_manager_create_connection_sync_mock( + const struct aws_http_client_connection_options *options) { + s_aws_http_connection_manager_create_connection_validate(options); + struct cm_tester *tester = &s_tester; + struct mock_connection *connection = NULL; + size_t next_connection_id = aws_atomic_fetch_add(&tester->next_connection_id, 1); if (next_connection_id < aws_array_list_length(&tester->mock_connections)) { aws_array_list_get_at(&tester->mock_connections, &connection, next_connection_id); @@ -868,6 +874,63 @@ return aws_raise_error(AWS_ERROR_HTTP_UNKNOWN); } +struct connect_task_args { + void *user_data; + struct mock_connection *connection; + aws_http_on_client_connection_setup_fn *on_setup; +}; + +static void s_aws_http_connection_manager_connect_task( + struct aws_task *task, + void *user_data, + enum aws_task_status status) { + (void)status; + struct cm_tester *tester = &s_tester; + + struct connect_task_args *task_args = user_data; + struct mock_connection *connection = task_args->connection; + if (connection) { + if (connection->result == AWS_NCRT_SUCCESS) { + task_args->on_setup((struct aws_http_connection *)connection, AWS_ERROR_SUCCESS, task_args->user_data); + } else if (connection->result == AWS_NCRT_ERROR_VIA_CALLBACK) { + task_args->on_setup(NULL, AWS_ERROR_HTTP_UNKNOWN, task_args->user_data); + } else { + AWS_FATAL_ASSERT(0 && "Unexpected connection->result"); + } + } + + aws_mem_release(tester->allocator, task); + aws_mem_release(tester->allocator, task_args); +} + +static int s_aws_http_connection_manager_connect_async_mock(const struct aws_http_client_connection_options *options) { + s_aws_http_connection_manager_create_connection_validate(options); + struct cm_tester *tester = &s_tester; + + struct mock_connection *connection = NULL; + size_t next_connection_id = aws_atomic_fetch_add(&tester->next_connection_id, 1); + if (next_connection_id < aws_array_list_length(&tester->mock_connections)) { + aws_array_list_get_at(&tester->mock_connections, &connection, next_connection_id); + } + + if (connection->result == AWS_NCRT_ERROR_FROM_CREATE) { + return aws_raise_error(AWS_ERROR_HTTP_UNKNOWN); + } + + struct aws_task *task = aws_mem_calloc(options->allocator, 1, sizeof(struct aws_task)); + struct connect_task_args *task_args = aws_mem_calloc(options->allocator, 1, sizeof(struct connect_task_args)); + task_args->connection = connection; + task_args->user_data = options->user_data; + task_args->on_setup = options->on_setup; + aws_task_init(task, s_aws_http_connection_manager_connect_task, task_args, "create_connection_task"); + + struct aws_event_loop *event_loop = aws_event_loop_group_get_next_loop(tester->event_loop_group); + uint64_t now; + ASSERT_SUCCESS(aws_event_loop_current_clock_time(event_loop, &now)); + aws_event_loop_schedule_task_future(event_loop, task, now + 1000000000); + return AWS_OP_SUCCESS; +} + static void s_aws_http_connection_manager_release_connection_sync_mock(struct aws_http_connection *connection) { (void)connection; @@ -920,6 +983,17 @@ .aws_http_connection_get_version = s_aws_http_connection_manager_connection_get_version_sync_mock, }; +static struct aws_http_connection_manager_system_vtable s_async_connect_mock = { + .aws_http_client_connect = s_aws_http_connection_manager_connect_async_mock, + .aws_http_connection_release = s_aws_http_connection_manager_release_connection_sync_mock, + .aws_http_connection_close = s_aws_http_connection_manager_close_connection_sync_mock, + .aws_http_connection_new_requests_allowed = s_aws_http_connection_manager_is_connection_available_sync_mock, + .aws_high_res_clock_get_ticks = aws_high_res_clock_get_ticks, + .aws_http_connection_get_channel = s_aws_http_connection_manager_connection_get_channel_sync_mock, + .aws_channel_thread_is_callers_thread = s_aws_http_connection_manager_is_callers_thread_sync_mock, + .aws_http_connection_get_version = s_aws_http_connection_manager_connection_get_version_sync_mock, +}; + static int s_test_connection_manager_with_network_interface_list(struct aws_allocator *allocator, void *ctx) { (void)ctx; struct aws_byte_cursor *interface_names_array = aws_mem_calloc(allocator, 3, sizeof(struct aws_byte_cursor)); @@ -1055,6 +1129,68 @@ } AWS_TEST_CASE(connection_manager_connect_callback_failure, s_test_connection_manager_connect_callback_failure); +static int s_test_connection_manager_connect_callback_async_failure(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + int error_connections = 5; + int success_connections = 5; + struct cm_tester_options options = { + .allocator = allocator, + .max_connections = error_connections, + .mock_table = &s_async_connect_mock, + }; + + ASSERT_SUCCESS(s_cm_tester_init(&options)); + + s_add_mock_connections(error_connections, AWS_NCRT_ERROR_VIA_CALLBACK, false); + s_add_mock_connections(success_connections, AWS_NCRT_SUCCESS, true); + + s_acquire_connections(error_connections + success_connections); + + ASSERT_SUCCESS(s_wait_on_connection_reply_count(error_connections + success_connections)); + + ASSERT_UINT_EQUALS(s_tester.connection_errors, error_connections); + ASSERT_SUCCESS(s_release_connections(success_connections, false)); + + ASSERT_SUCCESS(s_cm_tester_clean_up()); + + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE( + test_connection_manager_connect_callback_async_failure, + s_test_connection_manager_connect_callback_async_failure); + +static int s_test_connection_manager_connect_callback_async_with_immediate_failure( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + int error_connections = 5; + int success_connections = 5; + struct cm_tester_options options = { + .allocator = allocator, + .max_connections = error_connections + success_connections, + .mock_table = &s_async_connect_mock, + }; + + ASSERT_SUCCESS(s_cm_tester_init(&options)); + + s_add_mock_connections(success_connections, AWS_NCRT_SUCCESS, true); + s_add_mock_connections(error_connections, AWS_NCRT_ERROR_VIA_CALLBACK, false); + + s_acquire_connections(error_connections + success_connections); + + ASSERT_SUCCESS(s_wait_on_connection_reply_count(error_connections + success_connections)); + + ASSERT_UINT_EQUALS(s_tester.connection_errors, error_connections); + ASSERT_SUCCESS(s_release_connections(success_connections, false)); + + ASSERT_SUCCESS(s_cm_tester_clean_up()); + + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE( + test_connection_manager_connect_callback_async_with_immediate_failure, + s_test_connection_manager_connect_callback_async_with_immediate_failure); + static int s_test_connection_manager_connect_immediate_failure(struct aws_allocator *allocator, void *ctx) { (void)ctx; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-http-0.9.5/tests/test_h1_decoder.c new/aws-c-http-0.9.6/tests/test_h1_decoder.c --- old/aws-c-http-0.9.5/tests/test_h1_decoder.c 2025-03-14 21:12:18.000000000 +0100 +++ new/aws-c-http-0.9.6/tests/test_h1_decoder.c 2025-03-28 22:24:03.000000000 +0100 @@ -340,18 +340,13 @@ struct aws_byte_cursor msg = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("HTTP/1.1 200 OK\r\n" "Server: some-server\r\n" "Transfer-Encoding: compress\r\n" - "Transfer-Encoding: gzip, ,deflate\r\n" + "Transfer-Encoding: gzip,deflate\r\n" "Transfer-Encoding: chunked\r\n" - "Transfer-Encoding:\r\n" - "\r\n" - "Hello noob."); + "\r\n"); struct aws_h1_decoder_params params; s_common_decoder_setup(allocator, 1024, ¶ms, s_response, NULL); struct aws_h1_decoder *decoder = aws_h1_decoder_new(¶ms); - /* Not a valid HTTP1.1 message, but not the job of decoder to return error here. */ - /* Instead, the user should know their buffer has been processed without returning any body data, and - * report the error in user-space. */ ASSERT_SUCCESS(aws_h1_decode(decoder, &msg)); int flags = aws_h1_decoder_get_encoding_flags(decoder); ASSERT_INT_EQUALS( @@ -614,6 +609,17 @@ "Transfer-Encoding: gzip\r\n" "\r\n"), + /* A sender MUST NOT apply chunked more than once to a message body */ + AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("GET / HTTP/1.1\r\n" + "Transfer-Encoding: chunked, chunked\r\n" + "\r\n"), + + /* A sender MUST NOT apply chunked more than once to a message body, p2 */ + AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("GET / HTTP/1.1\r\n" + "Transfer-Encoding: chunked,\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n"), + /* Invalid hex-int as chunk size. */ AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("GET / HTTP/1.1\r\n" "Transfer-Encoding: chunked\r\n" @@ -651,6 +657,28 @@ AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("GET / HTTP/1.1\r\n" "Transfer-Encoding: shrinkydinky, chunked\r\n"), + /* Transfer coding cannot be blank (empty header value) */ + AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("GET / HTTP/1.1\r\n" + "Transfer-Encoding: \r\n" + "Transfer-Encoding: chunked\r\n"), + + /* Transfer coding cannot be blank (empty item in list) */ + AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("GET / HTTP/1.1\r\n" + "Transfer-Encoding: gzip, ,chunked\r\n"), + + /* Transfer coding cannot be blank (empty item at start of list) */ + AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("GET / HTTP/1.1\r\n" + "Transfer-Encoding: ,chunked\r\n"), + + /* Transfer coding cannot be blank (empty item at end of list) */ + AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("GET / HTTP/1.1\r\n" + "Transfer-Encoding: chunked,\r\n"), + + /* Transfer coding cannot be blank (empty header value) */ + AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("GET / HTTP/1.1\r\n" + "Transfer-Encoding: \r\n" + "Transfer-Encoding: chunked\r\n"), + /* My chunk size is too big */ AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("GET / HTTP/1.1\r\n" "Transfer-Encoding: chunked\r\n" @@ -665,11 +693,21 @@ AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("POST / HTTP/1.1\r\n" "Content-Length:\r\n"), - /* Has both content-Length and transfer-encoding */ + /* Has both content-length and transfer-encoding */ AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("POST / HTTP/1.1\r\n" - "Content-Length: 999\r\n" + "Content-Length: 0\r\n" "Transfer-Encoding: chunked\r\n"), + /* Has both transfer-encoding and content-length (but with transfer-encoding first this time) */ + AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("POST / HTTP/1.1\r\n" + "Transfer-Encoding: chunked\r\n" + "Content-Length: 0\r\n"), + + /* Multiple content-length headers */ + AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("POST / HTTP/1.1\r\n" + "Content-Length: 0\r\n" + "Content-Length: 0\r\n" + "\r\n"), /* Header is missing colon */ AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("GET / HTTP/1.1\r\n" "Header-Missing-Colon yes it is\r\n" @@ -744,12 +782,16 @@ s_common_decoder_setup(allocator, 1024, ¶ms, s_request, NULL); struct aws_h1_decoder *decoder = aws_h1_decoder_new(¶ms); + aws_reset_error(); + ASSERT_FAILS( aws_h1_decode(decoder, &request), "Entry [%zu] should have failed, but it passed:\n------\n" PRInSTR "\n------\n", iter, AWS_BYTE_CURSOR_PRI(requests[iter])); + ASSERT_INT_EQUALS(AWS_ERROR_HTTP_PROTOCOL_ERROR, aws_last_error()); + aws_h1_decoder_destroy(decoder); }