Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8263793

GZIPOutputStream doesn't support optional GZIP fields

XMLWordPrintable

    • low
    • This change only adding new Constructors and keeps the old ones work as before. So set the risk to "low"
    • Java API
    • SE

      Summary

      Introduce new constructors of GZIPOutputStream to support adding optional flags/fields into the gzip file header.

      Problem

      As described in JDK-4890732 , the GZIPOutputStream does not support adding extra fields, file name and file comment into the gzip header. And it is hard for programmer to achieve this target by simply extend GZIPOutputStream because the writeHeader() method is private and hence can not be override.

      Per the gzip file specification RFC-1952 (https://tools.ietf.org/html/rfc1952), these optional flags/fields in gzip file header could help add extra information about the gzip file.

      one example is the gziped heap dump file generated by jcmd jmap -dump command, it adds file comment in the header which describe the block size, so that the decompressor could specify proper block size for decompressing . (please refer comments in https://github.com/openjdk/jdk/blob/ff52f2989fd60ec8251eaf76f4c4b78f10d3e048/src/hotspot/share/services/heapDumperCompression.cpp#L127)

      Solution

      Add a class GZIPHeaderBuilder and a record GZIPHeaderBuilder.GZIPHeaderData to set and get gzip header data.

      Users could use GZIPHeaderBuilder to generate GZIPHeaderBuilder.GZIPHeaderData with builder pattern.

      Add two constructors of GZIPOutputStream:

      public GZIPOutputStream(OutputStream out, int size, boolean syncFlush, GZIPHeaderBuilder.GZIPHeaderData gzipHeaderData)

      and

      public GZIPOutputStream(OutputStream out, GZIPHeaderBuilder.GZIPHeaderData gzipHeaderData)

      Users could use them for the purpose of setting gzip header fields.

      Specification

      In summary, this CSR contains 4 kind of changes:

      - Adds the definition of class GZIPHeaderBuilder's API for setting gzip header fields and generate header bytes.
      
      - Adds the Record GZIPHeaderData inside GZIPHeaderBuilder, which includes header fields.
      
      - Adds 2 new constructors in GZIPOutputStream to generate gzip data with specified header data.
      
      - Adds 1 new API in GZIPInputStream to get the header data from the gzip data.

      Defined API for added GZIPHeaderBuilder:

      /**
       * Creates a GZIP file header builder.
       * Current only {@code Deflater.DEFLATED} compress method supportted.
       *
       * @since 18
       */
      public GZIPHeaderBuilder()
      
      /**
       * Add extra field in GZIP file header.
       * This method verifies the extra fileds layout per RFC-1952.
       * See comments of {@code verifyExtraFieldLayout}
       *
       * @param extraFieldBytes The byte array of extra field.
       * @return {@code this}
       *
       * @throws ZipException if extra field layout is incorrect.
       *
       * @since 18
       */
      public GZIPHeaderBuilder withExtraFieldBytes(byte[] extraFieldBytes) throws ZipException
      
      /**
       * Add file name in GZIP file header.
       *
       * Per RFC-1952, the file name string should in ISO_8859-1 character set.
       *
       * @param filename The file name
       * @return {@code this}
       *
       * @since 18
       */
      public GZIPHeaderBuilder withFileName(String filename) 
      
      /**
       * Add file comment in GZIP file header.
       *
       * Per RFC-1952, the file comment string should in ISO_8859-1 character set.
       *
       * @param fileComment The file comment
       * @return {@code this}'
       *
       * @since 18
       */
      public GZIPHeaderBuilder withFileComment(String fileComment)
      
      /**
       * Enable/Disable the CRC calculation of GZIP file header.
       *
       * @param calculateHeaderCRC if {@code true} the header data contains the lower 16 bytes of header CRC
       * @return {@code this}
       *
       * @since 18
       */
      public GZIPHeaderBuilder calculateHeaderCRC(boolean calculateHeaderCRC) 
      
      /**
       *  Generate the GZIP header data
       * @return the {@code record} of GZIP header data.
       *
       * @throws ZipException         If extra field size is out of range.
       *                              Or if extra filed data layout is incorrect.
       * @throws IllegalArgumentException     If compress method is not {@code Deflater.DEFLATED}
       * @since 18
       */
      public GZIPHeaderData build() throws IOException
      
      /**
       * Creates GZIP header bytes with optional header members and compress method.
       * Per RFC-1952:
       *    The filename and fileComment member should be String in
       *          LATIN-1 (ISO-8859-1) character set.
       *
       *    A compliant compressor must produce files with correct ID1,
       *          ID2, CM, CRC32, and ISIZE, but may set all the other fields in
       *          the fixed-length part of the header to default values (255 for
       *          OS, 0 for all others).  The compressor must set all reserved
       *          bits to zero.
       *
       *   The XFL (extra Flags) is set to zero and OS is set to {@code OS_UNKNOWN (=255)}.
       *   The FTEXT flag is set to zero and MTIME is filled with 0.
       *
       * @param cm                    compress method,
       *                              per RFC-1952, 0-7 are reserved, 8 denotes "deflate".
       *                              at present only support {@code Deflater.DEFLATED}
       *
       * @param extraFieldBytes
       *        The byte array of extra filed, the generated header would calculate the
       *        byte[] size and fill it before the byte[] in header.
       * @param filename              the original file name in ISO-8859-1 character set
       * @param fileComment           the file comment in ISO_8859-1 character set.
       *
       * @return Bytes of header data generated.
       *
       * @throws ZipException         If extra field size is out of range.
       *                              Or if extra filed data layout is incorrect.
       * @throws IllegalArgumentException     If compress method is not {@code Deflater.DEFLATED}.
       *
       * @since 18
       */
      public byte[] generateBytes(byte cm,
                                  byte[] extraFieldBytes,
                                  String filename,
                                  String fileComment) throws IOException 

      The Record definition of GZIPHeaderBuilder.GZIPHeaderData :

      /**
       * This class implements the header of GZIP file which contains members defined
       * in the RFC 1952 specification
       *
       * @since 18
       *
       */
      public record GZIPHeaderData (byte compressMethod, byte flags,
                                    byte[] extraFieldBytes,
                                    String filename,
                                    String fileComment,
                                    int headerCRC16,
                                    byte[] headerBytes)

      For GZIPOutputStream the specification of the added two constructors are as following:

      /**
       * Creates a new output stream with the specified buffer size,
       * flush mode flags and header fields.
       *
       * @param out the output stream
       * @param size the output buffer size
       * @param syncFlush
       *        if {@code true} invocation of the inherited
       *        {@link DeflaterOutputStream#flush() flush()} method of
       *        this instance flushes the compressor with flush mode
       *        {@link Deflater#SYNC_FLUSH} before flushing the output
       *        stream, otherwise only flushes the output stream
       * @param gzipHeaderData
       *        The header of Gzip file, contains header members defined
       *        in RFC 1952. if {@code null}, use default header data.
       *
       * @throws    IOException If an I/O error has occurred.
       * @throws    IllegalArgumentException if {@code size <= 0}
       *
       * @since 18
       */
      public GZIPOutputStream(OutputStream out,
                              int size,
                              boolean syncFlush,
                              GZIPHeaderBuilder.GZIPHeaderData gzipHeaderData)
          throws IOException

      and:

      /**
       * Creates a new output stream with the specified flags.
       *
       * @param out the output stream
       * @param gzipHeaderData
       *        The header of Gzip file, contains header members defined
       *        in RFC 1952. if {@code null}, use default header data.
       * @throws    IOException If an I/O error has occurred.
       * @throws    IllegalArgumentException if {@code size <= 0}
       *
       * @since 18
       */
      public GZIPOutputStream(OutputStream out,
                              GZIPHeaderBuilder.GZIPHeaderData gzipHeaderData)
          throws IOException

      For GInputOutputStream the specification of the added method is as following:

      /**
       * Retures gzip header data
       * @return header data
       */
      public GZIPHeaderBuilder.GZIPHeaderData headerData() {
          return headerData;
      }

            lzang Lin Zang
            rmandalasunw Ranjith Mandala (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: