/**
   * Extracts the response from the method as a InputStream.
   *
   * @param method the method that was executed
   * @return the response either as a stream, or as a deserialized java object
   * @throws IOException can be thrown
   */
  protected static Object extractResponseBody(HttpMethod method, Exchange exchange)
      throws IOException, ClassNotFoundException {
    InputStream is = method.getResponseBodyAsStream();
    if (is == null) {
      return null;
    }

    Header header = method.getResponseHeader(Exchange.CONTENT_ENCODING);
    String contentEncoding = header != null ? header.getValue() : null;

    if (!exchange.getProperty(Exchange.SKIP_GZIP_ENCODING, Boolean.FALSE, Boolean.class)) {
      is = GZIPHelper.uncompressGzip(contentEncoding, is);
    }

    // Honor the character encoding
    String contentType = null;
    header = method.getResponseHeader("content-type");
    if (header != null) {
      contentType = header.getValue();
      // find the charset and set it to the Exchange
      HttpHelper.setCharsetFromContentType(contentType, exchange);
    }
    InputStream response = doExtractResponseBodyAsStream(is, exchange);
    // if content type is a serialized java object then de-serialize it back to a Java object
    if (contentType != null
        && contentType.equals(HttpConstants.CONTENT_TYPE_JAVA_SERIALIZED_OBJECT)) {
      return HttpHelper.deserializeJavaObjectFromStream(response);
    } else {
      return response;
    }
  }
  protected void populateResponse(
      Exchange exchange,
      HttpMethod method,
      Message in,
      HeaderFilterStrategy strategy,
      int responseCode)
      throws IOException, ClassNotFoundException {
    // We just make the out message is not create when extractResponseBody throws exception,
    Object response = extractResponseBody(method, exchange);
    Message answer = exchange.getOut();

    answer.setHeader(Exchange.HTTP_RESPONSE_CODE, responseCode);
    answer.setBody(response);

    // propagate HTTP response headers
    Header[] headers = method.getResponseHeaders();
    for (Header header : headers) {
      String name = header.getName();
      String value = header.getValue();
      if (name.toLowerCase().equals("content-type")) {
        name = Exchange.CONTENT_TYPE;
        exchange.setProperty(Exchange.CHARSET_NAME, IOHelper.getCharsetNameFromContentType(value));
      }

      // use http helper to extract parameter value as it may contain multiple values
      Object extracted = HttpHelper.extractHttpParameterValue(value);
      if (strategy != null && !strategy.applyFilterToExternalHeaders(name, extracted, exchange)) {
        HttpHelper.appendHeader(answer.getHeaders(), name, extracted);
      }
    }

    // preserve headers from in by copying any non existing headers
    // to avoid overriding existing headers with old values
    MessageHelper.copyHeaders(exchange.getIn(), answer, false);
  }
  /**
   * Creates the HttpMethod to use to call the remote server, either its GET or POST.
   *
   * @param exchange the exchange
   * @return the created method as either GET or POST
   * @throws CamelExchangeException is thrown if error creating RequestEntity
   */
  @SuppressWarnings("deprecation")
  protected HttpMethod createMethod(Exchange exchange) throws Exception {
    // creating the url to use takes 2-steps
    String url = HttpHelper.createURL(exchange, getEndpoint());
    URI uri = HttpHelper.createURI(exchange, url, getEndpoint());
    // get the url and query string from the uri
    url = uri.toASCIIString();
    String queryString = uri.getRawQuery();

    // execute any custom url rewrite
    String rewriteUrl = HttpHelper.urlRewrite(exchange, url, getEndpoint(), this);
    if (rewriteUrl != null) {
      // update url and query string from the rewritten url
      url = rewriteUrl;
      uri = new URI(url);
      // use raw query to have uri decimal encoded which http client requires
      queryString = uri.getRawQuery();
    }

    // remove query string as http client does not accept that
    if (url.indexOf('?') != -1) {
      url = url.substring(0, url.indexOf('?'));
    }

    // create http holder objects for the request
    RequestEntity requestEntity = createRequestEntity(exchange);
    HttpMethods methodToUse =
        HttpHelper.createMethod(exchange, getEndpoint(), requestEntity != null);
    HttpMethod method = methodToUse.createMethod(url);
    if (queryString != null) {
      // need to encode query string
      queryString = UnsafeUriCharactersEncoder.encode(queryString);
      method.setQueryString(queryString);
    }

    LOG.trace("Using URL: {} with method: {}", url, method);

    if (methodToUse.isEntityEnclosing()) {
      ((EntityEnclosingMethod) method).setRequestEntity(requestEntity);
      if (requestEntity != null && requestEntity.getContentType() == null) {
        LOG.debug("No Content-Type provided for URL: {} with exchange: {}", url, exchange);
      }
    }

    // there must be a host on the method
    if (method.getHostConfiguration().getHost() == null) {
      throw new IllegalArgumentException(
          "Invalid uri: "
              + url
              + ". If you are forwarding/bridging http endpoints, then enable the bridgeEndpoint option on the endpoint: "
              + getEndpoint());
    }

    return method;
  }
  /**
   * Creates a holder object for the data to send to the remote server.
   *
   * @param exchange the exchange with the IN message with data to send
   * @return the data holder
   * @throws CamelExchangeException is thrown if error creating RequestEntity
   */
  protected RequestEntity createRequestEntity(Exchange exchange) throws CamelExchangeException {
    Message in = exchange.getIn();
    if (in.getBody() == null) {
      return null;
    }

    RequestEntity answer = in.getBody(RequestEntity.class);
    if (answer == null) {
      try {
        Object data = in.getBody();
        if (data != null) {
          String contentType = ExchangeHelper.getContentType(exchange);

          if (contentType != null
              && HttpConstants.CONTENT_TYPE_JAVA_SERIALIZED_OBJECT.equals(contentType)) {
            // serialized java object
            Serializable obj = in.getMandatoryBody(Serializable.class);
            // write object to output stream
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            HttpHelper.writeObjectToStream(bos, obj);
            answer =
                new ByteArrayRequestEntity(
                    bos.toByteArray(), HttpConstants.CONTENT_TYPE_JAVA_SERIALIZED_OBJECT);
            IOHelper.close(bos);
          } else if (data instanceof File || data instanceof GenericFile) {
            // file based (could potentially also be a FTP file etc)
            File file = in.getBody(File.class);
            if (file != null) {
              answer = new FileRequestEntity(file, contentType);
            }
          } else if (data instanceof String) {
            // be a bit careful with String as any type can most likely be converted to String
            // so we only do an instanceof check and accept String if the body is really a String
            // do not fallback to use the default charset as it can influence the request
            // (for example application/x-www-form-urlencoded forms being sent)
            String charset = IOHelper.getCharsetName(exchange, false);
            answer = new StringRequestEntity((String) data, contentType, charset);
          }
          // fallback as input stream
          if (answer == null) {
            // force the body as an input stream since this is the fallback
            InputStream is = in.getMandatoryBody(InputStream.class);
            answer = new InputStreamRequestEntity(is, contentType);
          }
        }
      } catch (UnsupportedEncodingException e) {
        throw new CamelExchangeException(
            "Error creating RequestEntity from message body", exchange, e);
      } catch (IOException e) {
        throw new CamelExchangeException("Error serializing message body", exchange, e);
      }
    }
    return answer;
  }