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-07-23 16:35:33 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/aws-c-http (Old) and /work/SRC/openSUSE:Factory/.aws-c-http.new.8875 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "aws-c-http" Wed Jul 23 16:35:33 2025 rev:19 rq:1295231 version:0.10.4 Changes: -------- --- /work/SRC/openSUSE:Factory/aws-c-http/aws-c-http.changes 2025-06-13 18:47:25.451686827 +0200 +++ /work/SRC/openSUSE:Factory/.aws-c-http.new.8875/aws-c-http.changes 2025-07-23 16:39:12.273611009 +0200 @@ -1,0 +2,9 @@ +Tue Jul 22 08:32:29 UTC 2025 - John Paul Adrian Glaubitz <adrian.glaub...@suse.com> + +- Update to 0.10.4 + * [fix] failed to compile on FreeBSD by @TingDaoK in (#527) +- from version 0.10.3 + * Remove Windows 2019 and add Windows 2025 with MSVC-17 by @TingDaoK in (#521) + * Support no_proxy exactly like CURL by @TingDaoK in (#522) + +------------------------------------------------------------------- Old: ---- v0.10.2.tar.gz New: ---- v0.10.4.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ aws-c-http.spec ++++++ --- /var/tmp/diff_new_pack.FKWklw/_old 2025-07-23 16:39:12.905636355 +0200 +++ /var/tmp/diff_new_pack.FKWklw/_new 2025-07-23 16:39:12.909636515 +0200 @@ -20,7 +20,7 @@ %define library_version 1.0.0 %define library_soversion 1_0_0 Name: aws-c-http -Version: 0.10.2 +Version: 0.10.4 Release: 0 Summary: C99 implementation of the HTTP/1.1 and HTTP/2 specifications License: Apache-2.0 ++++++ v0.10.2.tar.gz -> v0.10.4.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-http-0.10.2/.github/workflows/ci.yml new/aws-c-http-0.10.4/.github/workflows/ci.yml --- old/aws-c-http-0.10.2/.github/workflows/ci.yml 2025-06-09 18:12:18.000000000 +0200 +++ new/aws-c-http-0.10.4/.github/workflows/ci.yml 2025-07-21 22:04:12.000000000 +0200 @@ -149,7 +149,7 @@ ./linux-container-ci.sh ${{ env.BUILDER_VERSION }} aws-crt-${{ env.LINUX_BASE_IMAGE }} build downstream -p ${{ env.PACKAGE_NAME }} windows: - runs-on: windows-2022 # latest + runs-on: windows-2025 # latest steps: - uses: aws-actions/configure-aws-credentials@v4 with: @@ -160,8 +160,8 @@ python -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder.pyz')" python builder.pyz build -p ${{ env.PACKAGE_NAME }} - windows-vc14: - runs-on: windows-2019 # windows-2019 is last env with Visual Studio 2015 (v14.0) + windows-vc17: + runs-on: windows-2025 # latest strategy: matrix: arch: [x86, x64] @@ -173,10 +173,10 @@ - name: Build ${{ env.PACKAGE_NAME }} + consumers run: | python -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder.pyz')" - python builder.pyz build -p ${{ env.PACKAGE_NAME }} --target windows-${{ matrix.arch }} --compiler msvc-14 + python builder.pyz build -p ${{ env.PACKAGE_NAME }} --target windows-${{ matrix.arch }} --compiler msvc-17 windows-shared-libs: - runs-on: windows-2022 # latest + runs-on: windows-2025 # latest steps: - uses: aws-actions/configure-aws-credentials@v4 with: @@ -188,7 +188,7 @@ python builder.pyz build -p ${{ env.PACKAGE_NAME }} --cmake-extra=-DBUILD_SHARED_LIBS=ON windows-app-verifier: - runs-on: windows-2022 # latest + runs-on: windows-2025 # latest steps: - uses: aws-actions/configure-aws-credentials@v4 with: @@ -267,7 +267,7 @@ 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 + runs-on: windows-2025 # latest steps: - uses: aws-actions/configure-aws-credentials@v4 with: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-http-0.10.2/include/aws/http/private/no_proxy.h new/aws-c-http-0.10.4/include/aws/http/private/no_proxy.h --- old/aws-c-http-0.10.2/include/aws/http/private/no_proxy.h 1970-01-01 01:00:00.000000000 +0100 +++ new/aws-c-http-0.10.4/include/aws/http/private/no_proxy.h 2025-07-21 22:04:12.000000000 +0200 @@ -0,0 +1,33 @@ +#ifndef AWS_NO_PROXY_H +#define AWS_NO_PROXY_H + +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/http/http.h> +AWS_PUSH_SANE_WARNING_LEVEL +AWS_EXTERN_C_BEGIN + +/* + * Check if a host should bypass the proxy based on the NO_PROXY environment variable or provided no_proxy value. + * Since NO_PROXY has no standard yet. Follows the curl implementation from noproxy.c at 8.14.1. + * https://github.com/curl/curl/blob/curl-8_14_1/lib/noproxy.c + * + * NO_PROXY is a comma-separated list of domain names, hostnames, or IP addresses that + * should bypass the proxy. + * + * If no_proxy is NULL, the function will read the NO_PROXY environment variable. + * If no_proxy is provided, it will be used instead of the environment variable. + * + * Returns true if the host should bypass the proxy. + */ +AWS_HTTP_API bool aws_http_host_matches_no_proxy( + struct aws_allocator *allocator, + struct aws_byte_cursor host, + struct aws_string *no_proxy_str); + +AWS_EXTERN_C_END +AWS_POP_SANE_WARNING_LEVEL + +#endif /* AWS_NO_PROXY_H */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-http-0.10.2/include/aws/http/proxy.h new/aws-c-http-0.10.4/include/aws/http/proxy.h --- old/aws-c-http-0.10.2/include/aws/http/proxy.h 2025-06-09 18:12:18.000000000 +0200 +++ new/aws-c-http-0.10.4/include/aws/http/proxy.h 2025-07-21 22:04:12.000000000 +0200 @@ -43,7 +43,9 @@ * Enable get proxy URL from environment variable, when the manual proxy options of connection manager is not set. * env HTTPS_PROXY/https_proxy will be checked when the main connection use tls. * env HTTP_PROXY/http_proxy will be checked when the main connection NOT use tls. - * The lower case version has precedence. + * env NO_PROXY/no_proxy will be checked to bypass proxy if the host match the pattern. + * Check `aws_http_host_matches_no_proxy` for detail. This function can also be used with a direct no_proxy + * parameter. The lower case version has precedence. */ AWS_HPEV_ENABLE, }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-http-0.10.2/source/no_proxy.c new/aws-c-http-0.10.4/source/no_proxy.c --- old/aws-c-http-0.10.2/source/no_proxy.c 1970-01-01 01:00:00.000000000 +0100 +++ new/aws-c-http-0.10.4/source/no_proxy.c 2025-07-21 22:04:12.000000000 +0200 @@ -0,0 +1,293 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/common/byte_order.h> +#include <aws/common/environment.h> +#include <aws/http/private/no_proxy.h> +#include <aws/io/socket.h> + +enum hostname_type { + HOSTNAME_TYPE_IPV4, + HOSTNAME_TYPE_IPV6, + HOSTNAME_TYPE_REGULAR, +}; + +/** + * s_cidr4_match() returns true if the given IPv4 address is within the + * specified CIDR address range. + * Based on the curl implementation Curl_cidr4_match(). + * + * @param bits The number of network bits in the CIDR notation + * @param network_part The network pattern to match against (e.g., "192.168.0.0")\ + * @param host_addr Pre-parsed binary representation of the host IP, or NULL to parse from host + * @return true if the IP address matches the CIDR pattern, false otherwise + */ +static bool s_cidr4_match(uint64_t bits, struct aws_string *network_part, uint32_t address) { + + uint32_t check = 0; + + /* Check for valid bits parameter */ + if (bits > 32) { + /* Invalid netmask bits */ + return false; + } + + /* Convert network pattern to binary */ + if (aws_parse_ipv4_address(network_part, &check) != AWS_OP_SUCCESS) { + return false; + } + + if (bits > 0 && bits < 32) { + /* Apply the network mask for CIDR comparison */ + uint32_t mask = 0xffffffff << (32 - bits); + uint32_t host_network = aws_ntoh32(address); + uint32_t check_network = aws_ntoh32(check); + + /* Compare the masked addresses */ + return (host_network & mask) == (check_network & mask); + } + + /* For /32 or no bits specified, use exact match */ + return address == check; +} + +/** + * s_cidr6_match() returns true if the given IPv6 address is within the + * specified CIDR address range. + * Based on the curl implementation Curl_cidr6_match(). + * + * @param bits The number of network bits in the CIDR notation + * @param network_part The network pattern to match against (e.g., "2001:db8::") + * @param host_addr Pre-parsed binary representation of the host IP, or NULL to parse from host + * @return true if the IP address matches the CIDR pattern, false otherwise + */ +static bool s_cidr6_match( + struct aws_allocator *allocator, + uint64_t bits, + struct aws_string *network_part, + struct aws_byte_cursor address) { + bool result = false; + struct aws_byte_buf check_buf; + aws_byte_buf_init(&check_buf, allocator, 16); + /* If no bits specified, use full 128 bits for IPv6 */ + if (!bits) { + bits = 128; + } + + /* Check for valid bits parameter */ + if (bits > 128) { + goto cleanup; + } + /* Convert network pattern to binary */ + if (aws_parse_ipv6_address(network_part, &check_buf) != AWS_OP_SUCCESS) { + goto cleanup; + } + struct aws_byte_cursor check = aws_byte_cursor_from_buf(&check_buf); + + /* Calculate full bytes and remaining bits in the netmask */ + uint64_t bytes = bits / 8; + uint64_t rest = bits % 8; + if (bytes > address.len || address.len != check_buf.len || check_buf.len != 16) { + goto cleanup; + } + if (bytes > 0 && !aws_array_eq(address.ptr, (size_t)bytes, check.ptr, (size_t)bytes)) { + goto cleanup; + } + + /* If we have remaining bits, compare the partial byte */ + if (rest > 0) { + /* Create a mask for the remaining bits */ + unsigned char mask = (unsigned char)(0xff << (8 - rest)); + aws_byte_cursor_advance(&check, (size_t)bytes); + aws_byte_cursor_advance(&address, (size_t)bytes); + uint8_t address_byte = 0; + uint8_t check_byte = 0; + if (aws_byte_cursor_read_u8(&address, &address_byte) == false || + aws_byte_cursor_read_u8(&check, &check_byte) == false) { + goto cleanup; + } + /* Check if the masked bits match */ + if ((address_byte & mask) != (check_byte & mask)) { + goto cleanup; + } + } + + /* All checks passed, addresses match within the CIDR range */ + result = true; +cleanup: + aws_byte_buf_clean_up(&check_buf); + return result; +} + +static bool s_is_dot(uint8_t c) { + return c == '.'; +} + +/* The host is expected to be the host result from URL parser. */ +bool aws_http_host_matches_no_proxy( + struct aws_allocator *allocator, + struct aws_byte_cursor host, + struct aws_string *no_proxy_str) { + if (host.len == 0 || no_proxy_str == NULL) { + return false; + } + /* Single "*" wildcard matches all hosts */ + if (aws_string_eq_c_str(no_proxy_str, "*")) { + AWS_LOGF_DEBUG(AWS_LS_HTTP_CONNECTION, "wildcard no_proxy found, bypassing any proxy"); + return true; + } + bool bypass = false; + struct aws_byte_cursor no_proxy_cur = aws_byte_cursor_from_string(no_proxy_str); + struct aws_array_list no_proxy_list; + struct aws_string *host_str = aws_string_new_from_cursor(allocator, &host); + struct aws_byte_buf ipv6_addr = {0}; + + if (aws_array_list_init_dynamic(&no_proxy_list, allocator, 10, sizeof(struct aws_byte_cursor))) { + goto cleanup; + } + /* Split the NO_PROXY string by commas */ + if (aws_byte_cursor_split_on_char(&no_proxy_cur, ',', &no_proxy_list)) { + goto cleanup; + } + + /* Store parsed binary addresses for reuse */ + uint32_t ipv4_addr = 0; + + /* Determine host type and parse address if applicable */ + enum hostname_type type = HOSTNAME_TYPE_REGULAR; + if (aws_parse_ipv4_address(host_str, &ipv4_addr) == AWS_OP_SUCCESS) { + type = HOSTNAME_TYPE_IPV4; + } else { + struct aws_string *host_str_copy = host_str; + struct aws_byte_cursor host_copy = host; + if (host_copy.ptr[0] == '[' && host_copy.ptr[host_copy.len - 1] == ']') { + /* Check if the address is enclosed in brackets and strip them for validation */ + aws_byte_cursor_advance(&host_copy, 1); + host_copy.len--; + host_str_copy = aws_string_new_from_cursor(allocator, &host_copy); + } + + aws_byte_buf_init(&ipv6_addr, allocator, 16); + if (aws_parse_ipv6_address(host_str_copy, &ipv6_addr) == AWS_OP_SUCCESS) { + /* Update the host str */ + if (host_str != host_str_copy) { + aws_string_destroy(host_str); + host_str = host_str_copy; + } + type = HOSTNAME_TYPE_IPV6; + } else { + /* Not an IP address, so it's a regular hostname */ + type = HOSTNAME_TYPE_REGULAR; + /* Ignore the trailing dot in the hostname */ + host = aws_byte_cursor_right_trim_pred(&host, s_is_dot); + } + if (host_str != host_str_copy) { + /* clean up the copy, but don't update the str. */ + aws_string_destroy(host_str_copy); + } + } + + for (size_t i = 0; i < aws_array_list_length(&no_proxy_list); i++) { + struct aws_byte_cursor pattern; + if (aws_array_list_get_at(&no_proxy_list, &pattern, i)) { + continue; + } + + /* Trim whitespace from both ends for the pattern */ + pattern = aws_byte_cursor_trim_pred(&pattern, aws_isspace); + if (pattern.len == 0) { + /* If pattern is empty, ignore it. */ + continue; + } + switch (type) { + case HOSTNAME_TYPE_REGULAR: { + /** + * A: example.com matches 'example.com' + * B: www.example.com matches 'example.com' + * C: nonexample.com DOES NOT match 'example.com' + */ + /* Trim dot from both ends for the pattern */ + pattern = aws_byte_cursor_trim_pred(&pattern, s_is_dot); + if (pattern.len == 0) { + /* If pattern is empty, ignore it. */ + continue; + } + if (pattern.len == host.len) { + if (aws_byte_cursor_eq_ignore_case(&pattern, &host)) { + bypass = true; + goto cleanup; + } else { + continue; + } + } else if (pattern.len < host.len) { + /* Check if the pattern is a suffix of the host. All the math is safe since pattern.len < + * host.len + */ + struct aws_byte_cursor tail_with_extra_byte = host; + /* 1. the byte before the tail should be `.` */ + aws_byte_cursor_advance(&tail_with_extra_byte, host.len - pattern.len - 1); + uint8_t var = 0; + /* tail_with_extra_byte will be updated to move over the `.` */ + aws_byte_cursor_read_u8(&tail_with_extra_byte, &var); + if (var != '.') { + continue; + } + /* 2. the tail of the host should match the pattern */ + if (aws_byte_cursor_eq_ignore_case(&pattern, &tail_with_extra_byte)) { + bypass = true; + goto cleanup; + } else { + continue; + } + } + } break; + case HOSTNAME_TYPE_IPV4: + case HOSTNAME_TYPE_IPV6: { + /* Extract network part and bits from CIDR notation */ + struct aws_byte_cursor substr = {0}; + struct aws_byte_cursor network_part = {0}; + /* CIDR found. parse the bits */ + uint64_t network_bits = 0; + if (aws_byte_cursor_next_split(&pattern, '/', &substr)) { + network_part = substr; + } + if (aws_byte_cursor_next_split(&pattern, '/', &substr)) { + /* There is a second part of the pattern after `/`. */ + /* Now, take the rest of the pattern after `/` as the bits */ + aws_byte_cursor_advance(&pattern, network_part.len + 1); + if (aws_byte_cursor_utf8_parse_u64(pattern, &network_bits)) { + continue; + } + } + struct aws_string *network_part_str = aws_string_new_from_cursor(allocator, &network_part); + if (type == HOSTNAME_TYPE_IPV4) { + if (s_cidr4_match(network_bits, network_part_str, ipv4_addr)) { + bypass = true; + aws_string_destroy(network_part_str); + goto cleanup; + } + } else { + if (s_cidr6_match( + allocator, network_bits, network_part_str, aws_byte_cursor_from_buf(&ipv6_addr))) { + bypass = true; + aws_string_destroy(network_part_str); + goto cleanup; + } + } + aws_string_destroy(network_part_str); + } break; + + default: + /* Invalid stage */ + AWS_FATAL_ASSERT(false); + break; + } + } + +cleanup: + aws_byte_buf_clean_up(&ipv6_addr); + aws_string_destroy(host_str); + aws_array_list_clean_up(&no_proxy_list); + return bypass; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-http-0.10.2/source/proxy_connection.c new/aws-c-http-0.10.4/source/proxy_connection.c --- old/aws-c-http-0.10.2/source/proxy_connection.c 2025-06-09 18:12:18.000000000 +0200 +++ new/aws-c-http-0.10.4/source/proxy_connection.c 2025-07-21 22:04:12.000000000 +0200 @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0. */ +#include <aws/http/private/no_proxy.h> #include <aws/http/private/proxy_impl.h> #include <aws/common/encoding.h> @@ -11,7 +12,6 @@ #include <aws/common/string.h> #include <aws/http/connection_manager.h> #include <aws/http/private/connection_impl.h> -#include <aws/http/proxy.h> #include <aws/http/request_response.h> #include <aws/io/channel.h> #include <aws/io/logging.h> @@ -29,10 +29,12 @@ AWS_STATIC_STRING_FROM_LITERAL(s_options_method, "OPTIONS"); AWS_STATIC_STRING_FROM_LITERAL(s_star_path, "*"); -AWS_STATIC_STRING_FROM_LITERAL(s_http_proxy_env_var, "HTTP_PROXY"); -AWS_STATIC_STRING_FROM_LITERAL(s_http_proxy_env_var_low, "http_proxy"); -AWS_STATIC_STRING_FROM_LITERAL(s_https_proxy_env_var, "HTTPS_PROXY"); -AWS_STATIC_STRING_FROM_LITERAL(s_https_proxy_env_var_low, "https_proxy"); +static const char *s_http_proxy_env_var = "HTTP_PROXY"; +static const char *s_http_proxy_env_var_low = "http_proxy"; +static const char *s_https_proxy_env_var = "HTTPS_PROXY"; +static const char *s_https_proxy_env_var_low = "https_proxy"; +static const char *s_no_proxy_env_var = "NO_PROXY"; +static const char *s_no_proxy_env_var_low = "no_proxy"; #ifndef BYO_CRYPTO AWS_STATIC_STRING_FROM_LITERAL(s_proxy_no_verify_peer_env_var, "AWS_PROXY_NO_VERIFY_PEER"); @@ -1140,23 +1142,6 @@ } } -static struct aws_string *s_get_proxy_environment_value( - struct aws_allocator *allocator, - const struct aws_string *env_name) { - struct aws_string *out_string = NULL; - if (aws_get_environment_value(allocator, env_name, &out_string) == AWS_OP_SUCCESS && out_string != NULL && - out_string->len > 0) { - AWS_LOGF_DEBUG( - AWS_LS_HTTP_CONNECTION, - "%s environment found, %s", - aws_string_c_str(env_name), - aws_string_c_str(out_string)); - return out_string; - } - aws_string_destroy(out_string); - return NULL; -} - static int s_proxy_uri_init_from_env_variable( struct aws_allocator *allocator, const struct aws_http_client_connection_options *options, @@ -1164,18 +1149,40 @@ bool *found) { struct aws_string *proxy_uri_string = NULL; *found = false; + + /* First check if this host should bypass proxy using NO_PROXY */ + struct aws_byte_cursor host_cursor = options->host_name; + + /* Get the NO_PROXY environment variable */ + struct aws_string *no_proxy_str = aws_get_env_nonempty(allocator, s_no_proxy_env_var_low); + if (no_proxy_str == NULL) { + no_proxy_str = aws_get_env_nonempty(allocator, s_no_proxy_env_var); + } + + if (no_proxy_str != NULL) { + if (aws_http_host_matches_no_proxy(allocator, host_cursor, no_proxy_str)) { + AWS_LOGF_DEBUG( + AWS_LS_HTTP_CONNECTION, + "Host \"" PRInSTR "\" found in NO_PROXY, bypassing proxy", + AWS_BYTE_CURSOR_PRI(host_cursor)); + aws_string_destroy(no_proxy_str); + return AWS_OP_SUCCESS; + } + aws_string_destroy(no_proxy_str); + } + if (options->tls_options) { - proxy_uri_string = s_get_proxy_environment_value(allocator, s_https_proxy_env_var_low); + proxy_uri_string = aws_get_env_nonempty(allocator, s_https_proxy_env_var_low); if (proxy_uri_string == NULL) { - proxy_uri_string = s_get_proxy_environment_value(allocator, s_https_proxy_env_var); + proxy_uri_string = aws_get_env_nonempty(allocator, s_https_proxy_env_var); } if (proxy_uri_string == NULL) { return AWS_OP_SUCCESS; } } else { - proxy_uri_string = s_get_proxy_environment_value(allocator, s_http_proxy_env_var_low); + proxy_uri_string = aws_get_env_nonempty(allocator, s_http_proxy_env_var_low); if (proxy_uri_string == NULL) { - proxy_uri_string = s_get_proxy_environment_value(allocator, s_http_proxy_env_var); + proxy_uri_string = aws_get_env_nonempty(allocator, s_http_proxy_env_var); } if (proxy_uri_string == NULL) { return AWS_OP_SUCCESS; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-http-0.10.2/tests/CMakeLists.txt new/aws-c-http-0.10.4/tests/CMakeLists.txt --- old/aws-c-http-0.10.2/tests/CMakeLists.txt 2025-06-09 18:12:18.000000000 +0200 +++ new/aws-c-http-0.10.4/tests/CMakeLists.txt 2025-07-21 22:04:12.000000000 +0200 @@ -636,6 +636,19 @@ add_test_case(http_proxy_adaptive_failure) add_test_case(http_forwarding_proxy_uri_rewrite) add_test_case(http_forwarding_proxy_uri_rewrite_options_star) + +# NO_PROXY tests +add_test_case(test_no_proxy_subdomain_matching) +add_test_case(test_no_proxy_wildcard_patterns) +add_test_case(test_no_proxy_case_insensitivity) +add_test_case(test_no_proxy_multiple_patterns) +add_test_case(test_no_proxy_ipv6_address) +add_test_case(test_no_proxy_whitespace_handling) +add_test_case(test_no_proxy_cidr_notation) +add_test_case(test_no_proxy_ipv6_cidr_notation) +add_test_case(test_no_proxy_invalid_patterns) +add_test_case(test_no_proxy_invalid_host_inputs) + add_test_case(http_tunnel_proxy_connection_success) add_test_case(https_tunnel_proxy_connection_success) add_test_case(http_tunnel_proxy_connection_failure_connect) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-http-0.10.2/tests/test_no_proxy.c new/aws-c-http-0.10.4/tests/test_no_proxy.c --- old/aws-c-http-0.10.2/tests/test_no_proxy.c 1970-01-01 01:00:00.000000000 +0100 +++ new/aws-c-http-0.10.4/tests/test_no_proxy.c 2025-07-21 22:04:12.000000000 +0200 @@ -0,0 +1,446 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include <aws/http/connection.h> +#include <aws/http/proxy.h> + +#include <aws/common/environment.h> +#include <aws/common/string.h> +#include <aws/io/uri.h> + +#include <aws/http/private/no_proxy.h> +#include <aws/testing/aws_test_harness.h> +#include <aws/testing/io_testing_channel.h> + +static int s_test_no_proxy_helper( + struct aws_allocator *allocator, + const char *host, + const char *no_proxy_value, + bool expected_bypass) { + /* Create a no_proxy string for testing */ + struct aws_string *no_proxy_str = aws_string_new_from_c_str(allocator, no_proxy_value); + + /* Call the function that checks NO_PROXY */ + ASSERT_UINT_EQUALS( + aws_http_host_matches_no_proxy(allocator, aws_byte_cursor_from_c_str(host), no_proxy_str), expected_bypass); + + /* Clean up */ + aws_string_destroy(no_proxy_str); + + return AWS_OP_SUCCESS; +} + +/** + * Test subdomain matching with NO_PROXY + */ +static int s_test_no_proxy_subdomain_matching(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + const char *no_proxy_value = ".example.com"; + + aws_http_library_init(allocator); + + /* Test that a subdomain matches when NO_PROXY contains a domain with leading dot */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "sub.example.com", no_proxy_value, true)); + /* cannot match */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "sub.subexample.com", no_proxy_value, false)); + + aws_http_library_clean_up(); + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(test_no_proxy_subdomain_matching, s_test_no_proxy_subdomain_matching); + +/** + * Test wildcard patterns in NO_PROXY + */ +static int s_test_no_proxy_wildcard_patterns(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + const char *no_proxy_value = "*"; + + aws_http_library_init(allocator); + + /* Test that a wildcard pattern matches all hosts */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "any.example.com", no_proxy_value, true)); + + aws_http_library_clean_up(); + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(test_no_proxy_wildcard_patterns, s_test_no_proxy_wildcard_patterns); + +/** + * Test case insensitivity in NO_PROXY + */ +static int s_test_no_proxy_case_insensitivity(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + const char *no_proxy_value = "example.COM"; + + aws_http_library_init(allocator); + + /* Test that case insensitive matching works for both host and NO_PROXY entries */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "EXAMPLE.com", no_proxy_value, true)); + + aws_http_library_clean_up(); + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(test_no_proxy_case_insensitivity, s_test_no_proxy_case_insensitivity); + +/** + * Test IPv6 addresses + */ +static int s_test_no_proxy_ipv6_address(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + /* Pattern don't allow `[]`, just follows what curl does. */ + const char *no_proxy_value = "2001:db8::1, ::1, [2001:db8::2]"; + + aws_http_library_init(allocator); + + /* Test that an IPv6 address in brackets matches */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "[2001:db8::1]", no_proxy_value, true)); + + /* Test another IPv6 address format (localhost) */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "::1", no_proxy_value, true)); + + /* Test a non-matching IPv6 address */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "[2001:db8::2]", no_proxy_value, false)); + + aws_http_library_clean_up(); + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(test_no_proxy_ipv6_address, s_test_no_proxy_ipv6_address); + +/** + * Test multiple patterns in NO_PROXY + */ +static int s_test_no_proxy_multiple_patterns(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + /* Only wildcard support is a single `*`, if it's in the list, it will be ignored. */ + /* commas at start or end won't affect it. */ + const char *no_proxy_value = ",,foo.bar,example.com,other.net,*,"; + + aws_http_library_init(allocator); + + /* Test that a host matches when it's in the middle of a comma-separated list */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "example.com", no_proxy_value, true)); + + /* Test that another host in the list also matches */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "foo.bar", no_proxy_value, true)); + + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "foo.", no_proxy_value, false)); + + aws_http_library_clean_up(); + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(test_no_proxy_multiple_patterns, s_test_no_proxy_multiple_patterns); + +/** + * Test whitespace handling in NO_PROXY + */ +static int s_test_no_proxy_whitespace_handling(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + const char *no_proxy_value = " example.com , foo.bar "; + + aws_http_library_init(allocator); + + /* Test that whitespace is properly handled in NO_PROXY entries */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "example.com", no_proxy_value, true)); + + /* Test that another host with whitespace also matches */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "foo.bar", no_proxy_value, true)); + + aws_http_library_clean_up(); + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(test_no_proxy_whitespace_handling, s_test_no_proxy_whitespace_handling); + +/** + * Test IP addresses in NO_PROXY + */ +static int s_test_no_proxy_ip_address(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + const char *no_proxy_value = "192.168.1.1,10.0.0.0"; + + aws_http_library_init(allocator); + + /* Test that IP address matching works */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "192.168.1.1", no_proxy_value, true)); + + /* Test that a different IP doesn't match */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "192.168.2.1", no_proxy_value, false)); + + aws_http_library_clean_up(); + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(test_no_proxy_ip_address, s_test_no_proxy_ip_address); + +/** + * Test port-specific exclusions in NO_PROXY + * + * NOTE: This tests a curl-specific feature where entries like "example.com:8080" + * can be used to bypass the proxy only for specific ports. The current implementation + * doesn't support this feature, so this test documents that behavior. + */ +static int s_test_no_proxy_port_specific(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + const char *no_proxy_value = "example.com:8080"; + + aws_http_library_init(allocator); + + /* Our implementation only does hostname matching and ignores port information. + * In curl, this would bypass only on port 8080, but in our implementation it + * bypasses for all ports. */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "example.com", no_proxy_value, true)); + + aws_http_library_clean_up(); + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(test_no_proxy_port_specific, s_test_no_proxy_port_specific); + +/** + * Test CIDR notation in NO_PROXY + * + * Tests the CIDR notation support (similar to curl 7.86.0) where "192.168.0.0/16" + * would match all addresses starting with "192.168". + */ +static int s_test_no_proxy_cidr_notation(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + const char *no_proxy_value_16 = "192.168.0.0/16"; + const char *no_proxy_value_24 = "192.168.5.0/24"; + + aws_http_library_init(allocator); + + /* Test that an IP address in a CIDR range matches */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "192.168.5.10", no_proxy_value_16, true)); + + /* Test that an IP address outside the CIDR range doesn't match */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "10.0.5.10", no_proxy_value_16, false)); + + /* Test that an IP address in a more specific CIDR range matches */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "192.168.5.10", no_proxy_value_24, true)); + + /* Test that an IP address outside the specific CIDR range doesn't match */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "192.168.6.10", no_proxy_value_24, false)); + + aws_http_library_clean_up(); + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(test_no_proxy_cidr_notation, s_test_no_proxy_cidr_notation); + +/** + * Test IPv6 CIDR notation in NO_PROXY + * + * Tests the CIDR notation support for IPv6 addresses where "2001:db8::/32" + * would match all addresses starting with "2001:db8". + */ +static int s_test_no_proxy_ipv6_cidr_notation(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + const char *no_proxy_value_32 = "2001:db8::/32"; + const char *no_proxy_value_64 = "2001:db8:1:2::/64"; + + aws_http_library_init(allocator); + + /* Test that an IPv6 address in a CIDR range matches */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "[2001:db8:1:2::3]", no_proxy_value_32, true)); + + /* Test that an IPv6 address outside the CIDR range doesn't match */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "[2001:db9:1:2::3]", no_proxy_value_32, false)); + + /* Test that an IPv6 address in a more specific CIDR range matches */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "[2001:db8:1:2::3]", no_proxy_value_64, true)); + + /* Test that an IPv6 address outside the specific CIDR range doesn't match */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "[2001:db8:1:3::3]", no_proxy_value_64, false)); + + aws_http_library_clean_up(); + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(test_no_proxy_ipv6_cidr_notation, s_test_no_proxy_ipv6_cidr_notation); + +/** + * Test invalid IP addresses and CIDR blocks in NO_PROXY + * + * Verifies that the NO_PROXY implementation safely handles and ignores invalid + * IP addresses and CIDR blocks without crashing. + */ +static int s_test_no_proxy_invalid_patterns(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_http_library_init(allocator); + + /* Test with invalid IP addresses and CIDR notations mixed with valid entries */ + const char *no_proxy_value = "example.com,999.999.999.999,192.168.1.3/33,192.168.b.c," + "2001:xyz::bad:ipv6,2001:db8::/129,not:a:valid:ip/64," + "[malformed],192.168.1.2," + "192.168.1.1/99999999999999999," /* Invalid network bits */ + "192.168.1.1/9/9/9," /* Invalid network bits */ + "2001:db8::/999999"; /* Invalid IPv6 prefix */ + // const char *no_proxy_value = "192.168.1.1/9/9/9"; + + /* Test that invalid IP addresses and CIDR blocks are safely ignored */ + /* The last valid entry (192.168.1.2) should still match */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "192.168.1.2", no_proxy_value, true)); + + /* Test that the valid hostname entry still works */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "example.com", no_proxy_value, true)); + + /* Test with an invalid host address parameter */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "192.168.1.1", no_proxy_value, false)); + + /* An invalid IP address will be treated as regular hostname and match as regular hostname. */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "999.999.999.999", no_proxy_value, true)); + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "999.999.999.999.999", no_proxy_value, true)); + + /* Test with only invalid entries */ + const char *invalid_only = "999.999.999.999,192.168.1.1/33,not:an:ip:addr,2001:xyz::bad"; + + /* Test that a valid IP doesn't match when NO_PROXY contains only invalid entries */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "192.168.1.1", invalid_only, false)); + + /* Test with empty host parameter */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "", invalid_only, false)); + + /* Test with a very malformed CIDR input that could cause parsing issues */ + const char *malformed_cidr = "192.168.1.1/abcdef,2001:db8::/xyz"; + + /* Malformed CIDR will be taken as the entry */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "2001:db8::", malformed_cidr, false)); + + /* Test with very large CIDR parts that would be rejected in the buffer size check */ + char large_value[200]; + memset(large_value, 'x', sizeof(large_value) - 1); + large_value[sizeof(large_value) - 1] = '\0'; + + char large_cidr[256]; + snprintf(large_cidr, sizeof(large_cidr), "192.168.1.1/%s", large_value); + + /* Test with oversized CIDR notation */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "192.168.1.1", large_cidr, false)); + + aws_http_library_clean_up(); + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(test_no_proxy_invalid_patterns, s_test_no_proxy_invalid_patterns); + +/** + * Test invalid host inputs to aws_http_host_matches_no_proxy + * + * Verifies that the aws_http_host_matches_no_proxy function handles malformed host inputs + * gracefully without crashing. These tests specifically check the host parameter + * rather than the NO_PROXY environment variable content. + */ +static int s_test_no_proxy_invalid_host_inputs(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_http_library_init(allocator); + + /* Set up a valid NO_PROXY value for testing */ + const char *no_proxy_value = "example.com,192.168.1.0/24,2001:db8::/32"; + + /* Test with invalid IPv4 address */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "999.999.999.999", no_proxy_value, false)); + + /* Test with malformed IPv4 address formats */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "192.168.1", no_proxy_value, false)); + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "192.168..1", no_proxy_value, false)); + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "192.168.1.", no_proxy_value, false)); + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, ".192.168.1", no_proxy_value, false)); + + /* Test with invalid IPv6 address variants */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "[2001:db8::xyz]", no_proxy_value, false)); + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "[2001:db8::]:", no_proxy_value, false)); + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "2001:db8:::", no_proxy_value, false)); + + /* Test with malformed IPv6 address brackets */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "[2001:db8::1", no_proxy_value, false)); + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "2001:db8::1]", no_proxy_value, false)); + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "[[2001:db8::1]]", no_proxy_value, false)); + + /* Test with empty host */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "", no_proxy_value, false)); + + /* Test with extremely long host that exceeds buffer sizes */ + char long_host[1024]; + memset(long_host, 'a', sizeof(long_host) - 1); + long_host[sizeof(long_host) - 1] = '\0'; + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, long_host, no_proxy_value, false)); + + /* Test with extremely long IPv4 address that would hit buffer checks */ + char long_ipv4[150] = "192.168.1.1"; + for (int i = 0; i < 130; i++) { + long_ipv4[11 + i] = '9'; /* Padding with extra digits */ + } + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, long_ipv4, no_proxy_value, false)); + + /* Test with extremely long IPv6 address that would hit buffer checks */ + char long_ipv6[150] = "[2001:db8::1"; + for (int i = 0; i < 130; i++) { + long_ipv6[11 + i] = '1'; /* Padding with extra digits */ + } + long_ipv6[141] = ']'; + long_ipv6[142] = '\0'; + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, long_ipv6, no_proxy_value, false)); + + aws_http_library_clean_up(); + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(test_no_proxy_invalid_host_inputs, s_test_no_proxy_invalid_host_inputs); + +/** + * Test behavior when NO_PROXY is empty + * + * Verifies that the aws_http_host_matches_no_proxy function correctly handles cases where + * the NO_PROXY value is empty. + */ +static int s_test_no_proxy_empty(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_http_library_init(allocator); + + /* Test with empty NO_PROXY */ + const char *empty_no_proxy_value = ""; + + /* With empty NO_PROXY, aws_http_host_matches_no_proxy should return false for any host */ + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "example.com", empty_no_proxy_value, false)); + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "192.168.1.1", empty_no_proxy_value, false)); + ASSERT_SUCCESS(s_test_no_proxy_helper(allocator, "[2001:db8::1]", empty_no_proxy_value, false)); + + aws_http_library_clean_up(); + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(test_no_proxy_empty, s_test_no_proxy_empty); + +/** + * Test using the direct no_proxy parameter + * + * Verifies that the aws_http_host_matches_no_proxy function correctly uses the provided no_proxy parameter. + */ +static int s_test_no_proxy_direct_parameter(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_http_library_init(allocator); + + /* Create different no_proxy values to test with */ + const char *direct_no_proxy_value = "direct.example.com"; + const char *wildcard_no_proxy_value = "*"; + + /* Test with specific host */ + ASSERT_TRUE(aws_http_host_matches_no_proxy( + allocator, + aws_byte_cursor_from_c_str("direct.example.com"), + aws_string_new_from_c_str(allocator, direct_no_proxy_value))); + + ASSERT_FALSE(aws_http_host_matches_no_proxy( + allocator, + aws_byte_cursor_from_c_str("other.example.com"), + aws_string_new_from_c_str(allocator, direct_no_proxy_value))); + + /* Test with wildcard */ + ASSERT_TRUE(aws_http_host_matches_no_proxy( + allocator, + aws_byte_cursor_from_c_str("any.host.com"), + aws_string_new_from_c_str(allocator, wildcard_no_proxy_value))); + + aws_http_library_clean_up(); + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(test_no_proxy_direct_parameter, s_test_no_proxy_direct_parameter);