public Request apply(HttpRequest request) {

      for (HttpRequestFilter filter : request.getFilters()) {
        filter.filter(request);
      }

      AsyncHttpClient client = new AsyncHttpClient();
      AsyncHttpClient.BoundRequestBuilder nativeRequestBuilder;
      String endpoint = request.getEndpoint().toASCIIString();
      if (request.getMethod().equals(HttpMethod.HEAD)) {
        nativeRequestBuilder = client.prepareHead(endpoint);
      } else if (request.getMethod().equals(HttpMethod.GET)) {
        nativeRequestBuilder = client.prepareGet(endpoint);
      } else if (request.getMethod().equals(HttpMethod.DELETE)) {
        nativeRequestBuilder = client.prepareDelete(endpoint);
      } else if (request.getMethod().equals(HttpMethod.PUT)) {
        nativeRequestBuilder = client.preparePut(endpoint);
      } else if (request.getMethod().equals(HttpMethod.POST)) {
        nativeRequestBuilder = client.preparePost(endpoint);
      } else {
        throw new UnsupportedOperationException(request.getMethod());
      }
      Payload payload = request.getPayload();
      if (payload != null) {
        boolean chunked = "chunked".equals(request.getFirstHeaderOrNull("Transfer-Encoding"));

        if (request.getPayload().getContentMD5() != null)
          nativeRequestBuilder.addHeader(
              "Content-MD5", CryptoStreams.base64(request.getPayload().getContentMD5()));
        if (request.getPayload().getContentType() != null)
          nativeRequestBuilder.addHeader(
              HttpHeaders.CONTENT_TYPE, request.getPayload().getContentType());
        if (!chunked) {
          Long length =
              checkNotNull(request.getPayload().getContentLength(), "payload.getContentLength");
          nativeRequestBuilder.addHeader(HttpHeaders.CONTENT_LENGTH, length.toString());
        }
        setPayload(nativeRequestBuilder, payload);
      } else {
        nativeRequestBuilder.addHeader(HttpHeaders.CONTENT_LENGTH, "0");
      }

      nativeRequestBuilder.addHeader(HttpHeaders.USER_AGENT, USER_AGENT);
      for (String header : request.getHeaders().keySet()) {
        for (String value : request.getHeaders().get(header)) {
          nativeRequestBuilder.addHeader(header, value);
        }
      }

      return nativeRequestBuilder.build();
    }
  private <T> T callRecurly(
      final AsyncHttpClient.BoundRequestBuilder builder, @Nullable final Class<T> clazz)
      throws IOException, ExecutionException, InterruptedException {
    final Response response =
        builder
            .addHeader("Authorization", "Basic " + key)
            .addHeader("Accept", "application/xml")
            .addHeader("Content-Type", "application/xml; charset=utf-8")
            .execute()
            .get();

    final InputStream in = response.getResponseBodyAsStream();
    try {
      final String payload = convertStreamToString(in);
      if (debug()) {
        log.info("Msg from Recurly API :: {}", payload);
      }

      // Handle errors payload
      if (response.getStatusCode() >= 300) {
        log.warn("Recurly error whilst calling: {}\n{}", response.getUri(), payload);

        if (response.getStatusCode() == 422) {
          final Errors errors;
          try {
            errors = xmlMapper.readValue(payload, Errors.class);
          } catch (Exception e) {
            // 422 is returned for transaction errors (see
            // http://docs.recurly.com/api/transactions/error-codes)
            // as well as bad input payloads
            log.debug("Unable to extract error", e);
            return null;
          }
          throw new TransactionErrorException(errors);
        } else {
          RecurlyAPIError recurlyError = null;
          try {
            recurlyError = xmlMapper.readValue(payload, RecurlyAPIError.class);
          } catch (Exception e) {
            log.debug("Unable to extract error", e);
          }
          throw new RecurlyAPIException(recurlyError);
        }
      }

      if (clazz == null) {
        return null;
      }

      final T obj = xmlMapper.readValue(payload, clazz);
      if (obj instanceof RecurlyObject) {
        ((RecurlyObject) obj).setRecurlyClient(this);
      } else if (obj instanceof RecurlyObjects) {
        final RecurlyObjects recurlyObjects = (RecurlyObjects) obj;
        recurlyObjects.setRecurlyClient(this);

        // Set the RecurlyClient on all objects for later use
        for (final Object object : recurlyObjects) {
          ((RecurlyObject) object).setRecurlyClient(this);
        }

        // Set the total number of records
        final String xRecords = response.getHeader(X_RECORDS_HEADER_NAME);
        if (xRecords != null) {
          recurlyObjects.setNbRecords(Integer.valueOf(xRecords));
        }

        // Set links for pagination
        final String linkHeader = response.getHeader(LINK_HEADER_NAME);
        if (linkHeader != null) {
          final String[] links = PaginationUtils.getLinks(linkHeader);
          recurlyObjects.setStartUrl(links[0]);
          recurlyObjects.setPrevUrl(links[1]);
          recurlyObjects.setNextUrl(links[2]);
        }
      }
      return obj;
    } finally {
      closeStream(in);
    }
  }