diff --git a/bindings/java/pom.xml b/bindings/java/pom.xml index 4eaa70059a6e..6536ec442594 100644 --- a/bindings/java/pom.xml +++ b/bindings/java/pom.xml @@ -63,6 +63,7 @@ 2.3.2 1.18.30 2.0.7 + 5.2 3.1.2 @@ -101,6 +102,11 @@ dotenv-java ${dotenv.version} + + org.apache.httpcomponents.client5 + httpclient5 + ${httpclient.version} + @@ -136,6 +142,11 @@ dotenv-java test + + org.apache.httpcomponents.client5 + httpclient5 + test + diff --git a/bindings/java/src/test/java/org/apache/opendal/test/behavior/AsyncPresignTest.java b/bindings/java/src/test/java/org/apache/opendal/test/behavior/AsyncPresignTest.java new file mode 100644 index 000000000000..4f2a33c59a4a --- /dev/null +++ b/bindings/java/src/test/java/org/apache/opendal/test/behavior/AsyncPresignTest.java @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License 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 org.apache.opendal.test.behavior; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assumptions.assumeTrue; +import java.io.IOException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.time.Duration; +import java.util.Map; +import java.util.UUID; +import org.apache.hc.client5.http.entity.EntityBuilder; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.HttpEntity; +import org.apache.hc.core5.http.HttpHeaders; +import org.apache.hc.core5.http.HttpStatus; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; +import org.apache.opendal.Capability; +import org.apache.opendal.Metadata; +import org.apache.opendal.PresignedRequest; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class AsyncPresignTest extends BehaviorTestBase { + + @BeforeAll + public void precondition() { + final Capability capability = op().info.fullCapability; + assumeTrue(capability.list && capability.write && capability.presign); + } + + /** + * Presign write should succeed. + */ + @Test + public void testPresignWrite() throws IOException { + final String path = UUID.randomUUID().toString(); + final byte[] content = generateBytes(); + + final PresignedRequest signedReq = + op().presignWrite(path, Duration.ofSeconds(3600)).join(); + + try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + final HttpEntity body = EntityBuilder.create().setBinary(content).build(); + final ClassicRequestBuilder builder = + createRequestBuilder(signedReq).setEntity(body); + + httpclient.execute(builder.build(), rsp -> rsp); + } + + final Metadata meta = op().stat(path).join(); + assertEquals(content.length, meta.getContentLength()); + op().delete(path).join(); + } + + /** + * Presign stat should succeed. + */ + @Test + public void testPresignStat() throws IOException { + final String path = UUID.randomUUID().toString(); + final byte[] content = generateBytes(); + op().write(path, content).join(); + + final PresignedRequest signedReq = + op().presignStat(path, Duration.ofSeconds(3600)).join(); + try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + final ClassicRequestBuilder builder = createRequestBuilder(signedReq); + + final ClassicHttpResponse response = httpclient.execute(builder.build(), rsp -> rsp); + assertEquals(HttpStatus.SC_OK, response.getCode()); + assertEquals( + String.valueOf(content.length), + response.getFirstHeader(HttpHeaders.CONTENT_LENGTH).getValue()); + } + + op().delete(path).join(); + } + + /** + * Presign read should read content successfully. + */ + @Test + public void testPresignRead() throws IOException, NoSuchAlgorithmException { + final String path = UUID.randomUUID().toString(); + final byte[] content = generateBytes(); + op().write(path, content).join(); + + final PresignedRequest signedReq = + op().presignRead(path, Duration.ofSeconds(3600)).join(); + try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + final ClassicRequestBuilder builder = createRequestBuilder(signedReq); + + final byte[] responseContent = httpclient.execute(builder.build(), rsp -> { + return EntityUtils.toByteArray(rsp.getEntity()); + }); + assertEquals(content.length, responseContent.length); + + final MessageDigest digest = MessageDigest.getInstance("SHA-256"); + assertArrayEquals(digest.digest(content), digest.digest(responseContent)); + } + + op().delete(path).join(); + } + + private ClassicRequestBuilder createRequestBuilder(final PresignedRequest signedReq) { + final ClassicRequestBuilder builder = + ClassicRequestBuilder.create(signedReq.getMethod()).setUri(signedReq.getUri()); + for (Map.Entry entry : signedReq.getHeaders().entrySet()) { + builder.addHeader(entry.getKey(), entry.getValue()); + } + return builder; + } +}