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.
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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() {
}
}
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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();
}
}
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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;
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading

0 comments on commit a5a0405

Please sign in to comment.