@SuppressWarnings("unchecked")
  private T parseResponseInternal(GaePendingResult<T, R> request, HTTPResponse response)
      throws IOException, ApiException, InterruptedException {
    if (shouldRetry(response)) {
      // Retry is a blocking method, but that's OK. If we're here, we're either in an await()
      // call, which is blocking anyway, or we're handling a callback in a separate thread.
      return request.retry();
    }

    byte[] bytes = response.getContent();
    R resp;

    String contentType = null;
    for (HTTPHeader header : response.getHeaders()) {
      if (header.getName().equalsIgnoreCase("Content-Type")) {
        contentType = header.getValue();
      }
    }

    if (contentType != null
        && contentType.startsWith("image")
        && responseClass == ImageResult.Response.class
        && response.getResponseCode() == 200) {
      ImageResult result = new ImageResult(contentType, bytes);
      return (T) result;
    }

    Gson gson =
        new GsonBuilder()
            .registerTypeAdapter(ZonedDateTime.class, new ZonedDateTimeAdapter())
            .registerTypeAdapter(Distance.class, new DistanceAdapter())
            .registerTypeAdapter(Duration.class, new DurationAdapter())
            .registerTypeAdapter(Fare.class, new FareAdapter())
            .registerTypeAdapter(LatLng.class, new LatLngAdapter())
            .registerTypeAdapter(
                AddressComponentType.class, new SafeEnumAdapter<>(AddressComponentType.UNKNOWN))
            .registerTypeAdapter(AddressType.class, new SafeEnumAdapter<>(AddressType.UNKNOWN))
            .registerTypeAdapter(TravelMode.class, new SafeEnumAdapter<>(TravelMode.UNKNOWN))
            .registerTypeAdapter(LocationType.class, new SafeEnumAdapter<>(LocationType.UNKNOWN))
            .registerTypeAdapter(RatingType.class, new SafeEnumAdapter<>(RatingType.UNKNOWN))
            .registerTypeAdapter(VehicleType.class, new SafeEnumAdapter<>(VehicleType.OTHER))
            .registerTypeAdapter(DayOfWeek.class, new DayOfWeekAdapter())
            .registerTypeAdapter(PriceLevel.class, new PriceLevelAdapter())
            .registerTypeAdapter(Instant.class, new InstantAdapter())
            .registerTypeAdapter(LocalTime.class, new LocalTimeAdapter())
            .registerTypeAdapter(GeolocationApi.Response.class, new GeolocationResponseAdapter())
            .registerTypeAdapter(EncodedPolyline.class, new EncodedPolylineInstanceCreator(""))
            .setFieldNamingPolicy(fieldNamingPolicy)
            .create();

    // Attempt to de-serialize before checking the HTTP status code, as there may be JSON in the
    // body that we can use to provide a more descriptive exception.
    try {
      resp = gson.fromJson(new String(bytes, "utf8"), responseClass);
    } catch (JsonSyntaxException e) {
      // Check HTTP status for a more suitable exception
      if (response.getResponseCode() > 399) {
        // Some of the APIs return 200 even when the API request fails, as long as the transport
        // mechanism succeeds. In these cases, INVALID_RESPONSE, etc are handled by the Gson
        // parsing.
        throw new IOException(
            String.format(
                "Server Error: %d %s",
                response.getResponseCode(),
                new String(response.getContent(), Charset.defaultCharset())));
      }

      // Otherwise just cough up the syntax exception.
      throw e;
    }

    if (resp.successful()) {
      // Return successful responses
      return resp.getResult();
    } else {
      ApiException e = resp.getError();
      if (shouldRetry(e)) {
        // Retry over_query_limit errors
        return request.retry();
      } else {
        // Throw anything else, including OQLs if we've spent too much time retrying
        throw e;
      }
    }
  }
@param request HTTP request to execute.
@param client The client used to execute the request.
@param responseClass Model class to unmarshal JSON body content.
@param fieldNamingPolicy FieldNamingPolicy for unmarshaling JSON.
@param errorTimeOut Number of milliseconds to re-send erroring requests.
@param maxRetries Number of times allowed to re-send erroring requests.
src/main/java/com/google/maps/internal/GaePendingResult.java