diff --git a/src/aws-cpp-sdk-core/source/http/HttpRequest.cpp b/src/aws-cpp-sdk-core/source/http/HttpRequest.cpp index 55ca99c64661..512d94bce2e9 100644 --- a/src/aws-cpp-sdk-core/source/http/HttpRequest.cpp +++ b/src/aws-cpp-sdk-core/source/http/HttpRequest.cpp @@ -70,15 +70,6 @@ namespace Aws // CRT sigv4 don't any encoding if double encoding is off, need to encode the path before passing to CRT. const URI& uri = m_uri; Aws::StringStream ss; - Aws::StringStream port; - if (uri.GetScheme() == Scheme::HTTP && uri.GetPort() != HTTP_DEFAULT_PORT) - { - port << ":" << uri.GetPort(); - } - else if (uri.GetScheme() == Scheme::HTTPS && uri.GetPort() != HTTPS_DEFAULT_PORT) - { - port << ":" << uri.GetPort(); - } // Note: GetURLEncodedPathRFC3986 does legacy mode by default, which // leaves some reserved delimeter characters unencoded (things like @@ -88,8 +79,11 @@ namespace Aws // practice however CRT will never support legacy mode and will // always need modern encoding, so be explicit here about which mode // we use. - ss << SchemeMapper::ToString(uri.GetScheme()) << SEPARATOR << uri.GetAuthority() << port.str() - << ((uri.GetPath() == "/") ? "" : uri.GetURLEncodedPath()) + + // Set path to origin-form (path + query only) per RFC 9112 ยง3.2.1. + // aws-c-http converts to absolute-form only when sending through a + // forward proxy via aws_http_rewrite_uri_for_proxy_request. + ss << (uri.GetPath().empty() ? "/" : uri.GetURLEncodedPath()) << uri.GetQueryString(); request->SetPath(Aws::Crt::ByteCursorFromCString(ss.str().c_str())); diff --git a/tests/aws-cpp-sdk-core-tests/http/HttpClientTest.cpp b/tests/aws-cpp-sdk-core-tests/http/HttpClientTest.cpp index 822df5e5859a..e9b558a23fb8 100644 --- a/tests/aws-cpp-sdk-core-tests/http/HttpClientTest.cpp +++ b/tests/aws-cpp-sdk-core-tests/http/HttpClientTest.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -385,4 +386,84 @@ TEST_F(CURLHttpClientTest, TestHttpRequestWorksFine) #endif // ENABLE_CURL_CLIENT #endif // ENABLE_HTTP_CLIENT_TESTING #endif // NO_HTTP_CLIENT + #endif // DISABLE_DNS_REQUIRED_TESTS + +// Test ToCrtHttpRequest conversion (no network required) +#include +#include +#include + +class HttpRequestConversionTest : public Aws::Testing::AwsCppSdkGTestSuite +{ +}; + +// Test that ToCrtHttpRequest produces origin-form request-target (path + query only) +TEST_F(HttpRequestConversionTest, ToCrtHttpRequestOriginForm) +{ + // Test with HTTP scheme and non-default port + { + Aws::Http::URI uri("http://example.com:9000/bucket/key?uploadId=abc&partNumber=1"); + Aws::Http::Standard::StandardHttpRequest request(uri, Aws::Http::HttpMethod::HTTP_PUT); + + auto crtRequest = request.ToCrtHttpRequest(); + ASSERT_NE(nullptr, crtRequest); + + // The path should be origin-form: /path?query + // NOT absolute-form: http://example.com:9000/path?query + auto pathOpt = crtRequest->GetPath(); + ASSERT_TRUE(pathOpt.has_value()); + Aws::String path(reinterpret_cast(pathOpt->ptr), pathOpt->len); + EXPECT_EQ("/bucket/key?uploadId=abc&partNumber=1", path); + EXPECT_FALSE(path.find("http://") == 0); + EXPECT_FALSE(path.find("https://") == 0); + } + + // Test with HTTPS scheme and default port + { + Aws::Http::URI uri("https://s3.amazonaws.com/my-bucket/my-key"); + Aws::Http::Standard::StandardHttpRequest request(uri, Aws::Http::HttpMethod::HTTP_GET); + + auto crtRequest = request.ToCrtHttpRequest(); + ASSERT_NE(nullptr, crtRequest); + + auto pathOpt = crtRequest->GetPath(); + ASSERT_TRUE(pathOpt.has_value()); + Aws::String path(reinterpret_cast(pathOpt->ptr), pathOpt->len); + EXPECT_EQ("/my-bucket/my-key", path); + EXPECT_FALSE(path.find("http://") == 0); + EXPECT_FALSE(path.find("https://") == 0); + } + + // Test with query string + { + Aws::Http::URI uri("https://storage.example.com:443/path/to/file?query=value&foo=bar"); + Aws::Http::Standard::StandardHttpRequest request(uri, Aws::Http::HttpMethod::HTTP_POST); + + auto crtRequest = request.ToCrtHttpRequest(); + ASSERT_NE(nullptr, crtRequest); + + auto pathOpt = crtRequest->GetPath(); + ASSERT_TRUE(pathOpt.has_value()); + Aws::String path(reinterpret_cast(pathOpt->ptr), pathOpt->len); + EXPECT_EQ("/path/to/file?query=value&foo=bar", path); + EXPECT_FALSE(path.find("http://") == 0); + EXPECT_FALSE(path.find("https://") == 0); + } + + // Test with root path + { + Aws::Http::URI uri("http://localhost:8080/"); + Aws::Http::Standard::StandardHttpRequest request(uri, Aws::Http::HttpMethod::HTTP_HEAD); + + auto crtRequest = request.ToCrtHttpRequest(); + ASSERT_NE(nullptr, crtRequest); + + auto pathOpt = crtRequest->GetPath(); + ASSERT_TRUE(pathOpt.has_value()); + Aws::String path(reinterpret_cast(pathOpt->ptr), pathOpt->len); + // Root path should be "/" + EXPECT_EQ("/", path); + EXPECT_FALSE(path.find("http://") == 0); + } +}