JDK-7031075 : GZIPInputStream's available() reports 1, but read() gives -1.
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.jar
  • Affected Version: 6,9
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_2008
  • CPU: x86
  • Submitted: 2011-03-25
  • Updated: 2017-01-02
  • Resolved: 2016-07-13
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 9
9 b128Fixed
Related Reports
Duplicate :  
Duplicate :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.6.0_21"
Java(TM) SE Runtime Environment (build 1.6.0_21-b07)
Java HotSpot(TM) 64-Bit Server VM (build 17.0-b17, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]

A DESCRIPTION OF THE PROBLEM :
The following unit test snippet fails as is:

// a) InputStream is = new FileInputStream(f1);
InputStream is = new GZIPInputStream(new FileInputStream(f1));
try {
	do {
		//Skip
		if (is.available() > 0) {
			assertTrue(is.read() != -1);
		} else {
			assertTrue(is.read() == -1);
			assertTrue(is.available() == 0);
			break;
		}
		
	} while (true);

} finally {
	is.close();
}

If we uncomment a) and comment the line after it, the test passes.
1) This hints a possible inconsistency between the input stream and the gzip-wrapped input stream.
2) According to the documentation, when available() tells we have something to take it returns a non-zero value, and a subsequent read() gives a byte from that something. This seems broken at the end of gzipped streams.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the unit test, completing it with supplying a gzipped file.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Test passes.
ACTUAL -
Test fails.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Test fails at
	assertTrue(is.read() != -1);

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
@Test
public final void testGzippedStreamAPIConsistency() throws IOException {
	File f1 = new File("bla01.gz");
	writeOperationsZipFile(f1, 20);
		
	// a) InputStream is = new FileInputStream(f1);
	InputStream is = new GZIPInputStream(new FileInputStream(f1));
	try {
		do {
			//Skip
			if (is.available() > 0) {
				assertTrue(is.read() != -1);
			} else {
				assertTrue(is.read() == -1);
				assertTrue(is.available() == 0);
				break;
			}
				
		} while (true);
		
	} finally {
		is.close();
	}
}

private void writeOperationsZipFile(File f, final int howMany) throws IOException {
	OutputStream fos = new GZIPOutputStream(new FileOutputStream(f));
	try {
		writeOutputStream(fos, howMany);
	} finally {
		fos.close();
	}
}
private void writeOutputStream(OutputStream fos, int howMany) {
	PrintStream ps = new PrintStream(fos);
	
	try {
		int i = 0;
		while (i < howMany) {
			//write an operation.
			ps.println("operation" + i);
			i++;
		}//while
	} finally {
		ps.close();
	}
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Do not rely on available() but on the return result of read().

Comments
EVALUATION The spec of InflaterInputStream.avaialble() (in which the GZIPInputstream.available() is inherited from) states that "Returns 0 AFTER EOF has been reached, otherwise always return 1", arguably the EOF can be considered that has not been reached until you read() pass it. So "arguably" this is NOT an implementation bug. That said, it appears to be reasonable for the IIS.available() to do a "inf.finished() || inf.needsDictionary()" check insteand of purely depend on the EOF flag which is set AFTER the last read() attempt was failed.
06-06-2011