@Override
    public InputStream read(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException {
        final List<Header> headers = new ArrayList<Header>(this.headers());
        if(status.isAppend()) {
            final HttpRange range = HttpRange.withStatus(status);
            final String header;
            if(TransferStatus.UNKNOWN_LENGTH == range.getEnd()) {
                header = String.format("bytes=%d-", range.getStart());
            }
            else {
                header = String.format("bytes=%d-%d", range.getStart(), range.getEnd());
            }
            if(log.isDebugEnabled()) {
                log.debug(String.format("Add range header %s for file %s", header, file));
            }
            headers.add(new BasicHeader(HttpHeaders.RANGE, header));
            // Disable compression
            headers.add(new BasicHeader(HttpHeaders.ACCEPT_ENCODING, "identity"));
        }
        try {
            final HttpRequestBase request = this.toRequest(file, status);
            for(Header header : headers) {
                request.addHeader(header);
            }
            final HttpResponse response = session.getClient().execute(request);
            final VoidResponseHandler handler = new VoidResponseHandler();
            try {
                handler.handleResponse(response);
                // Will abort the read when closed before EOF.
                final ContentLengthStatusInputStream stream = new ContentLengthStatusInputStream(new HttpMethodReleaseInputStream(response, status),
                        response.getEntity().getContentLength(),
                        response.getStatusLine().getStatusCode());
                if(status.isAppend()) {
                    if(stream.getCode() == HttpStatus.SC_OK) {
                        if(TransferStatus.UNKNOWN_LENGTH != status.getLength()) {
                            if(stream.getLength() != status.getLength()) {
                                log.warn(String.format("Range header not supported. Skipping %d bytes in file %s.", status.getOffset(), file));
                                stream.skip(status.getOffset());
                            }
                        }
                    }
                }
                return stream;
            }
            catch(IOException ex) {
                request.abort();
                throw ex;
            }
        }
        catch(SardineException e) {
            throw new DAVExceptionMappingService().map("Download {0} failed", e, file);
        }
        catch(IOException e) {
            throw new HttpExceptionMappingService().map("Download {0} failed", e, file);
        }
    }
@Test
    public void testReadRange() throws Exception {
        final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
        final Local local = new Local(System.getProperty("java.io.tmpdir"), new AlphanumericRandomStringService().random());
        final byte[] content = RandomUtils.nextBytes(1023);
        final OutputStream out = local.getOutputStream(false);
        assertNotNull(out);
        IOUtils.write(content, out);
        out.close();
        new DAVUploadFeature(session).upload(
                test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledStreamListener(),
                new TransferStatus().withLength(content.length),
                new DisabledConnectionCallback());
        final TransferStatus status = new TransferStatus();
        status.setLength(content.length);
        status.setAppend(true);
        status.setOffset(100L);
        final InputStream in = new DAVReadFeature(session).read(test, status.withLength(content.length - 100), new DisabledConnectionCallback());
        assertNotNull(in);
        final ByteArrayOutputStream buffer = new ByteArrayOutputStream(content.length - 100);
        new StreamCopier(status, status).transfer(in, buffer);
        final byte[] reference = new byte[content.length - 100];
        System.arraycopy(content, 100, reference, 0, content.length - 100);
        assertArrayEquals(reference, buffer.toByteArray());
        in.close();
        new DAVDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
    }