1+ /*
2+ * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation.
8+ *
9+ * This code is distributed in the hope that it will be useful, but WITHOUT
10+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+ * version 2 for more details (a copy is included in the LICENSE file that
13+ * accompanied this code).
14+ *
15+ * You should have received a copy of the GNU General Public License version
16+ * 2 along with this work; if not, write to the Free Software Foundation,
17+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+ *
19+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+ * or visit www.oracle.com if you need additional information or have any
21+ * questions.
22+ */
23+
24+ /* @test
25+ @bug 8340684
26+ @summary Verify unspecified, but long-standing behavior when reading
27+ from an input stream obtained using ZipFile::getInputStream after
28+ the ZipFile has been closed.
29+ @run junit ReadAfterClose
30+ */
31+
32+ import org .junit .jupiter .api .AfterEach ;
33+ import org .junit .jupiter .api .BeforeEach ;
34+ import org .junit .jupiter .params .ParameterizedTest ;
35+ import org .junit .jupiter .params .provider .Arguments ;
36+ import org .junit .jupiter .params .provider .MethodSource ;
37+
38+ import java .io .IOException ;
39+ import java .io .InputStream ;
40+ import java .io .OutputStream ;
41+ import java .nio .charset .StandardCharsets ;
42+ import java .nio .file .Files ;
43+ import java .nio .file .Path ;
44+ import java .util .stream .Stream ;
45+ import java .util .zip .CRC32 ;
46+ import java .util .zip .ZipEntry ;
47+ import java .util .zip .ZipFile ;
48+ import java .util .zip .ZipOutputStream ;
49+
50+ import static org .junit .jupiter .api .Assertions .assertThrows ;
51+
52+ public class ReadAfterClose {
53+
54+ // ZIP file used in this test
55+ private Path zip = Path .of ("read-after-close.zip" );
56+
57+ /**
58+ * Create a sample ZIP file for use by this test
59+ * @throws IOException if an unexpected IOException occurs
60+ */
61+ @ BeforeEach
62+ public void setUp () throws IOException {
63+ byte [] content = "hello" .repeat (1000 ).getBytes (StandardCharsets .UTF_8 );
64+ try (OutputStream out = Files .newOutputStream (zip );
65+ ZipOutputStream zo = new ZipOutputStream (out )) {
66+ {
67+ zo .putNextEntry (new ZipEntry ("deflated.txt" ));
68+ zo .write (content );
69+ }
70+ {
71+ ZipEntry entry = new ZipEntry ("stored.txt" );
72+ entry .setMethod (ZipEntry .STORED );
73+ CRC32 crc = new CRC32 ();
74+ crc .update (content );
75+ entry .setCrc (crc .getValue ());
76+ entry .setSize (content .length );
77+ zo .putNextEntry (entry );
78+ zo .write (content );
79+ }
80+ }
81+ }
82+
83+ /**
84+ * Delete the ZIP file produced by this test
85+ * @throws IOException if an unexpected IOException occurs
86+ */
87+ @ AfterEach
88+ public void cleanup () throws IOException {
89+ Files .deleteIfExists (zip );
90+ }
91+
92+ /**
93+ * Produce arguments with a variation of stored / deflated entries,
94+ * and read behavior before closing the ZipFile.
95+ * @return
96+ */
97+ public static Stream <Arguments > arguments () {
98+ return Stream .of (
99+ Arguments .of ("stored.txt" , true ),
100+ Arguments .of ("stored.txt" , false ),
101+ Arguments .of ("deflated.txt" , true ),
102+ Arguments .of ("deflated.txt" , false )
103+ );
104+ }
105+ /**
106+ * Attempting to read from an InputStream obtained by ZipFile.getInputStream
107+ * after the backing ZipFile is closed should throw IOException
108+ *
109+ * @throws IOException if an unexpected IOException occurs
110+ */
111+ @ ParameterizedTest
112+ @ MethodSource ("arguments" )
113+ public void readAfterClose (String entryName , boolean readFirst ) throws IOException {
114+ // Retain a reference to an input stream backed by a closed ZipFile
115+ InputStream in ;
116+ try (ZipFile zf = new ZipFile (zip .toFile ())) {
117+ in = zf .getInputStream (new ZipEntry (entryName ));
118+ // Optionally consume a single byte from the stream before closing
119+ if (readFirst ) {
120+ in .read ();
121+ }
122+ }
123+
124+ assertThrows (IOException .class , () -> {
125+ in .read ();
126+ });
127+ }
128+ }
0 commit comments