Skip to content

Commit

Permalink
Add S3 Object Lambda customizations
Browse files Browse the repository at this point in the history
  • Loading branch information
Quanzzzz authored and dagnir committed Mar 18, 2021
1 parent 12d3094 commit a5a0405
Show file tree
Hide file tree
Showing 24 changed files with 1,955 additions and 15 deletions.
Expand Up @@ -34,6 +34,7 @@
import software.amazon.awssdk.core.RequestOverrideConfiguration;
import software.amazon.awssdk.core.SdkRequest;
import software.amazon.awssdk.core.SdkResponse;
import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption;
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
import software.amazon.awssdk.core.client.config.SdkClientOption;
import software.amazon.awssdk.core.client.handler.ClientExecutionParams;
Expand Down Expand Up @@ -95,7 +96,9 @@ static <InputT extends SdkRequest, OutputT extends SdkResponse> ExecutionContext
.putAttribute(SdkExecutionAttribute.SERVICE_NAME, clientConfig.option(SdkClientOption.SERVICE_NAME))
.putAttribute(SdkExecutionAttribute.OPERATION_NAME, executionParams.getOperationName())
.putAttribute(SdkExecutionAttribute.CLIENT_ENDPOINT, clientConfig.option(SdkClientOption.ENDPOINT))
.putAttribute(SdkExecutionAttribute.ENDPOINT_OVERRIDDEN, clientConfig.option(SdkClientOption.ENDPOINT_OVERRIDDEN));
.putAttribute(SdkExecutionAttribute.ENDPOINT_OVERRIDDEN, clientConfig.option(SdkClientOption.ENDPOINT_OVERRIDDEN))
.putAttribute(SdkInternalExecutionAttribute.DISABLE_HOST_PREFIX_INJECTION,
clientConfig.option(SdkAdvancedClientOption.DISABLE_HOST_PREFIX_INJECTION));

ExecutionInterceptorChain executionInterceptorChain =
new ExecutionInterceptorChain(clientConfig.option(SdkClientOption.EXECUTION_INTERCEPTORS));
Expand Down
Expand Up @@ -33,6 +33,13 @@ public final class SdkInternalExecutionAttribute extends SdkExecutionAttribute {
public static final ExecutionAttribute<HttpChecksumRequired> HTTP_CHECKSUM_REQUIRED =
new ExecutionAttribute<>("HttpChecksumRequired");

/**
* Whether host prefix injection has been disbabled on the client.
* See {@link software.amazon.awssdk.core.client.config.SdkAdvancedClientOption#DISABLE_HOST_PREFIX_INJECTION}
*/
public static final ExecutionAttribute<Boolean> DISABLE_HOST_PREFIX_INJECTION =
new ExecutionAttribute<>("DisableHostPrefixInjection");

private SdkInternalExecutionAttribute() {
}
}
Expand Up @@ -38,6 +38,7 @@
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.internal.endpoints.S3EndpointResolverContext;
import software.amazon.awssdk.services.s3.internal.endpoints.S3EndpointResolverFactory;
import software.amazon.awssdk.services.s3.internal.endpoints.S3EndpointResolverFactoryContext;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.GetUrlRequest;
import software.amazon.awssdk.utils.Validate;
Expand Down Expand Up @@ -168,7 +169,12 @@ public URL getUrl(GetUrlRequest getUrlRequest) {
.serviceConfiguration(s3Configuration)
.build();

SdkHttpRequest httpRequest = S3EndpointResolverFactory.getEndpointResolver(getObjectRequest.bucket())
S3EndpointResolverFactoryContext resolverFactoryContext = S3EndpointResolverFactoryContext.builder()
.bucketName(getObjectRequest.bucket())
.originalRequest(getObjectRequest)
.build();

SdkHttpRequest httpRequest = S3EndpointResolverFactory.getEndpointResolver(resolverFactoryContext)
.applyEndpointConfiguration(resolverContext)
.sdkHttpRequest();

Expand Down
Expand Up @@ -24,6 +24,7 @@
import static software.amazon.awssdk.services.s3.internal.endpoints.S3EndpointUtils.removeFipsIfNeeded;

import java.net.URI;
import java.util.Optional;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.arns.Arn;
import software.amazon.awssdk.http.SdkHttpRequest;
Expand All @@ -34,6 +35,8 @@
import software.amazon.awssdk.services.s3.internal.resource.S3AccessPointBuilder;
import software.amazon.awssdk.services.s3.internal.resource.S3AccessPointResource;
import software.amazon.awssdk.services.s3.internal.resource.S3ArnConverter;
import software.amazon.awssdk.services.s3.internal.resource.S3ObjectLambdaEndpointBuilder;
import software.amazon.awssdk.services.s3.internal.resource.S3ObjectLambdaResource;
import software.amazon.awssdk.services.s3.internal.resource.S3OutpostAccessPointBuilder;
import software.amazon.awssdk.services.s3.internal.resource.S3OutpostResource;
import software.amazon.awssdk.services.s3.internal.resource.S3Resource;
Expand All @@ -48,6 +51,7 @@
public final class S3AccessPointEndpointResolver implements S3EndpointResolver {

private static final String S3_OUTPOSTS_NAME = "s3-outposts";
private static final String S3_OBJECT_LAMBDA_NAME = "s3-object-lambda";

private S3AccessPointEndpointResolver() {
}
Expand Down Expand Up @@ -85,8 +89,7 @@ public ConfiguredS3SdkHttpRequest applyEndpointConfiguration(S3EndpointResolverC
.build();

String signingServiceModification = s3EndpointResource.parentS3Resource()
.filter(r -> r instanceof S3OutpostResource)
.map(ignore -> S3_OUTPOSTS_NAME)
.flatMap(S3AccessPointEndpointResolver::resolveSigningService)
.orElse(null);

return ConfiguredS3SdkHttpRequest.builder()
Expand Down Expand Up @@ -175,6 +178,8 @@ private URI getUriForAccessPointResource(S3EndpointResolverContext context, Stri

if (isOutpostAccessPoint(s3EndpointResource)) {
return getOutpostAccessPointUri(context, arnRegion, clientPartitionMetadata, s3EndpointResource);
} else if (isObjectLambdaAccessPoint(s3EndpointResource)) {
return getObjectLambdaAccessPointUri(context, arnRegion, clientPartitionMetadata, s3EndpointResource);
}

boolean dualstackEnabled = isDualstackEnabled(context.serviceConfiguration());
Expand All @@ -196,6 +201,10 @@ private boolean isOutpostAccessPoint(S3AccessPointResource s3EndpointResource) {
return s3EndpointResource.parentS3Resource().filter(r -> r instanceof S3OutpostResource).isPresent();
}

private boolean isObjectLambdaAccessPoint(S3AccessPointResource s3EndpointResource) {
return s3EndpointResource.parentS3Resource().filter(r -> r instanceof S3ObjectLambdaResource).isPresent();
}

private URI getOutpostAccessPointUri(S3EndpointResolverContext context, String arnRegion,
PartitionMetadata clientPartitionMetadata, S3AccessPointResource s3EndpointResource) {
if (isDualstackEnabled(context.serviceConfiguration())) {
Expand All @@ -221,4 +230,35 @@ private URI getOutpostAccessPointUri(S3EndpointResolverContext context, String a
.toUri();
}

private URI getObjectLambdaAccessPointUri(S3EndpointResolverContext context, String arnRegion,
PartitionMetadata clientPartitionMetadata,
S3AccessPointResource s3EndpointResource) {
if (isDualstackEnabled(context.serviceConfiguration())) {
throw new IllegalArgumentException("An Object Lambda Access Point ARN cannot be passed as a bucket parameter to "
+ "an S3 operation if the S3 client has been configured with dualstack.");
}

return S3ObjectLambdaEndpointBuilder.create()
.endpointOverride(context.endpointOverride())
.accountId(s3EndpointResource.accountId().get())
.region(arnRegion)
.accessPointName(s3EndpointResource.accessPointName())
.protocol(context.request().protocol())
.fipsEnabled(isFipsRegion(context.region().toString()))
.dualstackEnabled(isDualstackEnabled(context.serviceConfiguration()))
.domain(clientPartitionMetadata.dnsSuffix())
.toUri();
}

private static Optional<String> resolveSigningService(S3Resource resource) {
if (resource instanceof S3OutpostResource) {
return Optional.of(S3_OUTPOSTS_NAME);
}

if (resource instanceof S3ObjectLambdaResource) {
return Optional.of(S3_OBJECT_LAMBDA_NAME);
}

return Optional.empty();
}
}
Expand Up @@ -33,13 +33,15 @@ public final class S3EndpointResolverContext {
private final Region region;
private final S3Configuration serviceConfiguration;
private final URI endpointOverride;
private final boolean disableHostPrefixInjection;

private S3EndpointResolverContext(Builder builder) {
this.request = builder.request;
this.originalRequest = builder.originalRequest;
this.region = builder.region;
this.serviceConfiguration = builder.serviceConfiguration;
this.endpointOverride = builder.endpointOverride;
this.disableHostPrefixInjection = builder.disableHostPrefixInjection;
}

public static Builder builder() {
Expand All @@ -66,6 +68,9 @@ public URI endpointOverride() {
return endpointOverride;
}

public boolean isDisableHostPrefixInjection() {
return disableHostPrefixInjection;
}

@Override
public boolean equals(Object o) {
Expand All @@ -80,7 +85,8 @@ public boolean equals(Object o) {
Objects.equals(request, that.request) &&
Objects.equals(originalRequest, that.originalRequest) &&
Objects.equals(region, that.region) &&
Objects.equals(serviceConfiguration, that.serviceConfiguration);
Objects.equals(serviceConfiguration, that.serviceConfiguration) &&
Objects.equals(disableHostPrefixInjection, that.disableHostPrefixInjection);
}

@Override
Expand All @@ -91,6 +97,7 @@ public int hashCode() {
hashCode = 31 * hashCode + Objects.hashCode(region());
hashCode = 31 * hashCode + Objects.hashCode(serviceConfiguration());
hashCode = 31 * hashCode + Objects.hashCode(endpointOverride());
hashCode = 31 * hashCode + Objects.hashCode(isDisableHostPrefixInjection());
return hashCode;
}

Expand All @@ -108,6 +115,7 @@ public static final class Builder {
private Region region;
private S3Configuration serviceConfiguration;
private URI endpointOverride;
private boolean disableHostPrefixInjection;

private Builder() {
}
Expand Down Expand Up @@ -137,6 +145,11 @@ public Builder endpointOverride(URI endpointOverride) {
return this;
}

public Builder disableHostPrefixInjection(boolean disableHostPrefixInjection) {
this.disableHostPrefixInjection = disableHostPrefixInjection;
return this;
}

public S3EndpointResolverContext build() {
return new S3EndpointResolverContext(this);
}
Expand Down
Expand Up @@ -15,7 +15,10 @@

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

import java.util.Optional;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.services.s3.model.S3Request;
import software.amazon.awssdk.services.s3.model.WriteGetObjectResponseRequest;

/**
* Get endpoint resolver.
Expand All @@ -25,14 +28,25 @@ public final class S3EndpointResolverFactory {

private static final S3EndpointResolver ACCESS_POINT_ENDPOINT_RESOLVER = S3AccessPointEndpointResolver.create();
private static final S3EndpointResolver BUCKET_ENDPOINT_RESOLVER = S3BucketEndpointResolver.create();
private static final S3EndpointResolver OBJECT_LAMBDA_OPERATION_RESOLVER = S3ObjectLambdaOperationEndpointResolver.create();

private S3EndpointResolverFactory() {
}

public static S3EndpointResolver getEndpointResolver(String bucketName) {
if (bucketName != null && S3EndpointUtils.isArn(bucketName)) {
public static S3EndpointResolver getEndpointResolver(S3EndpointResolverFactoryContext context) {
Optional<String> bucketName = context.bucketName();
if (bucketName.isPresent() && S3EndpointUtils.isArn(bucketName.get())) {
return ACCESS_POINT_ENDPOINT_RESOLVER;
}

if (isObjectLambdaRequest(context.originalRequest())) {
return OBJECT_LAMBDA_OPERATION_RESOLVER;
}

return BUCKET_ENDPOINT_RESOLVER;
}

private static boolean isObjectLambdaRequest(S3Request request) {
return request instanceof WriteGetObjectResponseRequest;
}
}
@@ -0,0 +1,73 @@
/*
* 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.s3.internal.endpoints;

import java.util.Optional;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.services.s3.model.S3Request;

@SdkInternalApi
public final class S3EndpointResolverFactoryContext {
private final String bucketName;
private final S3Request originalRequest;

private S3EndpointResolverFactoryContext(DefaultBuilder builder) {
this.bucketName = builder.bucketName;
this.originalRequest = builder.originalRequest;
}

public Optional<String> bucketName() {
return Optional.ofNullable(bucketName);
}

public S3Request originalRequest() {
return originalRequest;
}

public static Builder builder() {
return new DefaultBuilder();
}

public interface Builder {
Builder bucketName(String bucketName);

Builder originalRequest(S3Request originalRequest);

S3EndpointResolverFactoryContext build();
}

private static final class DefaultBuilder implements Builder {
private String bucketName;
private S3Request originalRequest;

@Override
public Builder bucketName(String bucketName) {
this.bucketName = bucketName;
return this;
}

@Override
public Builder originalRequest(S3Request originalRequest) {
this.originalRequest = originalRequest;
return this;
}

@Override
public S3EndpointResolverFactoryContext build() {
return new S3EndpointResolverFactoryContext(this);
}
}
}
@@ -0,0 +1,75 @@
/*
* 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.s3.internal.endpoints;

import java.net.URI;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.utils.Validate;

/**
* Endpoint builder for operations specific to S3 Object Lambda.
*/
@SdkInternalApi
public class S3ObjectLambdaOperationEndpointBuilder {
private String region;
private String protocol;
private String domain;

private S3ObjectLambdaOperationEndpointBuilder() {
}

/**
* Create a new instance of this builder class.
*/
public static S3ObjectLambdaOperationEndpointBuilder create() {
return new S3ObjectLambdaOperationEndpointBuilder();
}


public S3ObjectLambdaOperationEndpointBuilder region(String region) {
this.region = region;
return this;
}

public S3ObjectLambdaOperationEndpointBuilder protocol(String protocol) {
this.protocol = protocol;
return this;
}

public S3ObjectLambdaOperationEndpointBuilder domain(String domain) {
this.domain = domain;
return this;
}

/**
* Generate an endpoint URI with no path that maps to the Object Lambdas Access Point information stored in this builder.
*/
public URI toUri() {
Validate.paramNotBlank(protocol, "protocol");
Validate.paramNotBlank(domain, "domain");
Validate.paramNotBlank(region, "region");

String servicePrefix = "s3-object-lambda";

String uriString = String.format("%s://%s.%s.%s",
protocol,
servicePrefix,
region,
domain);

return URI.create(uriString);
}
}

0 comments on commit a5a0405

Please sign in to comment.