Class GzipSupport.OptimisticAvailabilityInputStream

  • All Implemented Interfaces:
    java.io.Closeable, java.lang.AutoCloseable
    Enclosing class:
    GzipSupport

    private static final class GzipSupport.OptimisticAvailabilityInputStream
    extends java.io.FilterInputStream
    When GZIPInputStream completes processing an individual member it will call InputStream.available() to determine if there is more stream to try and process. If the call to available() returns 0 GZIPInputStream will determine it has processed the entirety of the underlying stream. This is spurious, as InputStream.available() is allowed to return 0 if it would require blocking in order for more bytes to be available. When GZIPInputStream is reading from a Transfer-Encoding: chunked response, if the chunk boundary happens to align closely enough to the member boundary GZIPInputStream won't consume the whole response.

    This class, provides an optimistic "estimate" (in actuality, a lie) of the number of available() bytes in the underlying stream. It does this by tracking the last number of bytes read. If the last number of bytes read is grater than -1, we return Integer.MAX_VALUE to any call of available().

    We're breaking the contract of available() in that we're lying about how much data we have accessible without blocking, however in the case where we're weaving GZIPInputStream into response processing we already know there are going to be blocking calls to read before the stream is exhausted.

    This scenario isn't unique to processing of chunked responses, and can be replicated reliably using a SequenceInputStream with two underlying ByteArrayInputStream. See the corresponding test class for a reproduction.

    The need for this class has been verified for the following JVMs:

    1.  openjdk version "1.8.0_292"
       OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_292-b10)
       OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.292-b10, mixed mode)
         
    2.  openjdk version "11.0.14.1" 2022-02-08
       OpenJDK Runtime Environment Temurin-11.0.14.1+1 (build 11.0.14.1+1)
       OpenJDK 64-Bit Server VM Temurin-11.0.14.1+1 (build 11.0.14.1+1, mixed mode)
         
    3.  openjdk version "17" 2021-09-14
       OpenJDK Runtime Environment Temurin-17+35 (build 17+35)
       OpenJDK 64-Bit Server VM Temurin-17+35 (build 17+35, mixed mode, sharing)
         
    • Field Summary

      Fields 
      Modifier and Type Field Description
      private int lastRead  
      • Fields inherited from class java.io.FilterInputStream

        in
    • Method Summary

      All Methods Instance Methods Concrete Methods 
      Modifier and Type Method Description
      int available()  
      int read()  
      int read​(byte[] b)  
      int read​(byte[] b, int off, int len)  
      • Methods inherited from class java.io.FilterInputStream

        close, mark, markSupported, reset, skip
      • Methods inherited from class java.lang.Object

        clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    • Field Detail

      • lastRead

        private int lastRead
    • Constructor Detail

      • OptimisticAvailabilityInputStream

        OptimisticAvailabilityInputStream​(java.io.InputStream delegate)
    • Method Detail

      • available

        public int available()
                      throws java.io.IOException
        Overrides:
        available in class java.io.FilterInputStream
        Throws:
        java.io.IOException
      • read

        public int read()
                 throws java.io.IOException
        Overrides:
        read in class java.io.FilterInputStream
        Throws:
        java.io.IOException
      • read

        public int read​(byte[] b)
                 throws java.io.IOException
        Overrides:
        read in class java.io.FilterInputStream
        Throws:
        java.io.IOException
      • read

        public int read​(byte[] b,
                        int off,
                        int len)
                 throws java.io.IOException
        Overrides:
        read in class java.io.FilterInputStream
        Throws:
        java.io.IOException