private void testCopyFile(
      BlockReader reader,
      String readFileName,
      BlockWriter writer,
      String writeFileName,
      File baseDir)
      throws IOException {
    ReadRequest readRequest = new ReadRequest();
    readRequest.setBaseDir(baseDir);
    readRequest.setFileName(readFileName);
    readRequest.setBlockSize(FileUtils.MIN_BLOCK_SIZE);

    WriteRequest writeRequest = new WriteRequest();
    writeRequest.setBaseDir(baseDir);
    writeRequest.setFileName(writeFileName);

    long offset = 0L;
    while (true) {
      readRequest.setOffset(offset);
      logger.info(String.format("Read Request Copy: %s", readRequest));
      ReadResponse readResponse = reader.readBlock(readRequest);
      logger.info(String.format("Read Response Copy: %s", readResponse));
      if (!readResponse.isSuccess()) {
        throw new ErrorReadingBlockException(
            String.format(
                "Error reading %d bytes '%s' in '%s' at offset of %d",
                readRequest.getBlockSize(),
                readRequest.getFileName(),
                readRequest.getBaseDir(),
                readRequest.getOffset()));
      }

      if (readResponse.getLength() > 0 || readResponse.isEof()) {
        writeRequest.setData(readResponse.getData());
        writeRequest.setOffset(offset);
        writeRequest.setLength(readResponse.getLength());
        writeRequest.setEof(readResponse.isEof());
        logger.info(String.format("Write Request Copy: %s", writeRequest));
        WriteResponse writeResponse = writer.writeBlock(writeRequest);
        logger.info(String.format("Write Response Copy: %s", writeResponse));
        if (!writeResponse.isSuccess()) {
          throw new ErrorWritingBlockException(
              String.format(
                  "Error writing %d bytes '%s' in '%s' at offset of %d",
                  writeRequest.getLength(),
                  writeRequest.getFileName(),
                  writeRequest.getBaseDir(),
                  writeRequest.getOffset()));
        }

        if (writeRequest.getLength() != writeResponse.getLength()) {
          throw new InvalidBlockHashException(
              String.format(
                  "Invalid length while writing what was expected. Exepecting %d but was %d",
                  writeRequest.getLength(), writeResponse.getLength()));
        } else if (readResponse.getLength() != writeResponse.getLength()) {
          throw new InvalidBlockHashException(
              String.format(
                  "Invalid length while writing what was read. Exepecting %d but was %d",
                  readResponse.getLength(), writeResponse.getLength()));
        }

        if (readResponse.getCrc32() != writeResponse.getCrc32()) {
          throw new InvalidBlockHashException(
              String.format(
                  "Invalid CRC while writing what was read. Exepecting %d but was %d",
                  readResponse.getCrc32(), writeResponse.getCrc32()));
        }

        offset += readResponse.getLength();
      }

      if (readResponse.isEof()) {
        break;
      }
    }
  }
  @Override
  public ReadResponse readBlock(ReadRequest request) {
    ReadResponse ret = new ReadResponse();

    if (remoteClient == null) {
      throw new IllegalStateException(
          String.format(
              "%s missing %s", getClass().getSimpleName(), RemoteClient.class.getSimpleName()));
    }

    ret.setRequest(request);

    String uri =
        remoteClient.getURI(String.format("%s/%s", Constants.FILE_PATH, request.getFileName()));

    WebResource webResource =
        remoteClient
            .getClient()
            .resource(uri)
            .queryParam(Constants.OFFSET_PARAM, String.valueOf(request.getOffset()))
            .queryParam(Constants.BLOCK_SIZE_PARAM, String.valueOf(request.getBlockSize()));
    logger.debug(webResource.toString());
    Builder builder = webResource.accept(MediaType.APPLICATION_OCTET_STREAM);
    if (FileUtils.isCompress()) {
      builder = builder.header(Constants.CONTENT_ENCODED_HEADER, Constants.DEFLATE);
    }

    ClientResponse clientResponse = builder.get(ClientResponse.class);
    try {
      remoteClient.checkForException(clientResponse);

      if (clientResponse.getStatus() != HttpStatus.OK_200) {
        throw new ErrorReadingBlockException(
            String.format(
                "Failed GET %s: HttpStatus: %d/%s",
                uri, clientResponse.getStatus(), clientResponse.getStatusInfo()));
      }

      logger.debug(clientResponse.getHeaders().toString());

      ret.setLength(
          Integer.parseInt(clientResponse.getHeaders().getFirst(Constants.LENGTH_HEADER)));
      int contentLength = Integer.parseInt(clientResponse.getHeaders().getFirst("Content-Length"));

      byte[] block = null;
      int compressed = -1;
      if (clientResponse.getHeaders().containsKey(Constants.COMPRESSED_HEADER)) {
        compressed =
            Integer.parseInt(clientResponse.getHeaders().getFirst(Constants.COMPRESSED_HEADER));
        if (contentLength != compressed) {
          throw new ErrorReadingBlockException(
              String.format(
                  "Failed GET %s: Compressed Content-Length: %d/%s",
                  uri, contentLength, compressed));
        }

        Inflated inflated =
            CompressionUtils.inflate(getDataByEntity(clientResponse, compressed), ret.getLength());
        if (inflated == null || inflated.getLength() != ret.getLength()) {
          throw new ErrorInflatingBlockException("Error inflating compressed read response");
        }

        block = inflated.getData();
      } else {
        if (contentLength != ret.getLength()) {
          throw new ErrorReadingBlockException(
              String.format(
                  "Failed GET %s: Content-Length: %d/%s", uri, contentLength, ret.getLength()));
        }

        block = getDataByEntity(clientResponse, ret.getLength());
      }

      ret.setCrc32(Long.parseLong(clientResponse.getHeaders().getFirst(Constants.CRC_HEADER)));
      ret.setEof(Boolean.parseBoolean(clientResponse.getHeaders().getFirst(Constants.EOF_HEADER)));
      ret.setSuccess(
          Boolean.parseBoolean(clientResponse.getHeaders().getFirst(Constants.SUCCESS_HEADER)));
      ret.setData(block);
    } finally {
      clientResponse.close();
    }

    return ret;
  }