@Test(groups = {"standalone", "default_provider"})
  public void propFindCompletionHandlerWebDavTest()
      throws InterruptedException, IOException, ExecutionException {

    AsyncHttpClient c = getAsyncHttpClient(null);
    try {
      Request mkcolRequest = new RequestBuilder("MKCOL").setUrl(getTargetUrl()).build();
      Response response = c.executeRequest(mkcolRequest).get();
      assertEquals(response.getStatusCode(), 201);

      Request propFindRequest = new RequestBuilder("PROPFIND").setUrl(getTargetUrl()).build();
      WebDavResponse webDavResponse =
          c.executeRequest(
                  propFindRequest,
                  new WebDavCompletionHandlerBase<WebDavResponse>() {
                    /** {@inheritDoc} */
                    /* @Override */
                    public void onThrowable(Throwable t) {

                      t.printStackTrace();
                    }

                    @Override
                    public WebDavResponse onCompleted(WebDavResponse response) throws Exception {
                      return response;
                    }
                  })
              .get();

      assertNotNull(webDavResponse);
      assertEquals(webDavResponse.getStatusCode(), 200);
    } finally {
      c.close();
    }
  }
  @Test(groups = {"standalone", "default_provider"})
  public void propFindWebDavTest() throws InterruptedException, IOException, ExecutionException {

    AsyncHttpClient c = getAsyncHttpClient(null);
    try {
      Request mkcolRequest = new RequestBuilder("MKCOL").setUrl(getTargetUrl()).build();
      Response response = c.executeRequest(mkcolRequest).get();
      assertEquals(response.getStatusCode(), 201);

      Request putRequest =
          new RequestBuilder("PUT")
              .setUrl(String.format("http://127.0.0.1:%s/folder1/Test.txt", port1))
              .setBody("this is a test")
              .build();
      response = c.executeRequest(putRequest).get();
      assertEquals(response.getStatusCode(), 201);

      Request propFindRequest =
          new RequestBuilder("PROPFIND")
              .setUrl(String.format("http://127.0.0.1:%s/folder1/Test.txt", port1))
              .build();
      response = c.executeRequest(propFindRequest).get();

      assertEquals(response.getStatusCode(), 207);
      assertTrue(response.getResponseBody().contains("<status>HTTP/1.1 200 OK</status>"));
    } finally {
      c.close();
    }
  }
  @Test(groups = {"online", "default_provider"})
  public void testRequestProxy()
      throws IOException, InterruptedException, ExecutionException, TimeoutException {
    AsyncHttpClientConfig.Builder b = new AsyncHttpClientConfig.Builder();
    b.setFollowRedirects(true);

    ProxyServer ps = new ProxyServer(ProxyServer.Protocol.HTTPS, "127.0.0.1", port1);

    AsyncHttpClientConfig config = b.build();
    AsyncHttpClient asyncHttpClient = getAsyncHttpClient(config);

    RequestBuilder rb = new RequestBuilder("GET").setProxyServer(ps).setUrl(getTargetUrl2());
    Future<Response> responseFuture =
        asyncHttpClient.executeRequest(
            rb.build(),
            new AsyncCompletionHandlerBase() {

              public void onThrowable(Throwable t) {
                t.printStackTrace();
                log.debug(t.getMessage(), t);
              }

              @Override
              public Response onCompleted(Response response) throws Exception {
                return response;
              }
            });
    Response r = responseFuture.get();
    assertEquals(r.getStatusCode(), 200);
    assertEquals(r.getHeader("X-Proxy-Connection"), "keep-alive");

    asyncHttpClient.close();
  }
  @AfterMethod(alwaysRun = true)
  public void clean() throws InterruptedException, Exception {
    AsyncHttpClient c = getAsyncHttpClient(null);

    Request deleteRequest = new RequestBuilder("DELETE").setUrl(getTargetUrl()).build();
    c.executeRequest(deleteRequest).get();
  }
  @Override
  public ListenableFuture<HttpResponse> submit(HttpCommand command) {
    try {
      for (; ; ) {
        Future<Response> responseF =
            client.executeRequest(convertToNingRequest.apply(command.getRequest()));
        final HttpResponse httpResponse = convertToJCloudsResponse.apply(responseF.get());
        int statusCode = httpResponse.getStatusCode();
        if (statusCode >= 300) {
          if (retryHandler.shouldRetryRequest(command, httpResponse)) {
            continue;
          } else {
            errorHandler.handleError(command, httpResponse);
            return wrapAsFuture(httpResponse);
          }
        } else {
          return wrapAsFuture(httpResponse);
        }
      }

    } catch (IOException e) {
      throw Throwables.propagate(e);
    } catch (InterruptedException e) {
      throw Throwables.propagate(e);
    } catch (ExecutionException e) {
      throw Throwables.propagate(e);
    }
  }
  @Test(groups = {"standalone", "default_provider"})
  public void mkcolWebDavTest1() throws InterruptedException, IOException, ExecutionException {

    AsyncHttpClient c = getAsyncHttpClient(null);
    try {
      Request mkcolRequest = new RequestBuilder("MKCOL").setUrl(getTargetUrl()).build();
      Response response = c.executeRequest(mkcolRequest).get();

      assertEquals(response.getStatusCode(), 201);
    } finally {
      c.close();
    }
  }
  private void doTestNegative(final int status, boolean strict) throws Exception {
    AsyncHttpClient p =
        getAsyncHttpClient(
            new AsyncHttpClientConfig.Builder()
                .setFollowRedirects(true)
                .setStrict302Handling(strict)
                .addResponseFilter(
                    new ResponseFilter() {
                      @Override
                      public <T> FilterContext<T> filter(FilterContext<T> ctx)
                          throws FilterException {
                        // pass on the x-expect-get and remove the x-redirect
                        // headers if found in the response
                        ctx.getResponseHeaders().getHeaders().get("x-expect-post");
                        ctx.getRequest().getHeaders().add("x-expect-post", "true");
                        ctx.getRequest().getHeaders().remove("x-redirect");
                        return ctx;
                      }
                    })
                .build());
    try {
      Request request =
          new RequestBuilder("POST")
              .setUrl(getTargetUrl())
              .addParameter("q", "a b")
              .addHeader("x-redirect", +status + "@" + "http://localhost:" + port1 + "/foo/bar/baz")
              .addHeader("x-negative", "true")
              .build();
      Future<Integer> responseFuture =
          p.executeRequest(
              request,
              new AsyncCompletionHandler<Integer>() {

                @Override
                public Integer onCompleted(Response response) throws Exception {
                  return response.getStatusCode();
                }

                /* @Override */
                public void onThrowable(Throwable t) {
                  t.printStackTrace();
                  Assert.fail("Unexpected exception: " + t.getMessage(), t);
                }
              });
      int statusCode = responseFuture.get();
      Assert.assertEquals(statusCode, 200);
    } finally {
      p.close();
    }
  }
  @Test(groups = {"standalone", "default_provider"})
  public void basicPropFindWebDavTest()
      throws InterruptedException, IOException, ExecutionException {

    AsyncHttpClient c = getAsyncHttpClient(null);
    try {
      Request propFindRequest = new RequestBuilder("PROPFIND").setUrl(getTargetUrl()).build();
      Response response = c.executeRequest(propFindRequest).get();

      assertEquals(response.getStatusCode(), 404);
    } finally {
      c.close();
    }
  }
    public void run() {
      download.setState(Transfer.State.ACTIVE);
      final String uri = validateUri(path);
      final TransferResource transferResource =
          new DefaultTransferResource(repository.getUrl(), path, file, download.getTrace());
      final boolean ignoreChecksum = RepositoryPolicy.CHECKSUM_POLICY_IGNORE.equals(checksumPolicy);
      CompletionHandler completionHandler = null;

      final FileLockCompanion fileLockCompanion =
          (file != null)
              ? createOrGetTmpFile(file.getPath(), allowResumable)
              : new FileLockCompanion(null, null);

      try {
        long length = 0;
        if (fileLockCompanion.getFile() != null) {
          fileProcessor.mkdirs(fileLockCompanion.getFile().getParentFile());
        }

        // Position the file to the end in case we are resuming an aborded download.
        final RandomAccessFile resumableFile =
            fileLockCompanion.getFile() == null
                ? null
                : new RandomAccessFile(fileLockCompanion.getFile(), "rw");
        if (resumableFile != null) {
          length = resumableFile.length();
        }

        FluentCaseInsensitiveStringsMap headers = new FluentCaseInsensitiveStringsMap();
        if (!useCache) {
          headers.add("Pragma", "no-cache");
        }
        headers.add("Accept", "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2");
        headers.replaceAll(AsyncRepositoryConnector.this.headers);

        Request request = null;
        final AtomicInteger maxRequestTry = new AtomicInteger();
        AsyncHttpClient client = httpClient;
        final AtomicBoolean closeOnComplete = new AtomicBoolean(false);

        /**
         * If length > 0, it means we are resuming a interrupted download. If that's the case, we
         * can't re-use the current httpClient because compression is enabled, and supporting
         * compression per request is not supported in ahc and may never has it could have a
         * performance impact.
         */
        if (length > 0) {
          AsyncHttpClientConfig config = createConfig(session, repository, false);
          client = new AsyncHttpClient(new NettyAsyncHttpProvider(config));
          request = client.prepareGet(uri).setRangeOffset(length).setHeaders(headers).build();
          closeOnComplete.set(true);
        } else {
          request = httpClient.prepareGet(uri).setHeaders(headers).build();
        }

        final Request activeRequest = request;
        final AsyncHttpClient activeHttpClient = client;
        completionHandler =
            new CompletionHandler(transferResource, httpClient, logger, RequestType.GET) {
              private final AtomicBoolean seekEndOnFile = new AtomicBoolean(false);

              private final AtomicBoolean handleTmpFile = new AtomicBoolean(true);

              private final AtomicBoolean localException = new AtomicBoolean(false);

              /** {@inheritDoc} */
              @Override
              public STATE onHeadersReceived(final HttpResponseHeaders headers) throws Exception {

                FluentCaseInsensitiveStringsMap h = headers.getHeaders();
                String rangeByteValue = h.getFirstValue("Content-Range");
                // Make sure the server acceptance of the range requests headers
                if (rangeByteValue != null && rangeByteValue.compareToIgnoreCase("none") != 0) {
                  seekEndOnFile.set(true);
                }
                return super.onHeadersReceived(headers);
              }

              @Override
              public void onThrowable(Throwable t) {
                try {
                  logger.debug("onThrowable", t);
                  /**
                   * If an IOException occurs, let's try to resume the request based on how much
                   * bytes has been so far downloaded. Fail after IOException.
                   */
                  if (!disableResumeSupport
                      && !localException.get()
                      && maxRequestTry.get() < maxIOExceptionRetry
                      && IOException.class.isAssignableFrom(t.getClass())) {
                    logger.debug("Trying to recover from an IOException " + activeRequest);
                    maxRequestTry.incrementAndGet();
                    Request newRequest =
                        new RequestBuilder(activeRequest)
                            .setRangeOffset(resumableFile.length())
                            .build();
                    activeHttpClient.executeRequest(newRequest, this);
                    deleteFile.set(false);
                    return;
                  }
                  localException.set(false);

                  if (closeOnComplete.get()) {
                    activeHttpClient.close();
                  }

                  super.onThrowable(t);
                  if (Exception.class.isAssignableFrom(t.getClass())) {
                    exception = Exception.class.cast(t);
                  } else {
                    exception = new Exception(t);
                  }
                  fireTransferFailed();
                } catch (Throwable ex) {
                  logger.debug("Unexpected exception", ex);
                } finally {
                  if (resumableFile != null) {
                    try {
                      resumableFile.close();
                    } catch (IOException ex) {
                    }
                  }
                  deleteFile(fileLockCompanion);

                  latch.countDown();
                  removeListeners();
                }
              }

              private void removeListeners() {
                removeTransferListener(listener);
              }

              public STATE onBodyPartReceived(final HttpResponseBodyPart content) throws Exception {
                if (status() != null
                    && (status().getStatusCode() == 200 || status().getStatusCode() == 206)) {
                  byte[] bytes = content.getBodyPartBytes();
                  try {
                    // If the content-range header was present, save the bytes at the end of the
                    // file
                    // as we are resuming an existing download.
                    if (seekEndOnFile.get()) {
                      resumableFile.seek(fileLockCompanion.getFile().length());

                      // No need to seek again.
                      seekEndOnFile.set(false);
                    }
                    resumableFile.write(bytes);
                  } catch (IOException ex) {
                    logger.debug("onBodyPartReceived", ex);
                    exception = ex;
                    localException.set(true);
                    throw ex;
                  }
                }
                return super.onBodyPartReceived(content);
              }

              @Override
              public Response onCompleted(Response r) throws Exception {
                try {
                  deleteFile.set(true);
                  try {
                    resumableFile.close();
                  } catch (IOException ex) {
                  }

                  final Response response = super.onCompleted(r);

                  handleResponseCode(uri, response.getStatusCode(), response.getStatusText());

                  if (!ignoreChecksum) {
                    activeHttpClient
                        .getConfig()
                        .executorService()
                        .execute(
                            new Runnable() {
                              public void run() {
                                try {
                                  try {
                                    Map<String, Object> checksums =
                                        ChecksumUtils.calc(
                                            fileLockCompanion.getFile(), checksumAlgos.keySet());
                                    if (!verifyChecksum(
                                            file, uri, (String) checksums.get("SHA-1"), ".sha1")
                                        && !verifyChecksum(
                                            file, uri, (String) checksums.get("MD5"), ".md5")) {
                                      throw new ChecksumFailureException(
                                          "Checksum validation failed"
                                              + ", no checksums available from the repository");
                                    }
                                  } catch (ChecksumFailureException e) {
                                    if (RepositoryPolicy.CHECKSUM_POLICY_FAIL.equals(
                                        checksumPolicy)) {
                                      throw e;
                                    }
                                    if (listener != null) {
                                      listener.transferCorrupted(
                                          newEvent(
                                              transferResource,
                                              e,
                                              RequestType.GET,
                                              EventType.CORRUPTED));
                                    }
                                  }
                                } catch (Exception ex) {
                                  exception = ex;
                                } finally {
                                  if (exception == null) {
                                    try {
                                      rename(fileLockCompanion.getFile(), file);
                                      releaseLock(fileLockCompanion);
                                    } catch (IOException e) {
                                      exception = e;
                                    }
                                  } else {
                                    deleteFile(fileLockCompanion);
                                  }

                                  latch.countDown();
                                  if (closeOnComplete.get()) {
                                    activeHttpClient.close();
                                  }
                                }
                              }
                            });
                  } else {

                    rename(fileLockCompanion.getFile(), file);
                    releaseLock(fileLockCompanion);
                    handleTmpFile.set(false);

                    // asyncHttpClient.close may takes time before all connections get closed.
                    // We unlatch first.
                    latch.countDown();
                    if (closeOnComplete.get()) {
                      activeHttpClient.close();
                    }
                  }
                  removeListeners();

                  return response;
                } catch (Exception ex) {
                  exception = ex;
                  localException.set(true);
                  throw ex;
                } finally {
                  try {
                    if (handleTmpFile.get() && fileLockCompanion.getFile() != null) {
                      if (exception != null) {
                        deleteFile(fileLockCompanion);
                      } else if (ignoreChecksum) {
                        rename(fileLockCompanion.getFile(), file);
                        releaseLock(fileLockCompanion);
                      }
                    }
                  } catch (IOException ex) {
                    exception = ex;
                  }
                }
              }
            };

        try {
          if (file == null) {
            if (!resourceExist(uri)) {
              throw new ResourceDoesNotExistException(
                  "Could not find " + uri + " in " + repository.getUrl());
            }
            latch.countDown();
          } else {
            if (listener != null) {
              completionHandler.addTransferListener(listener);
              listener.transferInitiated(
                  newEvent(transferResource, null, RequestType.GET, EventType.INITIATED));
            }

            activeHttpClient.executeRequest(request, completionHandler);
          }
        } catch (Exception ex) {
          try {
            if (resumableFile != null) {
              resumableFile.close();
            }
          } catch (IOException ex2) {
          }
          deleteFile(fileLockCompanion);
          exception = ex;
          latch.countDown();
        }
      } catch (Throwable t) {
        deleteFile(fileLockCompanion);
        try {
          if (Exception.class.isAssignableFrom(t.getClass())) {
            exception = Exception.class.cast(t);
          } else {
            exception = new Exception(t);
          }
          if (listener != null) {
            listener.transferFailed(
                newEvent(transferResource, exception, RequestType.GET, EventType.FAILED));
          }
        } finally {
          latch.countDown();
        }
      }
    }