AmazonsS3Client抛出UnknownHostException,如果试图连接到本地服务(MinIO).<



简而言之:使用AmazonS3Client连接到MinIO的本地实例导致UnknownHostException抛出,因为url被解析为http://{bucket_name}.localhost:port


问题的详细描述:

我正在为一个Java服务创建一个集成测试,该服务使用AmazonS3Client库从S3检索内容。我在测试容器中使用MinIO来执行Amazon S3的角色,如下所示:

@Container
static final GenericContainer<?> minioContainer = new GenericContainer<>("minio/minio:latest")
.withCommand("server /data")
.withEnv(
Map.of(
"MINIO_ACCESS_KEY", AWS_ACCESS_KEY.getValue(),
"MINIO_SECRET_KEY", AWS_SECRET_KEY.getValue()
)
)
.withExposedPorts(MINIO_PORT)
.waitingFor(new HttpWaitStrategy()
.forPath("/minio/health/ready")
.forPort(MINIO_PORT)
.withStartupTimeout(Duration.ofSeconds(10)));

,然后我动态地导出它的url(因为测试容器部署在一个随机端口),使用如下内容:

String.format("http://%s:%s", minioContainer.getHost(), minioContainer.getFirstMappedPort())

会得到如下url:

http://localhost:54123

我在测试运行时遇到的问题在于AmazonS3Client.getObject(String,String)的实际实现-在创建请求时,它执行以下验证(类S3RequestEndpointResolver,方法resolveRequestEndpoint):

...    
if (shouldUseVirtualAddressing(endpoint)) {
request.setEndpoint(convertToVirtualHostEndpoint(endpoint, bucketName));
request.setResourcePath(SdkHttpUtils.urlEncode(getHostStyleResourcePath(), true));
} else {
request.setEndpoint(endpoint);
request.setResourcePath(SdkHttpUtils.urlEncode(getPathStyleResourcePath(), true));
}
}
private boolean shouldUseVirtualAddressing(final URI endpoint) {
return !isPathStyleAccess && BucketNameUtils.isDNSBucketName(bucketName)
&& !isValidIpV4Address(endpoint.getHost());
}

这反过来返回urlhttp://localhost:54123true,因此这个方法

private static URI convertToVirtualHostEndpoint(URI endpoint, String bucketName) {
try {
return new URI(String.format("%s://%s.%s", endpoint.getScheme(), bucketName, endpoint.getAuthority()));
} catch (URISyntaxException e) {
throw new IllegalArgumentException("Invalid bucket name: " + bucketName, e);
}
}

将bucket的名称连接到主机,产生:http://mybucket.localhost:54123,这最终导致抛出UnknownHostException。我可以通过将主机设置为0.0.0.0而不是localhost来解决这个问题,但这几乎不是一个解决方案。

因此我想知道如果I)这是一个bug/限制在AmazonS3Client?ii)我是那个错过了一些东西的人,例如配置不良?

感谢您的宝贵时间

我找到了一个解决方案。查看解析器使用的方法:

private boolean shouldUseVirtualAddressing(final URI endpoint) {
return !isPathStyleAccess && BucketNameUtils.isDNSBucketName(bucketName)
&& !isValidIpV4Address(endpoint.getHost());
}

返回true并导致流到错误的连接,我发现我们可以在构建客户端时设置第一个变量isPathStyleAccess。在我的例子中,我在测试配置中创建了一个bean来覆盖主bean:

@Bean
@Primary
public AmazonS3 amazonS3() {
return AmazonS3Client.builder()
.withPathStyleAccessEnabled(true) //HERE
.withCredentials(new AWSStaticCredentialsProvider(
new BasicAWSCredentials(AWS_ACCESS_KEY.getValue(), AWS_SECRET_KEY.getValue())
))
.withEndpointConfiguration(
new AwsClientBuilder.EndpointConfiguration(s3Endpoint, region)
)
.build();

}

对于SDK V2,解决方案非常相似:

S3AsyncClient s3 = S3AsyncClient.builder()
.forcePathStyle(true) // adding this one
.endpointOverride(new URI(s3Endpoint))
.credentialsProvider(() -> AwsBasicCredentials.create(s3Properties.getAccessKey(), s3Properties.getSecretKey()))
.build()

最新更新