Skip to content

Conversation

@kai-ion
Copy link
Contributor

@kai-ion kai-ion commented Oct 27, 2025

Issue #, if available:

Description of changes:
Adding an endpoint resolver template to be called in client init to resolve endpoint in order chain

Check all that applies:

  • Did a review by yourself.
  • Added proper tests to cover this PR. (If tests are not applicable, explain.)
  • Checked if this PR is a breaking (APIs have been changed) change.
  • Checked if this PR will not introduce cross-platform inconsistent behavior.
  • Checked if this PR would require a ReadMe/Wiki update.

Check which platforms you have built SDK on to verify the correctness of this PR.

  • Linux
  • Windows
  • Android
  • MacOS
  • IOS
  • Other Platforms

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@kai-ion kai-ion force-pushed the noEnd branch 3 times, most recently from 2ace84b to 5e53be5 Compare October 28, 2025 21:09
@kai-ion kai-ion marked this pull request as ready for review October 31, 2025 17:07
@@ -78,6 +79,51 @@ namespace Endpoint
return SetFromClientConfiguration(static_cast<const Client::ClientConfiguration&>(config));
}

void BuiltInParameters::SetFromClientConfiguration(const Client::ClientConfiguration& config, const Aws::String& serviceName)
{
bool forceFIPS = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this logic is copy and pasted from the other function SetFromClientConfiguration with the exception that we handle the setting of override differently. lets not repeat ourselves and refactor this function so that we dont have copy and pasted code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor the logic to use a shared SetFromClientConfigurationImpl function

SetStringParameter(AWS_REGION, "region-not-set");
}
} else if (!serviceName.empty()) {
Aws::Config::EndpointResolver::EndpointSource(serviceName, config.profileName, config.scheme, *this);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we shouldnt be passing *this, the objective here is figure out what the endpoint override it, we should not also be mutating self in this call. something along the lines of

OverrideEndpoint(
  Aws::Config::EndpointResolver::EndpointSource(serviceName, config), 
  config.scheme);

would be what we want to be doing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor endpointResolver to return an endpoint instead of mutating the config directly

@@ -26,6 +26,8 @@ namespace Aws

virtual void SetFromClientConfiguration(const Client::ClientConfiguration& config);
virtual void SetFromClientConfiguration(const Client::GenericClientConfiguration& config);
virtual void SetFromClientConfiguration(const Client::ClientConfiguration& config, const Aws::String& serviceName);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should the non - service name ones have a implementation? why would they not simply call the service name function but with a empty service name?

would this be breaking if someone subclasses BuiltInParameters and they were already overriding SetFromClientConfiguration?

Copy link
Contributor Author

@kai-ion kai-ion Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor to use a fallback call instead of implementing new virtual functions

/**
* Resolver that sources endpoints and sets them on endpoint providers.
*/
class AWS_CORE_API EndpointResolver
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we have a class if all the methods are static and we have no members? currently this class is default constructible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change this class into a name space instead

/**
* Initialize client context parameters from a ClientConfiguration with service name
*/
virtual void InitBuiltInParameters(const ClientConfigurationT& config, const Aws::String& serviceName) = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adding a new pure virtual method means that anything else implementing this will break. why not have a default implementation that calls the previous function to preserve backwards compat?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor to use a fallback call with empty service name

@@ -26,6 +26,8 @@ namespace Aws

virtual void SetFromClientConfiguration(const Client::ClientConfiguration& config);
virtual void SetFromClientConfiguration(const Client::GenericClientConfiguration& config);
void SetFromClientConfiguration(const Client::ClientConfiguration& config, const Aws::String& serviceName);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we not allowing this to be virtual as it was before?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is me being stupid. Reverted back to virtual with the new impl private function to reduce code duplication.


// 2) Service-specific environment variable
{
Aws::String service = "AWS_ENDPOINT_URL_";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we create a new variable eachtime? shouldnt this be const and static

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added SERVICE_ENDPOINT_ENV_PREFIX as static const char*, also did the same for GLOBAL_ENDPOINT_ENV_VAR

service += serviceKey;
Aws::String fromEnv = Aws::Environment::GetEnv(service.c_str());
if (!fromEnv.empty()) {
AWS_LOGSTREAM_DEBUG(ENDPOINT_RESOLVER_TAG, "Resolved configured endpoint from service-specific environment variable: " << service);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we using DEBUG and TRACE in the same if statement, that means that no matter which log level you choose one or both will be displayed. additionally when you want to know one, but not the other?

Copy link
Contributor Author

@kai-ion kai-ion Nov 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced the DEBUG + TRACE with single DEBUG messages that include both the source information and the resolved URL


const char* ALLOCATION_TAG = "EndpointResolverIntegrationTest";

class S3TestClient : public S3Client
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why does this class exist? it looks like it just calls the base class and nothing more

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ops, copied over from s3unit test. Yes there is nothing in the test client being used in this integration test. (Headbucket request is already public).
Removed this class.

EXPECT_EQ("https://test-bucket.custom-s3.example.com", seenRequest.GetUri().GetURIString());
}

TEST_F(EndpointResolverIntegrationTest, GlobalEnvironmentVariable)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we would want a test for GlobalEnvironmentVariableUsedOverProfile so that we validate that global endpoints are chosen over a specific profile endpoint.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a GlobalEnvironmentVariableUsedOverProfile test, works as expected with global environment > profile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants