All Downloads are FREE. Search and download functionalities are using the official Maven repository.

software.amazon.awssdk.services.ecr.endpoints.internal.AwsEndpointProviderUtils Maven / Gradle / Ivy

Go to download

The AWS Java SDK for the Amazon EC2 Container Registry holds the client classes that are used for communicating with the Amazon EC2 Container Registry Service

There is a newer version: 2.29.39
Show newest version
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with
 * the License. A copy of the License is located at
 * 
 * http://aws.amazon.com/apache2.0
 * 
 * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
 * and limitations under the License.
 */

package software.amazon.awssdk.services.ecr.endpoints.internal;

import static software.amazon.awssdk.utils.FunctionalUtils.invokeSafely;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.awscore.AwsExecutionAttribute;
import software.amazon.awssdk.awscore.AwsRequest;
import software.amazon.awssdk.awscore.AwsRequestOverrideConfiguration;
import software.amazon.awssdk.awscore.endpoints.AwsEndpointAttribute;
import software.amazon.awssdk.core.exception.SdkClientException;
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute;
import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute;
import software.amazon.awssdk.endpoints.Endpoint;
import software.amazon.awssdk.http.SdkHttpRequest;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.utils.HostnameValidator;
import software.amazon.awssdk.utils.Logger;
import software.amazon.awssdk.utils.StringUtils;
import software.amazon.awssdk.utils.http.SdkHttpUtils;

@SdkInternalApi
public final class AwsEndpointProviderUtils {
    private static final Logger LOG = Logger.loggerFor(AwsEndpointProviderUtils.class);

    private AwsEndpointProviderUtils() {
    }

    public static Region regionBuiltIn(ExecutionAttributes executionAttributes) {
        return executionAttributes.getAttribute(AwsExecutionAttribute.AWS_REGION);
    }

    public static Boolean dualStackEnabledBuiltIn(ExecutionAttributes executionAttributes) {
        return executionAttributes.getAttribute(AwsExecutionAttribute.DUALSTACK_ENDPOINT_ENABLED);
    }

    public static Boolean fipsEnabledBuiltIn(ExecutionAttributes executionAttributes) {
        return executionAttributes.getAttribute(AwsExecutionAttribute.FIPS_ENDPOINT_ENABLED);
    }

    /**
     * Returns the endpoint set on the client. Note that this strips off the query part of the URI because the endpoint
     * rules library, e.g. {@code ParseURL} will return an exception if the URI it parses has query parameters.
     */
    public static String endpointBuiltIn(ExecutionAttributes executionAttributes) {
        if (endpointIsOverridden(executionAttributes)) {
            return invokeSafely(() -> {
                URI endpointOverride = executionAttributes.getAttribute(SdkExecutionAttribute.CLIENT_ENDPOINT);
                return new URI(endpointOverride.getScheme(), null, endpointOverride.getHost(), endpointOverride.getPort(),
                        endpointOverride.getPath(), null, endpointOverride.getFragment()).toString();
            });
        }
        return null;
    }

    public static Boolean useGlobalEndpointBuiltIn(ExecutionAttributes executionAttributes) {
        return executionAttributes.getAttribute(AwsExecutionAttribute.USE_GLOBAL_ENDPOINT);
    }

    /**
     * True if the the {@link SdkExecutionAttribute#ENDPOINT_OVERRIDDEN} attribute is present and its value is
     * {@code true}, {@code false} otherwise.
     */
    public static boolean endpointIsOverridden(ExecutionAttributes attrs) {
        return attrs.getOptionalAttribute(SdkExecutionAttribute.ENDPOINT_OVERRIDDEN).orElse(false);
    }

    /**
     * True if the the {@link SdkInternalExecutionAttribute#IS_DISCOVERED_ENDPOINT} attribute is present and its value
     * is {@code true}, {@code false} otherwise.
     */
    public static boolean endpointIsDiscovered(ExecutionAttributes attrs) {
        return attrs.getOptionalAttribute(SdkInternalExecutionAttribute.IS_DISCOVERED_ENDPOINT).orElse(false);
    }

    /**
     * True if the the {@link SdkInternalExecutionAttribute#DISABLE_HOST_PREFIX_INJECTION} attribute is present and its
     * value is {@code true}, {@code false} otherwise.
     */
    public static boolean disableHostPrefixInjection(ExecutionAttributes attrs) {
        return attrs.getOptionalAttribute(SdkInternalExecutionAttribute.DISABLE_HOST_PREFIX_INJECTION).orElse(false);
    }

    /**
     * Apply the given endpoint prefix to the endpoint.
     */
    public static Endpoint addHostPrefix(Endpoint endpoint, String prefix) {
        if (StringUtils.isBlank(prefix)) {
            return endpoint;
        }

        validatePrefixIsHostNameCompliant(prefix);

        URI originalUrl = endpoint.url();
        String newHost = prefix + endpoint.url().getHost();
        URI newUrl = invokeSafely(() -> new URI(originalUrl.getScheme(), null, newHost, originalUrl.getPort(),
                originalUrl.getPath(), originalUrl.getQuery(), originalUrl.getFragment()));

        return endpoint.toBuilder().url(newUrl).build();
    }

    public static Endpoint valueAsEndpointOrThrow(Value value) {
        if (value instanceof Value.Endpoint) {
            Value.Endpoint endpoint = value.expectEndpoint();
            Endpoint.Builder builder = Endpoint.builder();
            builder.url(URI.create(endpoint.getUrl()));

            Map> headers = endpoint.getHeaders();
            if (headers != null) {
                headers.forEach((name, values) -> values.forEach(v -> builder.putHeader(name, v)));
            }

            addKnownProperties(builder, endpoint.getProperties());

            return builder.build();
        } else if (value instanceof Value.Str) {
            String errorMsg = value.expectString();
            throw SdkClientException.create(errorMsg);
        } else {
            throw SdkClientException.create("Rule engine return neither an endpoint result or error value. Returned value was:"
                    + value);
        }
    }

    /**
     * This sets the request URI to the resolved URI returned by the endpoint provider. There are some things to be
     * careful about to make this work properly:
     * 

* If the client endpoint is an endpoint override, it may contain a path. In addition, the request marshaller itself * may add components to the path if it's modeled for the operation. Unfortunately, * {@link SdkHttpRequest#encodedPath()} returns the combined path from both the endpoint and the request. There is * no way to know, just from the HTTP request object, where the override path ends (if it's even there) and where * the request path starts. Additionally, the rule itself may also append other parts to the endpoint override path. *

* To solve this issue, we pass in the endpoint set on the path, which allows us to the strip the path from the * endpoint override from the request path, and then correctly combine the paths. *

* For example, let's suppose the endpoint override on the client is {@code https://example.com/a}. Then we call an * operation {@code Foo()}, that marshalls {@code /c} to the path. The resulting request path is {@code /a/c}. * However, we also pass the endpoint to provider as a parameter, and the resolver returns * {@code https://example.com/a/b}. This method takes care of combining the paths correctly so that the resulting * path is {@code https://example.com/a/b/c}. */ public static SdkHttpRequest setUri(SdkHttpRequest request, URI clientEndpoint, URI resolvedUri) { // [client endpoint path] String clientEndpointPath = clientEndpoint.getRawPath(); // [client endpoint path]/[request path] String requestPath = request.encodedPath(); // [client endpoint path]/[additional path added by resolver] String resolvedUriPath = resolvedUri.getRawPath(); String finalPath = requestPath; // If there is an additional path added by resolver, i.e., [additional path added by resolver] not null, // we need to combine the path if (!resolvedUriPath.equals(clientEndpointPath)) { finalPath = combinePath(clientEndpointPath, requestPath, resolvedUriPath); } return request.toBuilder().protocol(resolvedUri.getScheme()).host(resolvedUri.getHost()).port(resolvedUri.getPort()) .encodedPath(finalPath).build(); } /** * Our goal is to construct [client endpoint path]/[additional path added by resolver]/[request path], so we just * need to strip the client endpoint path from the marshalled request path to isolate just the part added by the * marshaller. Trailing slash is removed from client endpoint path before stripping because it could cause the * leading slash to be removed from the request path: e.g., StringUtils.replaceOnce("/", "//test", "") generates * "/test" and the expected result is "//test" */ private static String combinePath(String clientEndpointPath, String requestPath, String resolvedUriPath) { String requestPathWithClientPathRemoved = StringUtils.replaceOnce(requestPath, clientEndpointPath, ""); String finalPath = SdkHttpUtils.appendUri(resolvedUriPath, requestPathWithClientPathRemoved); return finalPath; } public static AwsRequest addHeaders(AwsRequest request, Map> headers) { AwsRequestOverrideConfiguration.Builder configBuilder = request.overrideConfiguration() .map(AwsRequestOverrideConfiguration::toBuilder).orElseGet(AwsRequestOverrideConfiguration::builder); headers.forEach((name, values) -> { List existingValues = configBuilder.headers().get(name); List updatedValues; if (existingValues != null) { updatedValues = new ArrayList<>(existingValues); } else { updatedValues = new ArrayList<>(); } updatedValues.addAll(values); configBuilder.putHeader(name, updatedValues); }); return request.toBuilder().overrideConfiguration(configBuilder.build()).build(); } private static void addKnownProperties(Endpoint.Builder builder, Map properties) { properties.forEach((n, v) -> { switch (n) { case "authSchemes": builder.putAttribute(AwsEndpointAttribute.AUTH_SCHEMES, AuthSchemeUtils.createAuthSchemes(v)); break; default: LOG.debug(() -> "Ignoring unknown endpoint property: " + n); break; } }); } private static void validatePrefixIsHostNameCompliant(String prefix) { String[] components = splitHostLabelOnDots(prefix); for (String component : components) { HostnameValidator.validateHostnameCompliant(component, component, "request"); } } private static String[] splitHostLabelOnDots(String label) { return label.split("\\."); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy