1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.codec.binary;
19
20 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
21 import static org.junit.jupiter.api.Assertions.assertEquals;
22 import static org.junit.jupiter.api.Assertions.assertFalse;
23 import static org.junit.jupiter.api.Assertions.assertNotNull;
24 import static org.junit.jupiter.api.Assertions.assertThrows;
25 import static org.junit.jupiter.api.Assertions.assertTrue;
26
27 import java.io.BufferedReader;
28 import java.io.ByteArrayInputStream;
29 import java.io.ByteArrayOutputStream;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.InputStreamReader;
33
34 import org.apache.commons.codec.CodecPolicy;
35 import org.apache.commons.io.IOUtils;
36 import org.junit.jupiter.api.Test;
37
38
39
40
41 class Base64InputStreamTest {
42
43
44
45
46 private static final String ENCODED_B64 = "AAAA////";
47
48 private static final byte[] CRLF = { (byte) '\r', (byte) '\n' };
49
50 private static final byte[] LF = { (byte) '\n' };
51
52 private static final String STRING_FIXTURE = "Hello World";
53
54
55
56
57
58
59
60 @Test
61 void testAvailable() throws Throwable {
62 final InputStream ins = new ByteArrayInputStream(StringUtils.getBytesIso8859_1(ENCODED_B64));
63 try (Base64InputStream b64stream = new Base64InputStream(ins)) {
64 assertEquals(1, b64stream.available());
65 assertEquals(6, b64stream.skip(10));
66
67 assertEquals(0, b64stream.available());
68 assertEquals(-1, b64stream.read());
69 assertEquals(-1, b64stream.read());
70 assertEquals(0, b64stream.available());
71 }
72 }
73
74 private void testBase64EmptyInputStream(final int chuckSize) throws Exception {
75 final byte[] emptyEncoded = {};
76 final byte[] emptyDecoded = {};
77 testByteByByte(emptyEncoded, emptyDecoded, chuckSize, CRLF);
78 testByChunk(emptyEncoded, emptyDecoded, chuckSize, CRLF);
79 }
80
81
82
83
84
85
86
87 @Test
88 void testBase64EmptyInputStreamMimeChuckSize() throws Exception {
89 testBase64EmptyInputStream(BaseNCodec.MIME_CHUNK_SIZE);
90 }
91
92
93
94
95
96
97
98 @Test
99 void testBase64EmptyInputStreamPemChuckSize() throws Exception {
100 testBase64EmptyInputStream(BaseNCodec.PEM_CHUNK_SIZE);
101 }
102
103
104
105
106
107
108
109 @Test
110 void testBase64InputStreamByChunk() throws Exception {
111
112 byte[] encoded = StringUtils.getBytesUtf8("SGVsbG8gV29ybGQ=\r\n");
113 byte[] decoded = StringUtils.getBytesUtf8(STRING_FIXTURE);
114 testByChunk(encoded, decoded, BaseNCodec.MIME_CHUNK_SIZE, CRLF);
115
116
117 encoded = StringUtils.getBytesUtf8("AA==\r\n");
118 decoded = new byte[] { (byte) 0 };
119 testByChunk(encoded, decoded, BaseNCodec.MIME_CHUNK_SIZE, CRLF);
120
121
122 encoded = StringUtils.getBytesUtf8(Base64TestData.ENCODED_64_CHARS_PER_LINE);
123 decoded = BaseNTestData.DECODED;
124 testByChunk(encoded, decoded, BaseNCodec.PEM_CHUNK_SIZE, LF);
125
126
127 final String singleLine = Base64TestData.ENCODED_64_CHARS_PER_LINE.replace("\n", "");
128 encoded = StringUtils.getBytesUtf8(singleLine);
129 decoded = BaseNTestData.DECODED;
130 testByChunk(encoded, decoded, 0, LF);
131
132
133 final BaseNCodec codec = new Base64(0, null, false);
134 for (int i = 0; i <= 150; i++) {
135 final byte[][] randomData = BaseNTestData.randomData(codec, i);
136 encoded = randomData[1];
137 decoded = randomData[0];
138 testByChunk(encoded, decoded, 0, LF);
139 }
140 }
141
142
143
144
145
146
147
148 @Test
149 void testBase64InputStreamByteByByte() throws Exception {
150
151 byte[] encoded = StringUtils.getBytesUtf8("SGVsbG8gV29ybGQ=\r\n");
152 byte[] decoded = StringUtils.getBytesUtf8(STRING_FIXTURE);
153 testByteByByte(encoded, decoded, BaseNCodec.MIME_CHUNK_SIZE, CRLF);
154
155
156 encoded = StringUtils.getBytesUtf8("AA==\r\n");
157 decoded = new byte[] { (byte) 0 };
158 testByteByByte(encoded, decoded, BaseNCodec.MIME_CHUNK_SIZE, CRLF);
159
160
161 encoded = StringUtils.getBytesUtf8(Base64TestData.ENCODED_64_CHARS_PER_LINE);
162 decoded = BaseNTestData.DECODED;
163 testByteByByte(encoded, decoded, BaseNCodec.PEM_CHUNK_SIZE, LF);
164
165
166 final String singleLine = Base64TestData.ENCODED_64_CHARS_PER_LINE.replace("\n", "");
167 encoded = StringUtils.getBytesUtf8(singleLine);
168 decoded = BaseNTestData.DECODED;
169 testByteByByte(encoded, decoded, 0, LF);
170
171
172 final BaseNCodec codec = new Base64(0, null, false);
173 for (int i = 0; i <= 150; i++) {
174 final byte[][] randomData = BaseNTestData.randomData(codec, i);
175 encoded = randomData[1];
176 decoded = randomData[0];
177 testByteByByte(encoded, decoded, 0, LF);
178 }
179 }
180
181 @Test
182 void testBuilder() {
183 assertNotNull(Base64InputStream.builder().getBaseNCodec());
184 }
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204 private void testByChunk(final byte[] encoded, final byte[] decoded, final int chunkSize, final byte[] separator) throws Exception {
205
206 try (InputStream in = new Base64InputStream(new ByteArrayInputStream(decoded), true, chunkSize, separator)) {
207 final byte[] output = IOUtils.toByteArray(in);
208 assertEquals(-1, in.read(), "EOF");
209 assertEquals(-1, in.read(), "Still EOF");
210 assertArrayEquals(encoded, output, "Streaming base64 encode");
211 }
212
213
214 InputStream in = new Base64InputStream(new ByteArrayInputStream(encoded));
215 final InputStream in1 = in;
216 byte[] output = IOUtils.toByteArray(in1);
217
218 assertEquals(-1, in.read(), "EOF");
219 assertEquals(-1, in.read(), "Still EOF");
220 assertArrayEquals(decoded, output, "Streaming base64 decode");
221
222
223 in = new ByteArrayInputStream(decoded);
224 for (int i = 0; i < 10; i++) {
225 in = new Base64InputStream(in, true, chunkSize, separator);
226 in = new Base64InputStream(in, false);
227 }
228 final InputStream in2 = in;
229 output = IOUtils.toByteArray(in2);
230
231 assertEquals(-1, in.read(), "EOF");
232 assertEquals(-1, in.read(), "Still EOF");
233 assertArrayEquals(decoded, output, "Streaming base64 wrap-wrap-wrap!");
234 in.close();
235 }
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255 private void testByteByByte(final byte[] encoded, final byte[] decoded, final int chunkSize, final byte[] separator) throws Exception {
256 byte[] output = new byte[encoded.length];
257
258 try (InputStream in = new Base64InputStream(new ByteArrayInputStream(decoded), true, chunkSize, separator)) {
259 for (int i = 0; i < output.length; i++) {
260 output[i] = (byte) in.read();
261 }
262
263 assertEquals(-1, in.read(), "EOF");
264 assertEquals(-1, in.read(), "Still EOF");
265 assertArrayEquals(encoded, output, "Streaming base64 encode");
266
267 }
268
269 try (InputStream in = new Base64InputStream(new ByteArrayInputStream(encoded))) {
270 output = new byte[decoded.length];
271 for (int i = 0; i < output.length; i++) {
272 output[i] = (byte) in.read();
273 }
274
275 assertEquals(-1, in.read(), "EOF");
276 assertEquals(-1, in.read(), "Still EOF");
277 assertArrayEquals(decoded, output, "Streaming base64 decode");
278 }
279
280
281 InputStream in = new ByteArrayInputStream(decoded);
282 for (int i = 0; i < 10; i++) {
283 in = new Base64InputStream(in, true, chunkSize, separator);
284 in = new Base64InputStream(in, false);
285 }
286 output = new byte[decoded.length];
287 for (int i = 0; i < output.length; i++) {
288 output[i] = (byte) in.read();
289 }
290
291 assertEquals(-1, in.read(), "EOF");
292 assertEquals(-1, in.read(), "Still EOF");
293 assertArrayEquals(decoded, output, "Streaming base64 wrap-wrap-wrap!");
294 }
295
296
297
298
299
300
301
302 @Test
303 void testCodec101() throws Exception {
304 final byte[] codec101 = StringUtils.getBytesUtf8(Base64TestData.CODEC_101_INPUT_LENGTH_IS_MULTIPLE_OF_3);
305 final ByteArrayInputStream bais = new ByteArrayInputStream(codec101);
306 try (Base64InputStream in = new Base64InputStream(bais)) {
307 final byte[] result = new byte[8192];
308 int c = in.read(result);
309 assertTrue(c > 0, "Codec101: First read successful [c=" + c + "]");
310
311 c = in.read(result);
312 assertTrue(c < 0, "Codec101: Second read should report end-of-stream [c=" + c + "]");
313 }
314 }
315
316
317
318
319 @Test
320 void testCodec105() throws IOException {
321 try (Base64InputStream in = new Base64InputStream(new Codec105ErrorInputStream(), true, 0, null)) {
322 for (int i = 0; i < 5; i++) {
323 in.read();
324 }
325 }
326 }
327
328
329
330
331 @Test
332 void testCodec130() throws IOException {
333 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
334 try (Base64OutputStream base64os = new Base64OutputStream(bos)) {
335 base64os.write(StringUtils.getBytesUtf8(STRING_FIXTURE));
336 }
337
338 final ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
339 final Base64InputStream ins = new Base64InputStream(bis);
340
341
342 ins.skip(1);
343 final byte[] decodedBytes = IOUtils.toByteArray(ins);
344 final String str = StringUtils.newStringUtf8(decodedBytes);
345
346 assertEquals(STRING_FIXTURE.substring(1), str);
347 }
348
349
350
351
352
353
354
355 @Test
356 void testCodec98NPE() throws Exception {
357 final byte[] codec98 = StringUtils.getBytesUtf8(Base64TestData.CODEC_98_NPE);
358 final ByteArrayInputStream data = new ByteArrayInputStream(codec98);
359 final Base64InputStream stream = new Base64InputStream(data);
360
361
362 final byte[] decodedBytes = IOUtils.toByteArray(stream);
363
364 final String decoded = StringUtils.newStringUtf8(decodedBytes);
365 assertEquals(Base64TestData.CODEC_98_NPE_DECODED, decoded, "codec-98 NPE Base64InputStream");
366 }
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383 @Test
384 void testInputStreamReader() throws Exception {
385 final byte[] codec101 = StringUtils.getBytesUtf8(Base64TestData.CODEC_101_INPUT_LENGTH_IS_MULTIPLE_OF_3);
386 final ByteArrayInputStream bais = new ByteArrayInputStream(codec101);
387 final Base64InputStream in = new Base64InputStream(bais);
388 final InputStreamReader isr = new InputStreamReader(in);
389 try (BufferedReader br = new BufferedReader(isr)) {
390 final String line = br.readLine();
391 assertNotNull(line, "Codec101: InputStreamReader works!");
392 }
393 }
394
395
396
397
398
399
400
401 @Test
402 void testMarkSupported() throws Exception {
403 final byte[] decoded = StringUtils.getBytesUtf8(STRING_FIXTURE);
404 final ByteArrayInputStream bin = new ByteArrayInputStream(decoded);
405 try (Base64InputStream in = new Base64InputStream(bin, true, 4, new byte[] { 0, 0, 0 })) {
406
407 assertFalse(in.markSupported(), "Base64InputStream.markSupported() is false");
408 }
409 }
410
411
412
413
414
415
416
417 @Test
418 void testRead0() throws Exception {
419 final byte[] decoded = StringUtils.getBytesUtf8(STRING_FIXTURE);
420 final byte[] buf = new byte[1024];
421 int bytesRead = 0;
422 final ByteArrayInputStream bin = new ByteArrayInputStream(decoded);
423 try (Base64InputStream in = new Base64InputStream(bin, true, 4, new byte[] { 0, 0, 0 })) {
424 bytesRead = in.read(buf, 0, 0);
425 assertEquals(0, bytesRead, "Base64InputStream.read(buf, 0, 0) returns 0");
426 }
427 }
428
429
430
431
432
433
434
435 @Test
436 void testReadMultipleBufferSizes() throws Exception {
437 final byte[][] randomData = BaseNTestData.randomData(new Base64(0, null, false), 1024 * 64);
438 final byte[] encoded = randomData[1];
439 final byte[] decoded = randomData[0];
440 final ByteArrayInputStream bin = new ByteArrayInputStream(encoded);
441 final ByteArrayOutputStream out = new ByteArrayOutputStream();
442 try (Base64InputStream in = new Base64InputStream(bin)) {
443 for (final int i : new int[] { 4 * 1024, 4 * 1024, 8 * 1024, 8 * 1024, 16 * 1024, 16 * 1024, 8 * 1024 }) {
444 final byte[] buf = new byte[i];
445 final int bytesRead = in.read(buf);
446 assertEquals(i, bytesRead);
447 out.write(buf, 0, bytesRead);
448 }
449 }
450 assertArrayEquals(decoded, out.toByteArray());
451 }
452
453
454
455
456
457
458
459 @Test
460 void testReadNull() throws Exception {
461 final byte[] decoded = StringUtils.getBytesUtf8(STRING_FIXTURE);
462 final ByteArrayInputStream bin = new ByteArrayInputStream(decoded);
463 try (Base64InputStream in = new Base64InputStream(bin, true, 4, new byte[] { 0, 0, 0 })) {
464 assertThrows(NullPointerException.class, () -> in.read(null, 0, 0));
465 }
466 }
467
468
469
470
471
472
473
474 @Test
475 void testReadOutOfBounds() throws Exception {
476 final byte[] decoded = StringUtils.getBytesUtf8(STRING_FIXTURE);
477 final byte[] buf = new byte[1024];
478 final ByteArrayInputStream bin = new ByteArrayInputStream(decoded);
479 try (Base64InputStream in = new Base64InputStream(bin, true, 4, new byte[] {0, 0, 0})) {
480 assertThrows(IndexOutOfBoundsException.class, () -> in.read(buf, -1, 0), "Base64InputStream.read(buf, -1, 0)");
481 assertThrows(IndexOutOfBoundsException.class, () -> in.read(buf, 0, -1), "Base64InputStream.read(buf, 0, -1)");
482 assertThrows(IndexOutOfBoundsException.class, () -> in.read(buf, buf.length + 1, 0), "Base64InputStream.read(buf, buf.length + 1, 0)");
483 assertThrows(IndexOutOfBoundsException.class, () -> in.read(buf, buf.length - 1, 2), "Base64InputStream.read(buf, buf.length - 1, 2)");
484 }
485 }
486
487
488
489
490
491
492
493 @Test
494 void testSkipBig() throws Throwable {
495 final InputStream ins = new ByteArrayInputStream(StringUtils.getBytesIso8859_1(ENCODED_B64));
496 try (Base64InputStream b64stream = new Base64InputStream(ins)) {
497 assertEquals(6, b64stream.skip(Integer.MAX_VALUE));
498
499 assertEquals(-1, b64stream.read());
500 assertEquals(-1, b64stream.read());
501 }
502 }
503
504
505
506
507
508
509
510 @Test
511 void testSkipNone() throws Throwable {
512 final InputStream ins = new ByteArrayInputStream(StringUtils.getBytesIso8859_1(ENCODED_B64));
513 try (Base64InputStream b64stream = new Base64InputStream(ins)) {
514 final byte[] actualBytes = new byte[6];
515 assertEquals(0, b64stream.skip(0));
516 b64stream.read(actualBytes, 0, actualBytes.length);
517 assertArrayEquals(actualBytes, new byte[] { 0, 0, 0, (byte) 255, (byte) 255, (byte) 255 });
518
519 assertEquals(-1, b64stream.read());
520 }
521 }
522
523
524
525
526
527
528
529 @Test
530 void testSkipPastEnd() throws Throwable {
531 final InputStream ins = new ByteArrayInputStream(StringUtils.getBytesIso8859_1(ENCODED_B64));
532 try (Base64InputStream b64stream = new Base64InputStream(ins)) {
533
534 assertEquals(6, b64stream.skip(10));
535
536 assertEquals(-1, b64stream.read());
537 assertEquals(-1, b64stream.read());
538 }
539 }
540
541
542
543
544
545
546
547 @Test
548 void testSkipToEnd() throws Throwable {
549 final InputStream ins = new ByteArrayInputStream(StringUtils.getBytesIso8859_1(ENCODED_B64));
550 try (Base64InputStream b64stream = new Base64InputStream(ins)) {
551
552 assertEquals(6, b64stream.skip(6));
553
554 assertEquals(-1, b64stream.read());
555 assertEquals(-1, b64stream.read());
556 }
557 }
558
559
560
561
562
563
564
565 @Test
566 void testSkipWrongArgument() throws Throwable {
567 final InputStream ins = new ByteArrayInputStream(StringUtils.getBytesIso8859_1(ENCODED_B64));
568 try (Base64InputStream b64stream = new Base64InputStream(ins)) {
569 assertThrows(IllegalArgumentException.class, () -> b64stream.skip(-10));
570 }
571 }
572
573
574
575
576
577
578
579 @Test
580 void testStrictDecoding() throws Exception {
581 for (final String s : Base64Test.BASE64_IMPOSSIBLE_CASES) {
582 final byte[] encoded = StringUtils.getBytesUtf8(s);
583 final Base64InputStream in = new Base64InputStream(new ByteArrayInputStream(encoded), false);
584
585 assertFalse(in.isStrictDecoding());
586 IOUtils.toByteArray(in);
587
588 final Base64InputStream in2 = new Base64InputStream(new ByteArrayInputStream(encoded), false, 0, null, CodecPolicy.STRICT);
589 assertTrue(in2.isStrictDecoding());
590 assertThrows(IllegalArgumentException.class, () -> IOUtils.toByteArray(in2));
591
592 try (Base64InputStream in3 = Base64InputStream.builder()
593 .setByteArray(encoded)
594 .setEncode(false)
595 .setBaseNCodec(Base64.builder().setLineLength(0).setLineSeparator(null).setDecodingPolicy(CodecPolicy.STRICT).get())
596 .get()) {
597 assertTrue(in3.isStrictDecoding());
598 assertThrows(IllegalArgumentException.class, () -> IOUtils.toByteArray(in3));
599 }
600 }
601 }
602 }