@Override
  public HttpRequest toNettyRequest(
      Message message, String uri, NettyHttpConfiguration configuration) throws Exception {
    LOG.trace("toNettyRequest: {}", message);

    // the message body may already be a Netty HTTP response
    if (message.getBody() instanceof HttpRequest) {
      return (HttpRequest) message.getBody();
    }

    // just assume GET for now, we will later change that to the actual method to use
    HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri);

    TypeConverter tc = message.getExchange().getContext().getTypeConverter();

    // if we bridge endpoint then we need to skip matching headers with the HTTP_QUERY to avoid
    // sending
    // duplicated headers to the receiver, so use this skipRequestHeaders as the list of headers to
    // skip
    Map<String, Object> skipRequestHeaders = null;
    if (configuration.isBridgeEndpoint()) {
      String queryString = message.getHeader(Exchange.HTTP_QUERY, String.class);
      if (queryString != null) {
        skipRequestHeaders = URISupport.parseQuery(queryString, false, true);
      }
      // Need to remove the Host key as it should be not used
      message.getHeaders().remove("host");
    }

    // append headers
    // must use entrySet to ensure case of keys is preserved
    for (Map.Entry<String, Object> entry : message.getHeaders().entrySet()) {
      String key = entry.getKey();
      Object value = entry.getValue();

      // we should not add headers for the parameters in the uri if we bridge the endpoint
      // as then we would duplicate headers on both the endpoint uri, and in HTTP headers as well
      if (skipRequestHeaders != null && skipRequestHeaders.containsKey(key)) {
        continue;
      }

      // use an iterator as there can be multiple values. (must not use a delimiter)
      final Iterator<?> it = ObjectHelper.createIterator(value, null, true);
      while (it.hasNext()) {
        String headerValue = tc.convertTo(String.class, it.next());

        if (headerValue != null
            && headerFilterStrategy != null
            && !headerFilterStrategy.applyFilterToCamelHeaders(
                key, headerValue, message.getExchange())) {
          LOG.trace("HTTP-Header: {}={}", key, headerValue);
          request.headers().add(key, headerValue);
        }
      }
    }

    Object body = message.getBody();
    if (body != null) {
      // support bodies as native Netty
      ChannelBuffer buffer;
      if (body instanceof ChannelBuffer) {
        buffer = (ChannelBuffer) body;
      } else {
        // try to convert to buffer first
        buffer = message.getBody(ChannelBuffer.class);
        if (buffer == null) {
          // fallback to byte array as last resort
          byte[] data = message.getMandatoryBody(byte[].class);
          buffer = ChannelBuffers.copiedBuffer(data);
        }
      }
      if (buffer != null) {
        request.setContent(buffer);
        int len = buffer.readableBytes();
        // set content-length
        request.headers().set(HttpHeaders.Names.CONTENT_LENGTH, len);
        LOG.trace("Content-Length: {}", len);
      } else {
        // we do not support this kind of body
        throw new NoTypeConversionAvailableException(body, ChannelBuffer.class);
      }
    }

    // update HTTP method accordingly as we know if we have a body or not
    HttpMethod method = NettyHttpHelper.createMethod(message, body != null);
    request.setMethod(method);

    // set the content type in the response.
    String contentType = MessageHelper.getContentType(message);
    if (contentType != null) {
      // set content-type
      request.headers().set(HttpHeaders.Names.CONTENT_TYPE, contentType);
      LOG.trace("Content-Type: {}", contentType);
    }

    // must include HOST header as required by HTTP 1.1
    // use URI as its faster than URL (no DNS lookup)
    URI u = new URI(uri);
    String host = u.getHost();
    request.headers().set(HttpHeaders.Names.HOST, host);
    LOG.trace("Host: {}", host);

    // configure connection to accordingly to keep alive configuration
    // favor using the header from the message
    String connection = message.getHeader(HttpHeaders.Names.CONNECTION, String.class);
    if (connection == null) {
      // fallback and use the keep alive from the configuration
      if (configuration.isKeepAlive()) {
        connection = HttpHeaders.Values.KEEP_ALIVE;
      } else {
        connection = HttpHeaders.Values.CLOSE;
      }
    }
    request.headers().set(HttpHeaders.Names.CONNECTION, connection);
    LOG.trace("Connection: {}", connection);
    return request;
  }