This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-net.git
commit 3b23d97d94dcc8abdb1c505ab7690bc7db3b316c Author: Gary Gregory <garydgreg...@gmail.com> AuthorDate: Mon May 27 12:44:37 2024 -0400 Add more tests Refactor internals --- .../java/org/apache/commons/net/util/Base64.java | 38 ++++-- .../org/apache/commons/net/util/Base64Test.java | 129 ++++++++++++++++----- 2 files changed, 128 insertions(+), 39 deletions(-) diff --git a/src/main/java/org/apache/commons/net/util/Base64.java b/src/main/java/org/apache/commons/net/util/Base64.java index 4531bb31..aa1730c4 100644 --- a/src/main/java/org/apache/commons/net/util/Base64.java +++ b/src/main/java/org/apache/commons/net/util/Base64.java @@ -19,6 +19,8 @@ package org.apache.commons.net.util; import java.math.BigInteger; import java.nio.charset.StandardCharsets; +import java.util.Base64.Decoder; +import java.util.Base64.Encoder; import java.util.Objects; /** @@ -230,9 +232,15 @@ public class Base64 { * * @param binaryData binary data to encode * @return Base64 characters chunked in 76 character blocks + * @throws ArithmeticException if the {@code binaryData} would overflows a byte[]. */ public static byte[] encodeBase64Chunked(final byte[] binaryData) { - return encodeBase64(binaryData, true); + final long encodeLength = getEncodeLength(binaryData, CHUNK_SIZE, CHUNK_SEPARATOR); + final byte[] dst = new byte[Math.toIntExact(encodeLength)]; + getMimeEncoder().encode(binaryData, dst); + dst[dst.length - 2] = CHUNK_SEPARATOR[0]; + dst[dst.length - 1] = CHUNK_SEPARATOR[1]; + return dst; } /** @@ -249,7 +257,7 @@ public class Base64 { * @since 1.4 */ public static String encodeBase64String(final byte[] binaryData) { - return java.util.Base64.getMimeEncoder().encodeToString(binaryData) + "\r\n"; + return getMimeEncoder().encodeToString(binaryData) + "\r\n"; } /** @@ -275,7 +283,7 @@ public class Base64 { * @since 3.2 */ public static String encodeBase64StringUnChunked(final byte[] binaryData) { - return newStringUtf8(encodeBase64(binaryData, false)); + return getEncoder().encodeToString(binaryData); } /** @@ -314,19 +322,23 @@ public class Base64 { return encodeBase64(toIntegerBytes(bigInt), false); } + private static Decoder getDecoder() { + return java.util.Base64.getDecoder(); + } + /** * Pre-calculates the amount of space needed to base64-encode the supplied array. * - * @param pArray byte[] array which will later be encoded + * @param array byte[] array which will later be encoded * @param chunkSize line-length of the output (<= 0 means no chunking) between each chunkSeparator (e.g. CRLF). * @param chunkSeparator the sequence of bytes used to separate chunks of output (e.g. CRLF). * * @return amount of space needed to encode the supplied array. Returns a long since a max-len array will require Integer.MAX_VALUE + 33%. */ - private static long getEncodeLength(final byte[] pArray, int chunkSize, final byte[] chunkSeparator) { + static long getEncodeLength(final byte[] array, int chunkSize, final byte[] chunkSeparator) { // base64 always encodes to multiples of 4. chunkSize = chunkSize / 4 * 4; - long len = pArray.length * 4 / 3; + long len = array.length * 4 / 3; final long mod = len % 4; if (mod != 0) { len += 4 - mod; @@ -341,6 +353,14 @@ public class Base64 { return len; } + private static Encoder getEncoder() { + return java.util.Base64.getEncoder(); + } + + private static Encoder getMimeEncoder() { + return java.util.Base64.getMimeEncoder(); + } + /** * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet. Currently, the method treats whitespace as valid. * @@ -395,7 +415,7 @@ public class Base64 { * @param bigInt <code>BigInteger</code> to be converted * @return a byte array representation of the BigInteger parameter */ - static byte[] toIntegerBytes(final BigInteger bigInt) { + private static byte[] toIntegerBytes(final BigInteger bigInt) { Objects.requireNonNull(bigInt, "bigInt"); int bitlen = bigInt.bitLength(); // round bitlen @@ -611,7 +631,7 @@ public class Base64 { if (source == null || source.length == 0) { return source; } - return java.util.Base64.getDecoder().decode(source); + return getDecoder().decode(source); } /** @@ -622,7 +642,7 @@ public class Base64 { * @since 1.4 */ public byte[] decode(final String source) { - return java.util.Base64.getDecoder().decode(source); + return getDecoder().decode(source); } /** diff --git a/src/test/java/org/apache/commons/net/util/Base64Test.java b/src/test/java/org/apache/commons/net/util/Base64Test.java index 475007cf..45588677 100644 --- a/src/test/java/org/apache/commons/net/util/Base64Test.java +++ b/src/test/java/org/apache/commons/net/util/Base64Test.java @@ -39,14 +39,14 @@ public class Base64Test { private void checkDecoders(final String expected, final byte[] actual) { final byte[] decoded = Base64.decodeBase64(actual); - assertEquals(expected, new String(decoded, StandardCharsets.UTF_8)); + assertEquals(expected, toString(decoded)); assertEquals(expected, new String(getJreDecoder().decode(actual), StandardCharsets.UTF_8)); } private void checkDecoders(final String expected, final String actual) { final byte[] decoded = Base64.decodeBase64(actual); assertEquals(expected, new String(decoded)); - assertEquals(expected, new String(decoded, StandardCharsets.UTF_8)); + assertEquals(expected, toString(decoded)); assertEquals(expected, new String(getJreDecoder().decode(actual), StandardCharsets.UTF_8)); } @@ -58,6 +58,10 @@ public class Base64Test { return java.util.Base64.getEncoder(); } + private Encoder getJreMimeEncoder() { + return java.util.Base64.getMimeEncoder(); + } + @Test public void testBase64() { final Base64 b64 = new Base64(); @@ -135,12 +139,6 @@ public class Base64Test { assertEquals(bi, Base64.decodeInteger(getJreEncoder().encode(bi.toByteArray()))); } - private void testEncodeInteger(final BigInteger bi) { - final byte[] decodedBytes = getJreDecoder().decode(Base64.encodeInteger(bi)); - final BigInteger decoded = decodedBytes.length == 0 ? BigInteger.ZERO : new BigInteger(decodedBytes); - assertEquals(bi, decoded); - } - @Test public void testDecodeNullString() { final Base64 base64 = new Base64(); @@ -152,28 +150,54 @@ public class Base64Test { checkDecoders("Hello World!", "SGVsbG8gV29ybGQh"); } - @Test - public void testEncodeBase64ByteArray() { - final byte[] binaryData = null; - assertArrayEquals(binaryData, Base64.encodeBase64(binaryData)); - } - @Test public void testEncodeBase64ByteArrayBoolean() { final byte[] binaryData = { '1', '2', '3' }; + final byte[] urlUnsafeData = "<<???>>".getBytes(); + final byte[] urlUnsafeDataChunky = "<<???>><<???>><<???>><<???>><<???>><<???>><<???>><<???>><<???>><<???>><<???>>".getBytes(); byte[] encoded; + // Boolean parameter: "isChunked". + // + // isChunked false encoded = Base64.encodeBase64(binaryData, false); assertNotNull(encoded); assertEquals(4, encoded.length); + assertEquals(Base64.getEncodeLength(binaryData, Base64.CHUNK_SIZE, Base64.CHUNK_SEPARATOR) - 2, encoded.length); + assertEquals(getJreEncoder().encodeToString(binaryData), toString(encoded)); + assertEquals("MTIz", toString(encoded)); + // URL unsafe + // <<???>> + encoded = Base64.encodeBase64(urlUnsafeData, false); + assertEquals("PDw/Pz8+Pg==", toString(encoded)); + encoded = Base64.encodeBase64(urlUnsafeDataChunky, false); + assertEquals(getJreEncoder().encodeToString(urlUnsafeDataChunky), toString(encoded)); + // + // isChunked false encoded = Base64.encodeBase64(binaryData, false); assertNotNull(encoded); assertEquals(4, encoded.length); + assertEquals(Base64.getEncodeLength(binaryData, Base64.CHUNK_SIZE, Base64.CHUNK_SEPARATOR) - 2, encoded.length); + assertEquals("MTIz", toString(encoded)); + // + // isChunked true encoded = Base64.encodeBase64(binaryData, true); assertNotNull(encoded); assertEquals(6, encoded.length); // always adds trailer + assertEquals(Base64.getEncodeLength(binaryData, Base64.CHUNK_SIZE, Base64.CHUNK_SEPARATOR), encoded.length); + assertEquals("MTIz\r\n", toString(encoded)); + // URL unsafe + // <<???>> + encoded = Base64.encodeBase64(urlUnsafeData, true); + assertEquals("PDw/Pz8+Pg==\r\n", toString(encoded)); + encoded = Base64.encodeBase64(urlUnsafeDataChunky, true); + assertEquals(getJreMimeEncoder().encodeToString(urlUnsafeDataChunky) + "\r\n", toString(encoded)); + // + // isChunked true encoded = Base64.encodeBase64(binaryData, true); assertNotNull(encoded); assertEquals(6, encoded.length); + assertEquals(Base64.getEncodeLength(binaryData, Base64.CHUNK_SIZE, Base64.CHUNK_SEPARATOR), encoded.length); + assertEquals("MTIz\r\n", toString(encoded)); } @Test @@ -214,30 +238,48 @@ public class Base64Test { assertEquals(6, encoded.length); } + @Test + public void testEncodeBase64ByteArrayEdges() { + final byte[] binaryData = null; + assertArrayEquals(binaryData, Base64.encodeBase64(binaryData)); + final byte[] binaryData2 = new byte[0]; + assertArrayEquals(binaryData2, Base64.encodeBase64(binaryData2)); + } + @Test public void testEncodeBase64Chunked() { - final byte[] bytesToEncode = { 'f', 'o', 'o', 'b', 'a', 'r' }; - final byte[] encodedData = Base64.encodeBase64Chunked(bytesToEncode); - assertEquals("Zm9vYmFy\r\n", new String(encodedData, StandardCharsets.UTF_8)); + byte[] bytesToEncode = { 'f', 'o', 'o', 'b', 'a', 'r' }; + byte[] encodedData = Base64.encodeBase64Chunked(bytesToEncode); + assertEquals("Zm9vYmFy\r\n", toString(encodedData)); + // URL unsafe data + // <<???>> + bytesToEncode = "<<???>>".getBytes(); + encodedData = Base64.encodeBase64Chunked(bytesToEncode); + assertEquals("PDw/Pz8+Pg==\r\n", toString(encodedData)); // > 76 final byte[] chunkMe = ArrayFill.fill(new byte[Base64.CHUNK_SIZE * 2], (byte) 'A'); final byte[] chunked = Base64.encodeBase64Chunked(chunkMe); assertEquals('\r', chunked[chunked.length - 2]); assertEquals('\n', chunked[chunked.length - 1]); - assertArrayEquals(ArrayUtils.addAll(java.util.Base64.getMimeEncoder().encode(chunkMe), Base64.CHUNK_SEPARATOR), chunked); + assertArrayEquals(ArrayUtils.addAll(getJreMimeEncoder().encode(chunkMe), Base64.CHUNK_SEPARATOR), chunked); } @Test public void testEncodeBase64StringByteArray() { - final String stringToEncode = "Many hands make light work."; - final String encodedData = Base64.encodeBase64String(stringToEncode.getBytes()); + String stringToEncode = "Many hands make light work."; + String encodedData = Base64.encodeBase64String(stringToEncode.getBytes()); assertEquals("TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu\r\n", encodedData); + // URL unsafe data + // <<???>> + stringToEncode = "<<???>>"; + encodedData = Base64.encodeBase64String(stringToEncode.getBytes()); + assertEquals("PDw/Pz8+Pg==\r\n", encodedData); // > 76 final byte[] chunkMe = ArrayFill.fill(new byte[Base64.CHUNK_SIZE * 2], (byte) 'A'); final String chunked = Base64.encodeBase64String(chunkMe); assertEquals('\r', chunked.charAt(chunked.length() - 2)); assertEquals('\n', chunked.charAt(chunked.length() - 1)); - assertEquals(java.util.Base64.getMimeEncoder().encodeToString(chunkMe) + "\r\n", chunked); + assertEquals(getJreMimeEncoder().encodeToString(chunkMe) + "\r\n", chunked); } @Test @@ -251,25 +293,42 @@ public class Base64Test { @Test public void testEncodeBase64StringUnChunked() { - final byte[] bytesToEncode = "Many hands make light work.".getBytes(); - final String encodedData = Base64.encodeBase64StringUnChunked(bytesToEncode); + byte[] bytesToEncode = "Many hands make light work.".getBytes(); + String encodedData = Base64.encodeBase64StringUnChunked(bytesToEncode); assertEquals("TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu", encodedData); + // URL unsafe data + // <<???>> + bytesToEncode = "<<???>>".getBytes(); + encodedData = Base64.encodeBase64StringUnChunked(bytesToEncode); + assertEquals("PDw/Pz8+Pg==", encodedData); + // > 76 + final byte[] chunkMe = ArrayFill.fill(new byte[Base64.CHUNK_SIZE * 2], (byte) 'A'); + final String chunked = Base64.encodeBase64StringUnChunked(chunkMe); + assertEquals(getJreEncoder().encodeToString(chunkMe), chunked); } @Test public void testEncodeBase64URLSafe() { - final byte[] bytesToEncode = "Many hands make light work.".getBytes(); - final byte[] encodedData = Base64.encodeBase64URLSafe(bytesToEncode); - assertEquals("TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu", new String(encodedData, StandardCharsets.UTF_8)); - // TODO more + byte[] bytesToEncode = "Many hands make light work.".getBytes(); + byte[] encodedData = Base64.encodeBase64URLSafe(bytesToEncode); + assertEquals("TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu", toString(encodedData)); + // URL unsafe data + // <<???>> + bytesToEncode = "<<???>>".getBytes(); + encodedData = Base64.encodeBase64URLSafe(bytesToEncode); + assertEquals("PDw_Pz8-Pg", toString(encodedData)); } @Test public void testEncodeBase64URLSafeString() { - final byte[] bytesToEncode = "Many hands make light work.".getBytes(); - final String encodedData = Base64.encodeBase64URLSafeString(bytesToEncode); + byte[] bytesToEncode = "Many hands make light work.".getBytes(); + String encodedData = Base64.encodeBase64URLSafeString(bytesToEncode); assertEquals("TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu", encodedData); - // TODO more + // URL unsafe data + // <<???>> + bytesToEncode = "<<???>>".getBytes(); + encodedData = Base64.encodeBase64URLSafeString(bytesToEncode); + assertEquals("PDw_Pz8-Pg", encodedData); } @Test @@ -286,6 +345,12 @@ public class Base64Test { testEncodeInteger(BigInteger.ZERO); } + private void testEncodeInteger(final BigInteger bi) { + final byte[] decodedBytes = getJreDecoder().decode(Base64.encodeInteger(bi)); + final BigInteger decoded = decodedBytes.length == 0 ? BigInteger.ZERO : new BigInteger(decodedBytes); + assertEquals(bi, decoded); + } + @Test public void testEncodeToString() { final Base64 base64 = new Base64(); @@ -305,4 +370,8 @@ public class Base64Test { assertFalse(Base64.isBase64((byte) ' ')); } + private String toString(byte[] encodedData) { + return new String(encodedData, StandardCharsets.UTF_8); + } + }