mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-09-26 01:44:15 +00:00
WebDAV range request support is back!
This commit is contained in:
+1
-1
@@ -33,7 +33,7 @@ public class InMemoryWebDavServer {
|
||||
server.setPort(8080);
|
||||
server.start();
|
||||
|
||||
FileSystem fileSystem = cryptoFileSystem();
|
||||
FileSystem fileSystem = inMemoryFileSystem();
|
||||
ServletContextHandler servlet = server.addServlet(fileSystem, URI.create("http://localhost:8080/foo"));
|
||||
servlet.addFilter(LoggingHttpFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
|
||||
servlet.start();
|
||||
|
||||
+202
@@ -15,8 +15,15 @@ import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.ForkJoinTask;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
@@ -27,6 +34,7 @@ import org.apache.commons.httpclient.Header;
|
||||
import org.apache.commons.httpclient.HttpClient;
|
||||
import org.apache.commons.httpclient.HttpException;
|
||||
import org.apache.commons.httpclient.HttpMethod;
|
||||
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
|
||||
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
|
||||
import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
|
||||
import org.apache.commons.httpclient.methods.GetMethod;
|
||||
@@ -51,11 +59,14 @@ import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
public class WebDavServerTest {
|
||||
|
||||
private static final WebDavServer SERVER = DaggerWebDavComponent.create().server();
|
||||
private static final Logger LOG = LoggerFactory.getLogger(WebDavServerTest.class);
|
||||
private String servletRoot;
|
||||
private FileSystem fs;
|
||||
private ServletContextHandler servlet;
|
||||
@@ -311,4 +322,195 @@ public class WebDavServerTest {
|
||||
Assert.assertFalse(fs.folder("dstFile").exists());
|
||||
}
|
||||
|
||||
/* Range requests */
|
||||
|
||||
@Test
|
||||
public void testGetWithUnsatisfiableRange() throws IOException {
|
||||
final HttpClient client = new HttpClient();
|
||||
|
||||
// write test content:
|
||||
final byte[] testContent = "hello world".getBytes();
|
||||
try (WritableFile w = fs.file("foo.txt").openWritable()) {
|
||||
w.write(ByteBuffer.wrap(testContent));
|
||||
}
|
||||
|
||||
// check get response body:
|
||||
final HttpMethod getMethod = new GetMethod(servletRoot + "/foo.txt");
|
||||
getMethod.addRequestHeader("Range", "chunks=1-2");
|
||||
final int statusCode = client.executeMethod(getMethod);
|
||||
Assert.assertEquals(416, statusCode);
|
||||
Assert.assertArrayEquals(testContent, getMethod.getResponseBody());
|
||||
getMethod.releaseConnection();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleGetWithRangeAsync() throws IOException, URISyntaxException, InterruptedException {
|
||||
final String testResourceUrl = servletRoot + "/foo.txt";
|
||||
|
||||
// prepare 8MiB test data:
|
||||
final byte[] plaintextData = new byte[2097152 * Integer.BYTES];
|
||||
final ByteBuffer plaintextDataByteBuffer = ByteBuffer.wrap(plaintextData);
|
||||
for (int i = 0; i < 2097152; i++) {
|
||||
plaintextDataByteBuffer.putInt(i);
|
||||
}
|
||||
try (WritableFile w = fs.file("foo.txt").openWritable()) {
|
||||
plaintextDataByteBuffer.flip();
|
||||
w.write(plaintextDataByteBuffer);
|
||||
}
|
||||
|
||||
final MultiThreadedHttpConnectionManager cm = new MultiThreadedHttpConnectionManager();
|
||||
cm.getParams().setDefaultMaxConnectionsPerHost(50);
|
||||
final HttpClient client = new HttpClient(cm);
|
||||
|
||||
// multiple async range requests:
|
||||
final List<ForkJoinTask<?>> tasks = new ArrayList<>();
|
||||
final Random generator = new Random(System.currentTimeMillis());
|
||||
|
||||
final AtomicBoolean success = new AtomicBoolean(true);
|
||||
|
||||
// 10 full interrupted requests:
|
||||
for (int i = 0; i < 10; i++) {
|
||||
final ForkJoinTask<?> task = ForkJoinTask.adapt(() -> {
|
||||
try {
|
||||
final HttpMethod getMethod = new GetMethod(testResourceUrl);
|
||||
final int statusCode = client.executeMethod(getMethod);
|
||||
if (statusCode != 200) {
|
||||
LOG.error("Invalid status code for interrupted full request");
|
||||
success.set(false);
|
||||
}
|
||||
getMethod.getResponseBodyAsStream().read();
|
||||
getMethod.getResponseBodyAsStream().close();
|
||||
getMethod.releaseConnection();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
tasks.add(task);
|
||||
}
|
||||
|
||||
// 50 crappy interrupted range requests:
|
||||
for (int i = 0; i < 50; i++) {
|
||||
final int lower = generator.nextInt(plaintextData.length);
|
||||
final ForkJoinTask<?> task = ForkJoinTask.adapt(() -> {
|
||||
try {
|
||||
final HttpMethod getMethod = new GetMethod(testResourceUrl);
|
||||
getMethod.addRequestHeader("Range", "bytes=" + lower + "-");
|
||||
final int statusCode = client.executeMethod(getMethod);
|
||||
if (statusCode != 206) {
|
||||
LOG.error("Invalid status code for interrupted range request");
|
||||
success.set(false);
|
||||
}
|
||||
getMethod.getResponseBodyAsStream().read();
|
||||
getMethod.getResponseBodyAsStream().close();
|
||||
getMethod.releaseConnection();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
tasks.add(task);
|
||||
}
|
||||
|
||||
// 50 normal open range requests:
|
||||
for (int i = 0; i < 50; i++) {
|
||||
final int lower = generator.nextInt(plaintextData.length - 512);
|
||||
final int upper = plaintextData.length - 1;
|
||||
final ForkJoinTask<?> task = ForkJoinTask.adapt(() -> {
|
||||
try {
|
||||
final HttpMethod getMethod = new GetMethod(testResourceUrl);
|
||||
getMethod.addRequestHeader("Range", "bytes=" + lower + "-");
|
||||
final byte[] expected = Arrays.copyOfRange(plaintextData, lower, upper + 1);
|
||||
final int statusCode = client.executeMethod(getMethod);
|
||||
final byte[] responseBody = new byte[upper - lower + 10];
|
||||
final int bytesRead = IOUtils.read(getMethod.getResponseBodyAsStream(), responseBody);
|
||||
getMethod.releaseConnection();
|
||||
if (statusCode != 206) {
|
||||
LOG.error("Invalid status code for open range request");
|
||||
success.set(false);
|
||||
} else if (upper - lower + 1 != bytesRead) {
|
||||
LOG.error("Invalid response length for open range request");
|
||||
success.set(false);
|
||||
} else if (!Arrays.equals(expected, Arrays.copyOfRange(responseBody, 0, bytesRead))) {
|
||||
LOG.error("Invalid response body for open range request");
|
||||
success.set(false);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
tasks.add(task);
|
||||
}
|
||||
|
||||
// 200 normal closed range requests:
|
||||
for (int i = 0; i < 200; i++) {
|
||||
final int pos1 = generator.nextInt(plaintextData.length - 512);
|
||||
final int pos2 = pos1 + 512;
|
||||
final ForkJoinTask<?> task = ForkJoinTask.adapt(() -> {
|
||||
try {
|
||||
final int lower = Math.min(pos1, pos2);
|
||||
final int upper = Math.max(pos1, pos2);
|
||||
final HttpMethod getMethod = new GetMethod(testResourceUrl);
|
||||
getMethod.addRequestHeader("Range", "bytes=" + lower + "-" + upper);
|
||||
final byte[] expected = Arrays.copyOfRange(plaintextData, lower, upper + 1);
|
||||
final int statusCode = client.executeMethod(getMethod);
|
||||
final byte[] responseBody = new byte[upper - lower + 1];
|
||||
final int bytesRead = IOUtils.read(getMethod.getResponseBodyAsStream(), responseBody);
|
||||
getMethod.releaseConnection();
|
||||
if (statusCode != 206) {
|
||||
LOG.error("Invalid status code for closed range request");
|
||||
success.set(false);
|
||||
} else if (upper - lower + 1 != bytesRead) {
|
||||
LOG.error("Invalid response length for closed range request");
|
||||
success.set(false);
|
||||
} else if (!Arrays.equals(expected, Arrays.copyOfRange(responseBody, 0, bytesRead))) {
|
||||
LOG.error("Invalid response body for closed range request");
|
||||
success.set(false);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
tasks.add(task);
|
||||
}
|
||||
|
||||
Collections.shuffle(tasks, generator);
|
||||
|
||||
final ForkJoinPool pool = new ForkJoinPool(4);
|
||||
for (ForkJoinTask<?> task : tasks) {
|
||||
pool.execute(task);
|
||||
}
|
||||
for (ForkJoinTask<?> task : tasks) {
|
||||
task.join();
|
||||
}
|
||||
pool.shutdown();
|
||||
cm.shutdown();
|
||||
|
||||
Assert.assertTrue(success.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnsatisfiableRangeRequest() throws IOException, URISyntaxException {
|
||||
final String testResourceUrl = servletRoot + "/unsatisfiableRangeRequestTestFile.txt";
|
||||
final HttpClient client = new HttpClient();
|
||||
|
||||
// prepare file content:
|
||||
final byte[] fileContent = "This is some test file content.".getBytes();
|
||||
|
||||
// put request:
|
||||
final EntityEnclosingMethod putMethod = new PutMethod(testResourceUrl.toString());
|
||||
putMethod.setRequestEntity(new ByteArrayRequestEntity(fileContent));
|
||||
final int putResponse = client.executeMethod(putMethod);
|
||||
putMethod.releaseConnection();
|
||||
Assert.assertEquals(201, putResponse);
|
||||
|
||||
// get request:
|
||||
final HttpMethod getMethod = new GetMethod(testResourceUrl.toString());
|
||||
getMethod.addRequestHeader("Range", "chunks=1-2");
|
||||
final int getResponse = client.executeMethod(getMethod);
|
||||
final byte[] response = new byte[fileContent.length];
|
||||
IOUtils.read(getMethod.getResponseBodyAsStream(), response);
|
||||
getMethod.releaseConnection();
|
||||
Assert.assertEquals(416, getResponse);
|
||||
Assert.assertArrayEquals(fileContent, response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user