Author: mattryan Date: Sat Mar 2 02:42:53 2019 New Revision: 1854622 URL: http://svn.apache.org/viewvc?rev=1854622&view=rev Log: OAK-8013: Disable filename* part of Content-Disposition specification for direct binary access.
Modified: jackrabbit/oak/branches/1.10/oak-blob-plugins/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/DataRecordDownloadOptions.java jackrabbit/oak/branches/1.10/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/AbstractDataRecordAccessProviderTest.java jackrabbit/oak/branches/1.10/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/DataRecordDownloadOptionsTest.java Modified: jackrabbit/oak/branches/1.10/oak-blob-plugins/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/DataRecordDownloadOptions.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.10/oak-blob-plugins/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/DataRecordDownloadOptions.java?rev=1854622&r1=1854621&r2=1854622&view=diff ============================================================================== --- jackrabbit/oak/branches/1.10/oak-blob-plugins/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/DataRecordDownloadOptions.java (original) +++ jackrabbit/oak/branches/1.10/oak-blob-plugins/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/DataRecordDownloadOptions.java Sat Mar 2 02:42:53 2019 @@ -20,9 +20,11 @@ package org.apache.jackrabbit.oak.plugins.blob.datastore.directaccess; import java.nio.charset.StandardCharsets; +import java.util.Set; import com.google.common.base.Joiner; import com.google.common.base.Strings; +import com.google.common.collect.Sets; import org.apache.jackrabbit.oak.api.blob.BlobDownloadOptions; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -35,6 +37,16 @@ public class DataRecordDownloadOptions { static final String DISPOSITION_TYPE_INLINE = "inline"; static final String DISPOSITION_TYPE_ATTACHMENT = "attachment"; + private static final char[] hex = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + private static final Set<Character> rfc5987AllowedChars = Sets.newHashSet( + '0','1','2','3','4','5','6','7','8','9', + 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', + 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z', + '!','#','$','&','+','-','.','^','_','`','|','~' + ); + /** * Create an instance of this class directly from a {@link * BlobDownloadOptions} instance. @@ -126,11 +138,7 @@ public class DataRecordDownloadOptions { if (Strings.isNullOrEmpty(dispositionType)) { dispositionType = DISPOSITION_TYPE_INLINE; } - contentDispositionHeader = - String.format("%s; filename=\"%s\"; filename*=UTF-8''%s", - dispositionType, fileName, - new String(fileName.getBytes(StandardCharsets.UTF_8)) - ); + contentDispositionHeader = formatContentDispositionHeader(dispositionType, fileName, null); } else if (DISPOSITION_TYPE_ATTACHMENT.equals(this.dispositionType)) { contentDispositionHeader = DISPOSITION_TYPE_ATTACHMENT; @@ -139,6 +147,31 @@ public class DataRecordDownloadOptions { return contentDispositionHeader; } + private String formatContentDispositionHeader(@NotNull final String dispositionType, + @NotNull final String fileName, + @Nullable final String rfc8187EncodedFileName) { + return null != rfc8187EncodedFileName ? + String.format("%s; filename=\"%s\"; filename*=UTF-8''%s", dispositionType, fileName, rfc8187EncodedFileName) : + String.format("%s; filename=\"%s\"", dispositionType, fileName); + } + + private String rfc8187Encode(@NotNull final String input) { + byte[] bytes = input.getBytes(StandardCharsets.UTF_8); + StringBuilder sb = new StringBuilder(); + for (byte b : bytes) { + char c = (char) b; + if (rfc5987AllowedChars.contains(c)) { + sb.append(c); + } + else { + sb.append('%'); + sb.append(hex[0x0F & (b >>> 4)]); + sb.append(hex[b & 0x0F]); + } + } + return sb.toString(); + } + /** * Returns the media type of this instance. * Modified: jackrabbit/oak/branches/1.10/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/AbstractDataRecordAccessProviderTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.10/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/AbstractDataRecordAccessProviderTest.java?rev=1854622&r1=1854621&r2=1854622&view=diff ============================================================================== --- jackrabbit/oak/branches/1.10/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/AbstractDataRecordAccessProviderTest.java (original) +++ jackrabbit/oak/branches/1.10/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/AbstractDataRecordAccessProviderTest.java Sat Mar 2 02:42:53 2019 @@ -18,12 +18,20 @@ */ package org.apache.jackrabbit.oak.plugins.blob.datastore.directaccess; +import static com.google.common.io.ByteStreams.toByteArray; +import static org.apache.jackrabbit.oak.plugins.blob.datastore.DataStoreUtils.randomStream; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Map; @@ -43,15 +51,6 @@ import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static com.google.common.io.ByteStreams.toByteArray; -import static org.apache.jackrabbit.oak.plugins.blob.datastore.DataStoreUtils.randomStream; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - public abstract class AbstractDataRecordAccessProviderTest { protected static final Logger LOG = LoggerFactory.getLogger(AbstractDataRecordAccessProviderTest.class); @@ -150,6 +149,7 @@ public abstract class AbstractDataRecord record = doSynchronousAddRecord((DataStore) dataStore, testStream); String mimeType = "image/png"; String fileName = "album cover.png"; + String encodedFileName = "album%20cover.png"; String dispositionType = "inline"; DataRecordDownloadOptions downloadOptions = DataRecordDownloadOptions.fromBlobDownloadOptions( @@ -168,10 +168,19 @@ public abstract class AbstractDataRecord assertEquals(200, conn.getResponseCode()); assertEquals(mimeType, conn.getHeaderField("Content-Type")); +// This proper behavior is disabled due to https://github.com/Azure/azure-sdk-for-java/issues/2900 +// (see also https://issues.apache.org/jira/browse/OAK-8013). We can re-enable the full test +// once the issue is resolved. -MR +// assertEquals( +// String.format("%s; filename=\"%s\"; filename*=UTF-8''%s", +// dispositionType, fileName, +// new String(encodedFileName.getBytes(StandardCharsets.UTF_8)) +// ), +// conn.getHeaderField("Content-Disposition") +// ); assertEquals( - String.format("%s; filename=\"%s\"; filename*=UTF-8''%s", - dispositionType, fileName, - new String(fileName.getBytes(StandardCharsets.UTF_8)) + String.format("%s; filename=\"%s\"", + dispositionType, fileName ), conn.getHeaderField("Content-Disposition") ); Modified: jackrabbit/oak/branches/1.10/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/DataRecordDownloadOptionsTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.10/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/DataRecordDownloadOptionsTest.java?rev=1854622&r1=1854621&r2=1854622&view=diff ============================================================================== --- jackrabbit/oak/branches/1.10/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/DataRecordDownloadOptionsTest.java (original) +++ jackrabbit/oak/branches/1.10/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/DataRecordDownloadOptionsTest.java Sat Mar 2 02:42:53 2019 @@ -130,8 +130,12 @@ public class DataRecordDownloadOptionsTe dispositionType = DISPOSITION_TYPE_INLINE; } String fileNameStar = new String(fileName.getBytes(StandardCharsets.UTF_8)); - return String.format("%s; filename=\"%s\"; filename*=UTF-8''%s", - dispositionType, fileName, fileNameStar); +// This proper behavior is disabled due to https://github.com/Azure/azure-sdk-for-java/issues/2900 +// (see also https://issues.apache.org/jira/browse/OAK-8013). We can re-enable the full test +// once the issue is resolved. -MR +// return String.format("%s; filename=\"%s\"; filename*=UTF-8''%s", +// dispositionType, fileName, fileNameStar); + return String.format("%s; filename=\"%s\"", dispositionType, fileName); } @Test