@Override
    public HttpResponseOutputStream<Node> write(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException {
        final CreateFileUploadResponse uploadResponse = upload.start(file, status);
        final String uploadUrl = uploadResponse.getUploadUrl();
        if(StringUtils.isBlank(uploadUrl)) {
            throw new InteroperabilityException("Missing upload URL in server response");
        }
        final String uploadToken = uploadResponse.getToken();
        if(StringUtils.isBlank(uploadToken)) {
            throw new InteroperabilityException("Missing upload token in server response");
        }
        final MultipartUploadTokenOutputStream proxy = new MultipartUploadTokenOutputStream(session, nodeid, file, status, uploadUrl);
        return new HttpResponseOutputStream<Node>(new MemorySegementingOutputStream(proxy, new HostPreferences(session.getHost()).getInteger("sds.upload.multipart.chunksize")),
                new SDSAttributesAdapter(session), status) {
            private final AtomicBoolean close = new AtomicBoolean();
            private final AtomicReference<Node> node = new AtomicReference<>();

            @Override
            public Node getStatus() {
                return node.get();
            }

            @Override
            public void close() throws IOException {
                try {
                    if(close.get()) {
                        log.warn(String.format("Skip double close of stream %s", this));
                        return;
                    }
                    super.close();
                    node.set(upload.complete(file, uploadToken, status));
                }
                catch(BackgroundException e) {
                    throw new IOException(e);
                }
                finally {
                    close.set(true);
                }
            }


            @Override
            protected void handleIOException(final IOException e) throws IOException {
                // Cancel upload on error reply
                try {
                    upload.cancel(file, uploadToken);
                }
                catch(BackgroundException f) {
                    log.warn(String.format("Failure %s cancelling upload for file %s with upload token %s after failure %s", f, file, uploadToken, e));
                }
                throw e;
            }
        };
    }
@Test
    public void testWriteUnknownContentLength() throws Exception {
        final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session);
        final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(
                new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
        final byte[] content = RandomUtils.nextBytes(32769);
        final TransferStatus status = new TransferStatus();
        status.setLength(-1L);
        status.setMime("text/plain");
        final Path test = new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
        final SDSMultipartWriteFeature writer = new SDSMultipartWriteFeature(session, nodeid);
        final StatusOutputStream<Node> out = writer.write(test, status, new DisabledConnectionCallback());
        assertNotNull(out);
        new StreamCopier(status, status).transfer(new ByteArrayInputStream(content), out);
        assertNotNull(test.attributes().getVersionId());
        assertEquals(content.length, out.getStatus().getSize(), 0L);
        assertTrue(new DefaultFindFeature(session).find(test));
        final byte[] compare = new byte[content.length];
        final InputStream stream = new SDSReadFeature(session, nodeid).read(test, new TransferStatus().withLength(content.length), new DisabledConnectionCallback());
        IOUtils.readFully(stream, compare);
        stream.close();
        assertArrayEquals(content, compare);
        new SDSDeleteFeature(session, nodeid).delete(Collections.singletonList(room), new DisabledLoginCallback(), new Delete.DisabledCallback());
    }