All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.api.client.http.GzipSupport Maven / Gradle / Ivy

Go to download

Google HTTP Client Library for Java. Functionality that works on all supported Java platforms, including Java 5 (or higher) desktop (SE) and web (EE), Android, and Google App Engine.

There is a newer version: 1.45.1
Show newest version
package com.google.api.client.http;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.GZIPInputStream;

final class GzipSupport {

  private GzipSupport() {}

  static GZIPInputStream newGzipInputStream(InputStream in) throws IOException {
    return new GZIPInputStream(new OptimisticAvailabilityInputStream(in));
  }

  /**
   * When {@link GZIPInputStream} completes processing an individual member it will call {@link
   * InputStream#available()} to determine if there is more stream to try and process. If the call
   * to {@code available()} returns 0 {@code GZIPInputStream} will determine it has processed the
   * entirety of the underlying stream. This is spurious, as {@link InputStream#available()} is
   * allowed to return 0 if it would require blocking in order for more bytes to be available. When
   * {@code GZIPInputStream} is reading from a {@code Transfer-Encoding: chunked} response, if the
   * chunk boundary happens to align closely enough to the member boundary {@code GZIPInputStream}
   * won't consume the whole response.
   *
   * 

This class, provides an optimistic "estimate" (in actuality, a lie) of the number of {@code * 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 {@link Integer#MAX_VALUE} * to any call of {@link #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 {@link 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 {@link java.io.SequenceInputStream} with two underlying {@link * java.io.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)
       *   
    *
*/ private static final class OptimisticAvailabilityInputStream extends FilterInputStream { private int lastRead = 0; OptimisticAvailabilityInputStream(InputStream delegate) { super(delegate); } @Override public int available() throws IOException { return lastRead > -1 ? Integer.MAX_VALUE : 0; } @Override public int read() throws IOException { return lastRead = super.read(); } @Override public int read(byte[] b) throws IOException { return lastRead = super.read(b); } @Override public int read(byte[] b, int off, int len) throws IOException { return lastRead = super.read(b, off, len); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy